mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-07 10:06:57 +02:00
Refactor and clean up akamai provider
refactor: remove dns api logic and use dns api library enhancement: add additional args for auth credential retieval cleanup: simplify, organize processing logic test: update automation and validate
This commit is contained in:
parent
65087c4e02
commit
75429cc504
2
go.mod
2
go.mod
@ -10,7 +10,7 @@ require (
|
|||||||
github.com/Azure/go-autorest/autorest/adal v0.9.5
|
github.com/Azure/go-autorest/autorest/adal v0.9.5
|
||||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.3
|
github.com/Azure/go-autorest/autorest/azure/auth v0.5.3
|
||||||
github.com/Azure/go-autorest/autorest/to v0.4.0
|
github.com/Azure/go-autorest/autorest/to v0.4.0
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.11
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.0.0
|
||||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect
|
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect
|
||||||
github.com/alecthomas/colour v0.1.0 // indirect
|
github.com/alecthomas/colour v0.1.0 // indirect
|
||||||
github.com/alecthomas/kingpin v2.2.5+incompatible
|
github.com/alecthomas/kingpin v2.2.5+incompatible
|
||||||
|
2
go.sum
2
go.sum
@ -60,6 +60,8 @@ github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4Rq
|
|||||||
github.com/ahmetb/gen-crd-api-reference-docs v0.1.5/go.mod h1:P/XzJ+c2+khJKNKABcm2biRwk2QAuwbLf8DlXuaL7WM=
|
github.com/ahmetb/gen-crd-api-reference-docs v0.1.5/go.mod h1:P/XzJ+c2+khJKNKABcm2biRwk2QAuwbLf8DlXuaL7WM=
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.11 h1:QGjNHMwoPYxE5NpOAc8kpd2KTY293/oFk5BWdjkza+k=
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.11 h1:QGjNHMwoPYxE5NpOAc8kpd2KTY293/oFk5BWdjkza+k=
|
||||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.11/go.mod h1:L+HB2uBoDgi3+r1pJEJcbGwyyHhd2QXaGsKLbDwtm8Q=
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.11/go.mod h1:L+HB2uBoDgi3+r1pJEJcbGwyyHhd2QXaGsKLbDwtm8Q=
|
||||||
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.0.0 h1:FJF58TWBaQnGqTIcziIP5/z3TTqWUn8fh26z03oZE2c=
|
||||||
|
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.0.0/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8=
|
||||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
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/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
|
||||||
github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk=
|
github.com/alecthomas/colour v0.1.0 h1:nOE9rJm6dsZ66RGWYSFrXw461ZIt9A6+nHgL7FRrDUk=
|
||||||
|
7
main.go
7
main.go
@ -151,7 +151,7 @@ func main() {
|
|||||||
var p provider.Provider
|
var p provider.Provider
|
||||||
switch cfg.Provider {
|
switch cfg.Provider {
|
||||||
case "akamai":
|
case "akamai":
|
||||||
p = akamai.NewAkamaiProvider(
|
p, err = akamai.NewAkamaiProvider(
|
||||||
akamai.AkamaiConfig{
|
akamai.AkamaiConfig{
|
||||||
DomainFilter: domainFilter,
|
DomainFilter: domainFilter,
|
||||||
ZoneIDFilter: zoneIDFilter,
|
ZoneIDFilter: zoneIDFilter,
|
||||||
@ -159,9 +159,10 @@ func main() {
|
|||||||
ClientToken: cfg.AkamaiClientToken,
|
ClientToken: cfg.AkamaiClientToken,
|
||||||
ClientSecret: cfg.AkamaiClientSecret,
|
ClientSecret: cfg.AkamaiClientSecret,
|
||||||
AccessToken: cfg.AkamaiAccessToken,
|
AccessToken: cfg.AkamaiAccessToken,
|
||||||
|
EdgercPath: cfg.AkamaiEdgercPath,
|
||||||
|
EdgercSection: cfg.AkamaiEdgercSection,
|
||||||
DryRun: cfg.DryRun,
|
DryRun: cfg.DryRun,
|
||||||
},
|
}, nil)
|
||||||
)
|
|
||||||
case "alibabacloud":
|
case "alibabacloud":
|
||||||
p, err = alibabacloud.NewAlibabaCloudProvider(cfg.AlibabaCloudConfigFile, domainFilter, zoneIDFilter, cfg.AlibabaCloudZoneType, cfg.DryRun)
|
p, err = alibabacloud.NewAlibabaCloudProvider(cfg.AlibabaCloudConfigFile, domainFilter, zoneIDFilter, cfg.AlibabaCloudZoneType, cfg.DryRun)
|
||||||
case "aws":
|
case "aws":
|
||||||
|
@ -88,6 +88,8 @@ type Config struct {
|
|||||||
AkamaiClientToken string
|
AkamaiClientToken string
|
||||||
AkamaiClientSecret string
|
AkamaiClientSecret string
|
||||||
AkamaiAccessToken string
|
AkamaiAccessToken string
|
||||||
|
AkamaiEdgercPath string
|
||||||
|
AkamaiEdgercSection string
|
||||||
InfobloxGridHost string
|
InfobloxGridHost string
|
||||||
InfobloxWapiPort int
|
InfobloxWapiPort int
|
||||||
InfobloxWapiUsername string
|
InfobloxWapiUsername string
|
||||||
@ -194,6 +196,8 @@ var defaultConfig = &Config{
|
|||||||
AkamaiClientToken: "",
|
AkamaiClientToken: "",
|
||||||
AkamaiClientSecret: "",
|
AkamaiClientSecret: "",
|
||||||
AkamaiAccessToken: "",
|
AkamaiAccessToken: "",
|
||||||
|
AkamaiEdgercSection: "",
|
||||||
|
AkamaiEdgercPath: "",
|
||||||
InfobloxGridHost: "",
|
InfobloxGridHost: "",
|
||||||
InfobloxWapiPort: 443,
|
InfobloxWapiPort: 443,
|
||||||
InfobloxWapiUsername: "admin",
|
InfobloxWapiUsername: "admin",
|
||||||
@ -353,10 +357,12 @@ 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-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)
|
||||||
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-serviceconsumerdomain", "When using the Akamai provider, specify the base URL (required when --provider=akamai and edgerc-path not specified)").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-token", "When using the Akamai provider, specify the client token (required when --provider=akamai and edgerc-path not specified)").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-client-secret", "When using the Akamai provider, specify the client secret (required when --provider=akamai and edgerc-path not specified)").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("akamai-access-token", "When using the Akamai provider, specify the access token (required when --provider=akamai and edgerc-path not specified)").Default(defaultConfig.AkamaiAccessToken).StringVar(&cfg.AkamaiAccessToken)
|
||||||
|
app.Flag("akamai-edgerc-path", "When using the Akamai provider, specify the .edgerc file path. Path must be reachable form invocation environment. (required when --provider=akamai and *-token, secret serviceconsumerdomain not specified)").Default(defaultConfig.AkamaiEdgercPath).StringVar(&cfg.AkamaiEdgercPath)
|
||||||
|
app.Flag("akamai-edgerc-section", "When using the Akamai provider, specify the .edgerc file path (Optional when edgerc-path is specified)").Default(defaultConfig.AkamaiEdgercSection).StringVar(&cfg.AkamaiEdgercSection)
|
||||||
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-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-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)
|
app.Flag("infoblox-wapi-username", "When using the Infoblox provider, specify the WAPI username (default: admin)").Default(defaultConfig.InfobloxWapiUsername).StringVar(&cfg.InfobloxWapiUsername)
|
||||||
|
@ -66,6 +66,8 @@ var (
|
|||||||
AkamaiClientToken: "",
|
AkamaiClientToken: "",
|
||||||
AkamaiClientSecret: "",
|
AkamaiClientSecret: "",
|
||||||
AkamaiAccessToken: "",
|
AkamaiAccessToken: "",
|
||||||
|
AkamaiEdgercPath: "",
|
||||||
|
AkamaiEdgercSection: "",
|
||||||
InfobloxGridHost: "",
|
InfobloxGridHost: "",
|
||||||
InfobloxWapiPort: 443,
|
InfobloxWapiPort: 443,
|
||||||
InfobloxWapiUsername: "admin",
|
InfobloxWapiUsername: "admin",
|
||||||
@ -144,6 +146,8 @@ var (
|
|||||||
AkamaiClientToken: "o184671d5307a388180fbf7f11dbdf46",
|
AkamaiClientToken: "o184671d5307a388180fbf7f11dbdf46",
|
||||||
AkamaiClientSecret: "o184671d5307a388180fbf7f11dbdf46",
|
AkamaiClientSecret: "o184671d5307a388180fbf7f11dbdf46",
|
||||||
AkamaiAccessToken: "o184671d5307a388180fbf7f11dbdf46",
|
AkamaiAccessToken: "o184671d5307a388180fbf7f11dbdf46",
|
||||||
|
AkamaiEdgercPath: "/home/test/.edgerc",
|
||||||
|
AkamaiEdgercSection: "default",
|
||||||
InfobloxGridHost: "127.0.0.1",
|
InfobloxGridHost: "127.0.0.1",
|
||||||
InfobloxWapiPort: 8443,
|
InfobloxWapiPort: 8443,
|
||||||
InfobloxWapiUsername: "infoblox",
|
InfobloxWapiUsername: "infoblox",
|
||||||
@ -235,6 +239,8 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"--akamai-client-token=o184671d5307a388180fbf7f11dbdf46",
|
"--akamai-client-token=o184671d5307a388180fbf7f11dbdf46",
|
||||||
"--akamai-client-secret=o184671d5307a388180fbf7f11dbdf46",
|
"--akamai-client-secret=o184671d5307a388180fbf7f11dbdf46",
|
||||||
"--akamai-access-token=o184671d5307a388180fbf7f11dbdf46",
|
"--akamai-access-token=o184671d5307a388180fbf7f11dbdf46",
|
||||||
|
"--akamai-edgerc-path=/home/test/.edgerc",
|
||||||
|
"--akamai-edgerc-section=default",
|
||||||
"--infoblox-grid-host=127.0.0.1",
|
"--infoblox-grid-host=127.0.0.1",
|
||||||
"--infoblox-wapi-port=8443",
|
"--infoblox-wapi-port=8443",
|
||||||
"--infoblox-wapi-username=infoblox",
|
"--infoblox-wapi-username=infoblox",
|
||||||
@ -328,6 +334,8 @@ func TestParseFlags(t *testing.T) {
|
|||||||
"EXTERNAL_DNS_AKAMAI_CLIENT_TOKEN": "o184671d5307a388180fbf7f11dbdf46",
|
"EXTERNAL_DNS_AKAMAI_CLIENT_TOKEN": "o184671d5307a388180fbf7f11dbdf46",
|
||||||
"EXTERNAL_DNS_AKAMAI_CLIENT_SECRET": "o184671d5307a388180fbf7f11dbdf46",
|
"EXTERNAL_DNS_AKAMAI_CLIENT_SECRET": "o184671d5307a388180fbf7f11dbdf46",
|
||||||
"EXTERNAL_DNS_AKAMAI_ACCESS_TOKEN": "o184671d5307a388180fbf7f11dbdf46",
|
"EXTERNAL_DNS_AKAMAI_ACCESS_TOKEN": "o184671d5307a388180fbf7f11dbdf46",
|
||||||
|
"EXTERNAL_DNS_AKAMAI_EDGERC_PATH": "/home/test/.edgerc",
|
||||||
|
"EXTERNAL_DNS_AKAMAI_EDGERC_SECTION": "default",
|
||||||
"EXTERNAL_DNS_INFOBLOX_GRID_HOST": "127.0.0.1",
|
"EXTERNAL_DNS_INFOBLOX_GRID_HOST": "127.0.0.1",
|
||||||
"EXTERNAL_DNS_INFOBLOX_WAPI_PORT": "8443",
|
"EXTERNAL_DNS_INFOBLOX_WAPI_PORT": "8443",
|
||||||
"EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME": "infoblox",
|
"EXTERNAL_DNS_INFOBLOX_WAPI_USERNAME": "infoblox",
|
||||||
|
@ -45,16 +45,20 @@ func ValidateConfig(cfg *externaldns.Config) error {
|
|||||||
|
|
||||||
// Akamai provider specific validations
|
// Akamai provider specific validations
|
||||||
if cfg.Provider == "akamai" {
|
if cfg.Provider == "akamai" {
|
||||||
if cfg.AkamaiServiceConsumerDomain == "" {
|
edgerc := false
|
||||||
|
if cfg.AkamaiEdgercPath != "" {
|
||||||
|
edgerc = true
|
||||||
|
}
|
||||||
|
if cfg.AkamaiServiceConsumerDomain == "" && !edgerc {
|
||||||
return errors.New("no Akamai ServiceConsumerDomain specified")
|
return errors.New("no Akamai ServiceConsumerDomain specified")
|
||||||
}
|
}
|
||||||
if cfg.AkamaiClientToken == "" {
|
if cfg.AkamaiClientToken == "" && !edgerc {
|
||||||
return errors.New("no Akamai client token specified")
|
return errors.New("no Akamai client token specified")
|
||||||
}
|
}
|
||||||
if cfg.AkamaiClientSecret == "" {
|
if cfg.AkamaiClientSecret == "" && !edgerc {
|
||||||
return errors.New("no Akamai client secret specified")
|
return errors.New("no Akamai client secret specified")
|
||||||
}
|
}
|
||||||
if cfg.AkamaiAccessToken == "" {
|
if cfg.AkamaiAccessToken == "" && !edgerc {
|
||||||
return errors.New("no Akamai access token specified")
|
return errors.New("no Akamai access token specified")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,13 @@ limitations under the License.
|
|||||||
package akamai
|
package akamai
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"os"
|
||||||
"net/http"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
c "github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1"
|
dns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v2"
|
||||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
@ -34,22 +32,23 @@ import (
|
|||||||
"sigs.k8s.io/external-dns/provider"
|
"sigs.k8s.io/external-dns/provider"
|
||||||
)
|
)
|
||||||
|
|
||||||
type akamaiClient interface {
|
const (
|
||||||
NewRequest(config edgegrid.Config, method, path string, body io.Reader) (*http.Request, error)
|
// Default Record TTL
|
||||||
Do(config edgegrid.Config, req *http.Request) (*http.Response, error)
|
edgeDNSRecordTTL = 600
|
||||||
|
maxUint = ^uint(0)
|
||||||
|
maxInt = int(maxUint >> 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
// edgeDNSClient is a proxy interface of the Akamai edgegrid configdns-v2 package that can be stubbed for testing.
|
||||||
|
type AkamaiDNSService interface {
|
||||||
|
ListZones(queryArgs dns.ZoneListQueryArgs) (*dns.ZoneListResponse, error)
|
||||||
|
GetRecordsets(zone string, queryArgs dns.RecordsetQueryArgs) (*dns.RecordSetResponse, error)
|
||||||
|
GetRecord(zone string, name string, recordtype string) (*dns.RecordBody, error)
|
||||||
|
DeleteRecord(record *dns.RecordBody, zone string, recLock bool) error
|
||||||
|
UpdateRecord(record *dns.RecordBody, zone string, recLock bool) error
|
||||||
|
CreateRecordsets(recordsets *dns.Recordsets, zone string, recLock bool) 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 {
|
type AkamaiConfig struct {
|
||||||
DomainFilter endpoint.DomainFilter
|
DomainFilter endpoint.DomainFilter
|
||||||
ZoneIDFilter provider.ZoneIDFilter
|
ZoneIDFilter provider.ZoneIDFilter
|
||||||
@ -57,17 +56,25 @@ type AkamaiConfig struct {
|
|||||||
ClientToken string
|
ClientToken string
|
||||||
ClientSecret string
|
ClientSecret string
|
||||||
AccessToken string
|
AccessToken string
|
||||||
|
EdgercPath string
|
||||||
|
EdgercSection string
|
||||||
|
MaxBody int
|
||||||
|
AccountKey string
|
||||||
DryRun bool
|
DryRun bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// AkamaiProvider implements the DNS provider for Akamai.
|
// AkamaiProvider implements the DNS provider for Akamai.
|
||||||
type AkamaiProvider struct {
|
type AkamaiProvider struct {
|
||||||
provider.BaseProvider
|
provider.BaseProvider
|
||||||
|
// Edgedns zones to filter on
|
||||||
domainFilter endpoint.DomainFilter
|
domainFilter endpoint.DomainFilter
|
||||||
|
// Contract Ids to filter on
|
||||||
zoneIDFilter provider.ZoneIDFilter
|
zoneIDFilter provider.ZoneIDFilter
|
||||||
config edgegrid.Config
|
// Edgegrid library configuration
|
||||||
|
config *edgegrid.Config
|
||||||
dryRun bool
|
dryRun bool
|
||||||
client akamaiClient
|
// Defines client. Allows for mocking.
|
||||||
|
client AkamaiDNSService
|
||||||
}
|
}
|
||||||
|
|
||||||
type akamaiZones struct {
|
type akamaiZones struct {
|
||||||
@ -79,84 +86,126 @@ type akamaiZone struct {
|
|||||||
Zone string `json:"zone"`
|
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.
|
// NewAkamaiProvider initializes a new Akamai DNS based Provider.
|
||||||
func NewAkamaiProvider(akamaiConfig AkamaiConfig) *AkamaiProvider {
|
func NewAkamaiProvider(akamaiConfig AkamaiConfig, akaService AkamaiDNSService) (provider.Provider, error) {
|
||||||
edgeGridConfig := edgegrid.Config{
|
var edgeGridConfig edgegrid.Config
|
||||||
|
|
||||||
|
/*
|
||||||
|
log.Debugf("Host: %s", akamaiConfig.ServiceConsumerDomain)
|
||||||
|
log.Debugf("ClientToken: %s", akamaiConfig.ClientToken)
|
||||||
|
log.Debugf("ClientSecret: %s", akamaiConfig.ClientSecret)
|
||||||
|
log.Debugf("AccessToken: %s", akamaiConfig.AccessToken)
|
||||||
|
log.Debugf("EdgePath: %s", akamaiConfig.EdgercPath)
|
||||||
|
log.Debugf("EdgeSection: %s", akamaiConfig.EdgercSection)
|
||||||
|
*/
|
||||||
|
// environment overrides edgerc file but config needs to be complete
|
||||||
|
if akamaiConfig.ServiceConsumerDomain == "" || akamaiConfig.ClientToken == "" || akamaiConfig.ClientSecret == "" || akamaiConfig.AccessToken == "" {
|
||||||
|
// Kubernetes config incomplete or non existent. Can't mix and match.
|
||||||
|
// Look for Akamai environment or .edgerd creds
|
||||||
|
var err error
|
||||||
|
edgeGridConfig, err = edgegrid.Init(akamaiConfig.EdgercPath, akamaiConfig.EdgercSection) // use default .edgerc location and section
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Edgegrid Init Failed")
|
||||||
|
return &AkamaiProvider{}, err // return empty provider for backward compatibility
|
||||||
|
}
|
||||||
|
edgeGridConfig.HeaderToSign = append(edgeGridConfig.HeaderToSign, "X-External-DNS")
|
||||||
|
} else {
|
||||||
|
// Use external-dns config
|
||||||
|
edgeGridConfig = edgegrid.Config{
|
||||||
Host: akamaiConfig.ServiceConsumerDomain,
|
Host: akamaiConfig.ServiceConsumerDomain,
|
||||||
ClientToken: akamaiConfig.ClientToken,
|
ClientToken: akamaiConfig.ClientToken,
|
||||||
ClientSecret: akamaiConfig.ClientSecret,
|
ClientSecret: akamaiConfig.ClientSecret,
|
||||||
AccessToken: akamaiConfig.AccessToken,
|
AccessToken: akamaiConfig.AccessToken,
|
||||||
MaxBody: 1024,
|
MaxBody: 131072, // same default val as used by Edgegrid
|
||||||
HeaderToSign: []string{
|
HeaderToSign: []string{
|
||||||
"X-External-DNS",
|
"X-External-DNS",
|
||||||
},
|
},
|
||||||
Debug: false,
|
Debug: false,
|
||||||
}
|
}
|
||||||
|
// Check for edgegrid overrides
|
||||||
|
if envval, ok := os.LookupEnv("AKAMAI_MAX_BODY"); ok {
|
||||||
|
if i, err := strconv.Atoi(envval); err == nil {
|
||||||
|
edgeGridConfig.MaxBody = i
|
||||||
|
log.Debugf("Edgegrid maxbody set to %s", envval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if envval, ok := os.LookupEnv("AKAMAI_ACCOUNT_KEY"); ok {
|
||||||
|
edgeGridConfig.AccountKey = envval
|
||||||
|
log.Debugf("Edgegrid applying account key %s", envval)
|
||||||
|
}
|
||||||
|
if envval, ok := os.LookupEnv("AKAMAI_DEBUG"); ok {
|
||||||
|
if dbgval, err := strconv.ParseBool(envval); err == nil {
|
||||||
|
edgeGridConfig.Debug = dbgval
|
||||||
|
log.Debugf("Edgegrid debug set to %s", envval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
provider := &AkamaiProvider{
|
provider := &AkamaiProvider{
|
||||||
domainFilter: akamaiConfig.DomainFilter,
|
domainFilter: akamaiConfig.DomainFilter,
|
||||||
zoneIDFilter: akamaiConfig.ZoneIDFilter,
|
zoneIDFilter: akamaiConfig.ZoneIDFilter,
|
||||||
config: edgeGridConfig,
|
config: &edgeGridConfig,
|
||||||
dryRun: akamaiConfig.DryRun,
|
dryRun: akamaiConfig.DryRun,
|
||||||
client: &akamaiOpenClient{},
|
|
||||||
}
|
}
|
||||||
return provider
|
if akaService != nil {
|
||||||
|
log.Debugf("Using STUB")
|
||||||
|
provider.client = akaService
|
||||||
|
} else {
|
||||||
|
provider.client = provider
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *AkamaiProvider) request(method, path string, body io.Reader) (*http.Response, error) {
|
// Init library for direct endpoint calls
|
||||||
req, err := p.client.NewRequest(p.config, method, fmt.Sprintf("https://%s/%s", p.config.Host, path), body)
|
dns.Init(edgeGridConfig)
|
||||||
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 {
|
return provider, nil
|
||||||
log.Errorf("Akamai client failed to do the request")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !c.IsSuccess(resp) {
|
|
||||||
return nil, c.NewAPIError(resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, err
|
func (p AkamaiProvider) ListZones(queryArgs dns.ZoneListQueryArgs) (*dns.ZoneListResponse, error) {
|
||||||
|
return dns.ListZones(queryArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Look here for endpoint documentation -> https://developer.akamai.com/api/web_performance/fast_dns_zone_management/v2.html#getzones
|
func (p AkamaiProvider) GetRecordsets(zone string, queryArgs dns.RecordsetQueryArgs) (*dns.RecordSetResponse, error) {
|
||||||
func (p *AkamaiProvider) fetchZones() (zones akamaiZones, err error) {
|
return dns.GetRecordsets(zone, queryArgs)
|
||||||
log.Debugf("Trying to fetch zones from Akamai")
|
}
|
||||||
resp, err := p.request("GET", "config-dns/v2/zones?showAll=true&types=primary%2Csecondary", nil)
|
|
||||||
|
func (p AkamaiProvider) CreateRecordsets(recordsets *dns.Recordsets, zone string, reclock bool) error {
|
||||||
|
return recordsets.Save(zone, reclock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p AkamaiProvider) GetRecord(zone string, name string, recordtype string) (*dns.RecordBody, error) {
|
||||||
|
return dns.GetRecord(zone, name, recordtype)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p AkamaiProvider) DeleteRecord(record *dns.RecordBody, zone string, recLock bool) error {
|
||||||
|
return record.Delete(zone, recLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p AkamaiProvider) UpdateRecord(record *dns.RecordBody, zone string, recLock bool) error {
|
||||||
|
return record.Update(zone, recLock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch zones using Edgegrid DNS v2 API
|
||||||
|
func (p AkamaiProvider) fetchZones() (akamaiZones, error) {
|
||||||
|
log.Debugf("Fetching Akamai Edge DNS zones")
|
||||||
|
filteredZones := akamaiZones{Zones: make([]akamaiZone, 0)}
|
||||||
|
queryArgs := dns.ZoneListQueryArgs{Types: "primary", ShowAll: true}
|
||||||
|
// filter based on contractIds
|
||||||
|
if len(p.zoneIDFilter.ZoneIDs) > 0 {
|
||||||
|
queryArgs.ContractIds = strings.Join(p.zoneIDFilter.ZoneIDs, ",")
|
||||||
|
}
|
||||||
|
resp, err := p.client.ListZones(queryArgs) // don't worry about paged results
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to fetch zones from Akamai")
|
log.Errorf("Failed to fetch zones from Akamai")
|
||||||
return zones, err
|
return filteredZones, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.NewDecoder(resp.Body).Decode(&zones)
|
for _, zone := range resp.Zones {
|
||||||
if err != nil {
|
//log.Debugf("Evaluating zone: %s", zone.Zone)
|
||||||
log.Errorf("Could not decode json response from Akamai on zone request")
|
if p.domainFilter.Match(zone.Zone) || !p.domainFilter.IsConfigured() {
|
||||||
return zones, err
|
filteredZones.Zones = append(filteredZones.Zones, akamaiZone{ContractID: zone.ContractId, Zone: zone.Zone})
|
||||||
|
log.Debugf("Fetched zone: '%s' (ZoneID: %s)", zone.Zone, zone.ContractId)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
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)
|
lenFilteredZones := len(filteredZones.Zones)
|
||||||
if lenFilteredZones == 0 {
|
if lenFilteredZones == 0 {
|
||||||
@ -168,53 +217,45 @@ func (p *AkamaiProvider) fetchZones() (zones akamaiZones, err error) {
|
|||||||
return filteredZones, nil
|
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)
|
|
||||||
resp, 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
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
err = json.NewDecoder(resp.Body).Decode(&recordSet)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Could not decode 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.
|
//Records returns the list of records in a given zone.
|
||||||
func (p *AkamaiProvider) Records(context.Context) (endpoints []*endpoint.Endpoint, err error) {
|
func (p AkamaiProvider) Records(context.Context) (endpoints []*endpoint.Endpoint, err error) {
|
||||||
zones, err := p.fetchZones()
|
log.Debugf("Entering Records function")
|
||||||
|
if p.config == nil {
|
||||||
|
log.Errorf("Akamai provider failed initialization!")
|
||||||
|
return endpoints, fmt.Errorf("edge dns provider is not initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
zones, err := p.fetchZones() // returns a filtered set of zones
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("No zones to fetch endpoints from!")
|
log.Warnf("Failed to identify target zones! Error: %s", err.Error())
|
||||||
return endpoints, err
|
return endpoints, err
|
||||||
}
|
}
|
||||||
for _, zone := range zones.Zones {
|
for _, zone := range zones.Zones {
|
||||||
records, err := p.fetchRecordSet(zone.Zone)
|
recordsets, err := p.client.GetRecordsets(zone.Zone, dns.RecordsetQueryArgs{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("No recordsets could be fetched for zone: '%s'!", zone.Zone)
|
log.Errorf("Recordsets retrieval for zone: '%s' failed! %s", zone.Zone, err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if len(recordsets.Recordsets) == 0 {
|
||||||
for _, record := range records.Recordsets {
|
log.Warnf("Zone %s contains no recordsets", zone.Zone)
|
||||||
rdata := make([]string, len(record.Rdata))
|
|
||||||
|
|
||||||
for i, v := range record.Rdata {
|
|
||||||
rdata[i] = v.(string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !p.domainFilter.Match(record.Name) {
|
for _, recordset := range recordsets.Recordsets {
|
||||||
log.Debugf("Skipping endpoint DNSName: '%s' RecordType: '%s', it does not match against Domain filters", record.Name, record.Type)
|
if !provider.SupportedRecordType(recordset.Type) {
|
||||||
|
log.Debugf("Skipping endpoint DNSName: '%s' RecordType: '%s'. Record type not supported.", recordset.Name, recordset.Type)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !p.domainFilter.Match(recordset.Name) {
|
||||||
endpoints = append(endpoints, endpoint.NewEndpoint(record.Name, record.Type, rdata...))
|
log.Debugf("Skipping endpoint. Record name %s doesn't match containing zone %s.", recordset.Name, zone)
|
||||||
log.Debugf("Fetched endpoint DNSName: '%s' RecordType: '%s' Rdata: '%s')", record.Name, record.Type, rdata)
|
continue
|
||||||
|
}
|
||||||
|
var temp interface{} = int64(recordset.TTL)
|
||||||
|
var ttl endpoint.TTL = endpoint.TTL(temp.(int64)) //endpoint.TTL)
|
||||||
|
endpoints = append(endpoints, endpoint.NewEndpointWithTTL(recordset.Name,
|
||||||
|
recordset.Type,
|
||||||
|
ttl,
|
||||||
|
trimTxtRdata(recordset.Rdata, recordset.Type)...))
|
||||||
|
log.Debugf("Fetched endpoint DNSName: '%s' RecordType: '%s' Rdata: '%s')", recordset.Name, recordset.Type, recordset.Rdata)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lenEndpoints := len(endpoints)
|
lenEndpoints := len(endpoints)
|
||||||
@ -222,161 +263,247 @@ func (p *AkamaiProvider) Records(context.Context) (endpoints []*endpoint.Endpoin
|
|||||||
log.Warnf("No endpoints could be fetched")
|
log.Warnf("No endpoints could be fetched")
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("Fetched '%d' endpoints from Akamai", lenEndpoints)
|
log.Debugf("Fetched '%d' endpoints from Akamai", lenEndpoints)
|
||||||
|
log.Debugf("Endpoints [%v]", endpoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
return endpoints, nil
|
return endpoints, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApplyChanges applies a given set of changes in a given zone.
|
// ApplyChanges applies a given set of changes in a given zone.
|
||||||
func (p *AkamaiProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
func (p AkamaiProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||||
|
log.Debugf("Entering ApplyChanges")
|
||||||
|
|
||||||
|
if p.config == nil {
|
||||||
|
log.Errorf("Akamai provider failed initialization!")
|
||||||
|
return fmt.Errorf("edge dns provider failed initialization")
|
||||||
|
}
|
||||||
|
|
||||||
zoneNameIDMapper := provider.ZoneIDName{}
|
zoneNameIDMapper := provider.ZoneIDName{}
|
||||||
zones, err := p.fetchZones()
|
zones, err := p.fetchZones()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("No zones to fetch endpoints from!")
|
log.Errorf("Failed to fetch zones from Akamai")
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, z := range zones.Zones {
|
for _, z := range zones.Zones {
|
||||||
zoneNameIDMapper[z.Zone] = z.Zone
|
zoneNameIDMapper[z.Zone] = z.Zone
|
||||||
}
|
}
|
||||||
|
log.Debugf("Processing zones: [%v]", zoneNameIDMapper)
|
||||||
|
|
||||||
_, cf := p.createRecords(zoneNameIDMapper, changes.Create)
|
// Create recodsets
|
||||||
if !p.dryRun {
|
log.Debugf("Create Changes requested [%v]", changes.Create)
|
||||||
if len(cf) > 0 {
|
if err := p.createRecordsets(zoneNameIDMapper, changes.Create); err != nil {
|
||||||
log.Warnf("Not all desired endpoints could be created, retrying next iteration")
|
return err
|
||||||
for _, f := range cf {
|
}
|
||||||
log.Warnf("Not created was DNSName: '%s' RecordType: '%s'", f.DNSName, f.RecordType)
|
// Delete recordsets
|
||||||
|
log.Debugf("Delete Changes requested [%v]", changes.Delete)
|
||||||
|
if err := p.deleteRecordsets(zoneNameIDMapper, changes.Delete); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Update recordsets
|
||||||
|
log.Debugf("Update Changes requested [%v]", changes.UpdateNew)
|
||||||
|
if err := p.updateNewRecordsets(zoneNameIDMapper, changes.UpdateNew); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Check that all old endpoints were accounted for
|
||||||
|
revRecs := changes.Delete
|
||||||
|
revRecs = append(revRecs, changes.UpdateNew...)
|
||||||
|
for _, rec := range changes.UpdateOld {
|
||||||
|
found := false
|
||||||
|
for _, r := range revRecs {
|
||||||
|
if rec.DNSName == r.DNSName {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if !found {
|
||||||
|
log.Warnf("UpdateOld endpoint '%s' is not accounted for in UpdateNew|Delete endpoint list", rec.DNSName)
|
||||||
_, 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *AkamaiProvider) newAkamaiRecord(dnsName, recordType string, targets ...string) *akamaiRecord {
|
// Create DNS Recordset
|
||||||
cleanTargets := make([]interface{}, len(targets))
|
func newAkamaiRecordset(dnsName, recordType string, ttl int, targets []string) dns.Recordset {
|
||||||
for idx, target := range targets {
|
return dns.Recordset{
|
||||||
cleanTargets[idx] = strings.TrimSuffix(target, ".")
|
|
||||||
}
|
|
||||||
return &akamaiRecord{
|
|
||||||
Name: strings.TrimSuffix(dnsName, "."),
|
Name: strings.TrimSuffix(dnsName, "."),
|
||||||
Rdata: cleanTargets,
|
Rdata: targets,
|
||||||
Type: recordType,
|
Type: recordType,
|
||||||
TTL: 300,
|
TTL: ttl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *AkamaiProvider) createRecords(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) (created []*endpoint.Endpoint, failed []*endpoint.Endpoint) {
|
// cleanTargets preps recordset rdata if necessary for EdgeDNS
|
||||||
|
func cleanTargets(rtype string, targets ...string) []string {
|
||||||
|
log.Debugf("Targets to clean: [%v]", targets)
|
||||||
|
//var targets []string = tgts
|
||||||
|
if rtype == "CNAME" || rtype == "SRV" {
|
||||||
|
for idx, target := range targets {
|
||||||
|
targets[idx] = strings.TrimSuffix(target, ".")
|
||||||
|
}
|
||||||
|
} else if rtype == "TXT" {
|
||||||
|
for idx, target := range targets {
|
||||||
|
log.Debugf("TXT data to clean: [%s]", target)
|
||||||
|
// need to embed text data in quotes. Make sure not piling on
|
||||||
|
target = strings.Trim(target, "\"")
|
||||||
|
// bug in DNS API with embedded quotes.
|
||||||
|
if strings.Contains(target, "owner") && strings.Contains(target, "\"") {
|
||||||
|
target = strings.ReplaceAll(target, "\"", "`")
|
||||||
|
}
|
||||||
|
targets[idx] = "\"" + target + "\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debugf("Clean targets: [%v]", targets)
|
||||||
|
|
||||||
|
return targets
|
||||||
|
}
|
||||||
|
|
||||||
|
// trimTxtRdata removes surrounding quotes for received TXT rdata
|
||||||
|
func trimTxtRdata(rdata []string, rtype string) []string {
|
||||||
|
if rtype == "TXT" {
|
||||||
|
for idx, d := range rdata {
|
||||||
|
//rdata[idx] = strings.Trim(d, "\"")
|
||||||
|
if strings.Contains(d, "`") {
|
||||||
|
rdata[idx] = strings.ReplaceAll(d, "`", "\"")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debugf("Trimmed data: [%v]", rdata)
|
||||||
|
|
||||||
|
return rdata
|
||||||
|
}
|
||||||
|
|
||||||
|
func ttlAsInt(src endpoint.TTL) int {
|
||||||
|
var temp interface{} = int64(src)
|
||||||
|
var temp64 = temp.(int64)
|
||||||
|
var ttl int = edgeDNSRecordTTL // int
|
||||||
|
if temp64 > 0 && temp64 <= int64(maxInt) {
|
||||||
|
ttl = int(temp64)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ttl
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Endpoint Recordsets
|
||||||
|
func (p AkamaiProvider) createRecordsets(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) error {
|
||||||
|
if len(endpoints) == 0 {
|
||||||
|
log.Info("No endpoints to create")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
endpointsByZone := edgeChangesByZone(zoneNameIDMapper, endpoints)
|
||||||
|
|
||||||
|
// create all recordsets by zone
|
||||||
|
for zone, endpoints := range endpointsByZone {
|
||||||
|
recordsets := &dns.Recordsets{Recordsets: make([]dns.Recordset, 0)}
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
if !p.domainFilter.Match(endpoint.DNSName) {
|
newrec := newAkamaiRecordset(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)
|
endpoint.RecordType,
|
||||||
continue
|
ttlAsInt(endpoint.RecordTTL),
|
||||||
|
cleanTargets(endpoint.RecordType, endpoint.Targets...))
|
||||||
|
logfields := log.Fields{
|
||||||
|
"record": newrec.Name,
|
||||||
|
"type": newrec.Type,
|
||||||
|
"ttl": newrec.TTL,
|
||||||
|
"target": fmt.Sprintf("%v", newrec.Rdata),
|
||||||
|
"zone": zone,
|
||||||
|
}
|
||||||
|
log.WithFields(logfields).Info("Creating recordsets")
|
||||||
|
recordsets.Recordsets = append(recordsets.Recordsets, newrec)
|
||||||
}
|
}
|
||||||
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 {
|
if p.dryRun {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
_, err := p.request("POST", "config-dns/v2/zones/"+zoneName+"/names/"+endpoint.DNSName+"/types/"+endpoint.RecordType, bytes.NewReader(body))
|
// Create recordsets all at once
|
||||||
|
err := p.client.CreateRecordsets(recordsets, zone, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to create Akamai endpoint DNSName: '%s' RecordType: '%s' for zone: '%s'", endpoint.DNSName, endpoint.RecordType, zoneName)
|
log.Errorf("Failed to create endpoints for DNS zone %s. Error: %s", zone, err.Error())
|
||||||
failed = append(failed, endpoint)
|
return err
|
||||||
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 provider.ZoneIDName, endpoints []*endpoint.Endpoint) (deleted []*endpoint.Endpoint, failed []*endpoint.Endpoint) {
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p AkamaiProvider) deleteRecordsets(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) error {
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
if !p.domainFilter.Match(endpoint.DNSName) {
|
zoneName, _ := zoneNameIDMapper.FindZone(endpoint.DNSName)
|
||||||
log.Debugf("Skipping deletion at Akamai of endpoint: '%s' type: '%s', it does not match against Domain filters", endpoint.DNSName, endpoint.RecordType)
|
if zoneName == "" {
|
||||||
|
log.Debugf("Skipping Akamai Edge DNS endpoint deletion: '%s' type: '%s', it does not match against Domain filters", endpoint.DNSName, endpoint.RecordType)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if zoneName, _ := zoneNameIDMapper.FindZone(endpoint.DNSName); zoneName != "" {
|
log.Infof("Akamai Edge DNS recordset deletion- Zone: '%s', DNSName: '%s', RecordType: '%s', Targets: '%+v'", zoneName, endpoint.DNSName, endpoint.RecordType, endpoint.Targets)
|
||||||
log.Infof("Deletion at Akamai FastDNS - Zone: '%s', DNSName: '%s', RecordType: '%s', Targets: '%+v'", zoneName, endpoint.DNSName, endpoint.RecordType, endpoint.Targets)
|
|
||||||
|
|
||||||
if p.dryRun {
|
if p.dryRun {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := p.request("DELETE", "config-dns/v2/zones/"+zoneName+"/names/"+endpoint.DNSName+"/types/"+endpoint.RecordType, nil)
|
recName := strings.TrimSuffix(endpoint.DNSName, ".")
|
||||||
|
rec, err := p.client.GetRecord(zoneName, recName, endpoint.RecordType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to delete Akamai endpoint DNSName: '%s' for zone: '%s'", endpoint.DNSName, zoneName)
|
// error not found?
|
||||||
failed = append(failed, endpoint)
|
if _, ok := err.(*dns.RecordError); !ok {
|
||||||
|
return fmt.Errorf("endpoint deletion. record validation failed. error: %s", err.Error())
|
||||||
|
}
|
||||||
|
log.Infof("Endpoint deletion. Record doesn't exist. Name: %s, Type: %s", recName, endpoint.RecordType)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
deleted = append(deleted, endpoint)
|
if err := p.client.DeleteRecord(rec, zoneName, true); err != nil {
|
||||||
} else {
|
log.Errorf("edge dns recordset deletion failed. error: %s", err.Error())
|
||||||
log.Warnf("No matching zone for endpoint deletion DNSName: '%s' RecordType: '%s'", endpoint.DNSName, endpoint.RecordType)
|
return err
|
||||||
failed = append(failed, endpoint)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return deleted, failed
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *AkamaiProvider) updateNewRecords(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) (updated []*endpoint.Endpoint, failed []*endpoint.Endpoint) {
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update endpoint recordsets
|
||||||
|
func (p AkamaiProvider) updateNewRecordsets(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) error {
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
if !p.domainFilter.Match(endpoint.DNSName) {
|
zoneName, _ := zoneNameIDMapper.FindZone(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)
|
if zoneName == "" {
|
||||||
|
log.Debugf("Skipping Akamai Edge DNS endpoint update: '%s' type: '%s', it does not match against Domain filters", endpoint.DNSName, endpoint.RecordType)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if zoneName, _ := zoneNameIDMapper.FindZone(endpoint.DNSName); zoneName != "" {
|
log.Infof("Akamai Edge DNS recordset update - Zone: '%s', DNSName: '%s', RecordType: '%s', Targets: '%+v'", zoneName, endpoint.DNSName, endpoint.RecordType, endpoint.Targets)
|
||||||
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 {
|
if p.dryRun {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := p.request("PUT", "config-dns/v2/zones/"+zoneName+"/names/"+endpoint.DNSName+"/types/"+endpoint.RecordType, bytes.NewReader(body))
|
recName := strings.TrimSuffix(endpoint.DNSName, ".")
|
||||||
|
rec, err := p.client.GetRecord(zoneName, recName, endpoint.RecordType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to update Akamai endpoint DNSName: '%s' for zone: '%s'", endpoint.DNSName, zoneName)
|
log.Errorf("Endpoint update. Record validation failed. Error: %s", err.Error())
|
||||||
failed = append(failed, endpoint)
|
return err
|
||||||
|
}
|
||||||
|
rec.TTL = ttlAsInt(endpoint.RecordTTL)
|
||||||
|
rec.Target = cleanTargets(endpoint.RecordType, endpoint.Targets...)
|
||||||
|
if err := p.client.UpdateRecord(rec, zoneName, true); err != nil {
|
||||||
|
log.Errorf("Akamai Edge DNS recordset update failed. Error: %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// edgeChangesByZone separates a multi-zone change into a single change per zone.
|
||||||
|
func edgeChangesByZone(zoneMap provider.ZoneIDName, endpoints []*endpoint.Endpoint) map[string][]*endpoint.Endpoint {
|
||||||
|
createsByZone := make(map[string][]*endpoint.Endpoint, len(zoneMap))
|
||||||
|
for _, z := range zoneMap {
|
||||||
|
createsByZone[z] = make([]*endpoint.Endpoint, 0)
|
||||||
|
}
|
||||||
|
for _, ep := range endpoints {
|
||||||
|
zone, _ := zoneMap.FindZone(ep.DNSName)
|
||||||
|
if zone != "" {
|
||||||
|
createsByZone[zone] = append(createsByZone[zone], ep)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
updated = append(updated, endpoint)
|
log.Debugf("Skipping Akamai Edge DNS creation of endpoint: '%s' type: '%s', it does not match against Domain filters", ep.DNSName, ep.RecordType)
|
||||||
} else {
|
|
||||||
log.Warnf("No matching zone for endpoint update DNSName: '%s' RecordType: '%s'", endpoint.DNSName, endpoint.RecordType)
|
|
||||||
failed = append(failed, endpoint)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return updated, failed
|
return createsByZone
|
||||||
}
|
}
|
||||||
|
@ -17,148 +17,208 @@ limitations under the License.
|
|||||||
package akamai
|
package akamai
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
log "github.com/sirupsen/logrus"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
dns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v2"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"sigs.k8s.io/external-dns/endpoint"
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
"sigs.k8s.io/external-dns/plan"
|
"sigs.k8s.io/external-dns/plan"
|
||||||
"sigs.k8s.io/external-dns/provider"
|
"sigs.k8s.io/external-dns/provider"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockAkamaiClient struct {
|
type edgednsStubData struct {
|
||||||
mock.Mock
|
objType string // zone, record, recordsets
|
||||||
|
output []interface{}
|
||||||
|
updateRecords []interface{}
|
||||||
|
createRecords []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockAkamaiClient) NewRequest(config edgegrid.Config, met, p string, b io.Reader) (*http.Request, error) {
|
type edgednsStub struct {
|
||||||
switch {
|
stubData map[string]edgednsStubData
|
||||||
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\"}"))
|
|
||||||
case met == "ERROR":
|
|
||||||
b = bytes.NewReader([]byte("{\"status\": 404 }"))
|
|
||||||
}
|
|
||||||
req := httptest.NewRequest(met, p, b)
|
|
||||||
return req, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockAkamaiClient) Do(config edgegrid.Config, req *http.Request) (*http.Response, error) {
|
func newStub() *edgednsStub {
|
||||||
handler := func(w http.ResponseWriter, r *http.Request) (isError bool) {
|
return &edgednsStub{
|
||||||
b, _ := ioutil.ReadAll(r.Body)
|
stubData: make(map[string]edgednsStubData),
|
||||||
io.WriteString(w, string(b))
|
}
|
||||||
return string(b) == "{\"status\": 404 }"
|
|
||||||
}
|
}
|
||||||
w := httptest.NewRecorder()
|
|
||||||
err := handler(w, req)
|
|
||||||
resp := w.Result()
|
|
||||||
|
|
||||||
if err == true {
|
func createAkamaiStubProvider(stub *edgednsStub, domfilter endpoint.DomainFilter, idfilter provider.ZoneIDFilter) (*AkamaiProvider, error) {
|
||||||
resp.StatusCode = 400
|
|
||||||
|
akamaiConfig := AkamaiConfig{
|
||||||
|
DomainFilter: domfilter,
|
||||||
|
ZoneIDFilter: idfilter,
|
||||||
|
ServiceConsumerDomain: "testzone.com",
|
||||||
|
ClientToken: "test_token",
|
||||||
|
ClientSecret: "test_client_secret",
|
||||||
|
AccessToken: "test_access_token",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prov, err := NewAkamaiProvider(akamaiConfig, stub)
|
||||||
|
aprov := prov.(*AkamaiProvider)
|
||||||
|
return aprov, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *edgednsStub) createStubDataEntry(objtype string) {
|
||||||
|
|
||||||
|
log.Debugf("Creating stub data entry")
|
||||||
|
if _, exists := r.stubData[objtype]; !exists {
|
||||||
|
r.stubData[objtype] = edgednsStubData{objType: objtype}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *edgednsStub) setOutput(objtype string, output []interface{}) {
|
||||||
|
|
||||||
|
log.Debugf("Setting output to %v", output)
|
||||||
|
r.createStubDataEntry(objtype)
|
||||||
|
stubdata := r.stubData[objtype]
|
||||||
|
stubdata.output = output
|
||||||
|
r.stubData[objtype] = stubdata
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *edgednsStub) setUpdateRecords(objtype string, records []interface{}) {
|
||||||
|
|
||||||
|
log.Debugf("Setting updaterecords to %v", records)
|
||||||
|
r.createStubDataEntry(objtype)
|
||||||
|
stubdata := r.stubData[objtype]
|
||||||
|
stubdata.updateRecords = records
|
||||||
|
r.stubData[objtype] = stubdata
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *edgednsStub) setCreateRecords(objtype string, records []interface{}) {
|
||||||
|
|
||||||
|
log.Debugf("Setting createrecords to %v", records)
|
||||||
|
r.createStubDataEntry(objtype)
|
||||||
|
stubdata := r.stubData[objtype]
|
||||||
|
stubdata.createRecords = records
|
||||||
|
r.stubData[objtype] = stubdata
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *edgednsStub) ListZones(queryArgs dns.ZoneListQueryArgs) (*dns.ZoneListResponse, error) {
|
||||||
|
|
||||||
|
log.Debugf("Entering ListZones")
|
||||||
|
// Ignore Metadata`
|
||||||
|
resp := &dns.ZoneListResponse{}
|
||||||
|
zones := make([]*dns.ZoneResponse, 0)
|
||||||
|
for _, zname := range r.stubData["zone"].output {
|
||||||
|
log.Debugf("Processing output: %v", zname)
|
||||||
|
zn := &dns.ZoneResponse{Zone: zname.(string), ContractId: "contract"}
|
||||||
|
log.Debugf("Created Zone Object: %v", zn)
|
||||||
|
zones = append(zones, zn)
|
||||||
|
}
|
||||||
|
resp.Zones = zones
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *edgednsStub) GetRecordsets(zone string, queryArgs dns.RecordsetQueryArgs) (*dns.RecordSetResponse, error) {
|
||||||
|
|
||||||
|
log.Debugf("Entering GetRecordsets")
|
||||||
|
// Ignore Metadata`
|
||||||
|
|
||||||
|
resp := &dns.RecordSetResponse{}
|
||||||
|
sets := make([]dns.Recordset, 0)
|
||||||
|
for _, rec := range r.stubData["recordset"].output {
|
||||||
|
rset := rec.(dns.Recordset)
|
||||||
|
sets = append(sets, rset)
|
||||||
|
}
|
||||||
|
resp.Recordsets = sets
|
||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRequestError(t *testing.T) {
|
func (r *edgednsStub) CreateRecordsets(recordsets *dns.Recordsets, zone string, reclock bool) error {
|
||||||
config := AkamaiConfig{}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
return nil
|
||||||
c := NewAkamaiProvider(config)
|
|
||||||
c.client = client
|
|
||||||
|
|
||||||
m := "ERROR"
|
|
||||||
p := ""
|
|
||||||
b := ""
|
|
||||||
x, err := c.request(m, p, bytes.NewReader([]byte(b)))
|
|
||||||
assert.Nil(t, x)
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *edgednsStub) GetRecord(zone string, name string, record_type string) (*dns.RecordBody, error) {
|
||||||
|
|
||||||
|
resp := &dns.RecordBody{}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *edgednsStub) DeleteRecord(record *dns.RecordBody, zone string, recLock bool) error {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *edgednsStub) UpdateRecord(record *dns.RecordBody, zone string, recLock bool) error {
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test FetchZones
|
||||||
func TestFetchZonesZoneIDFilter(t *testing.T) {
|
func TestFetchZonesZoneIDFilter(t *testing.T) {
|
||||||
config := AkamaiConfig{
|
|
||||||
ZoneIDFilter: provider.NewZoneIDFilter([]string{"Test"}),
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
stub := newStub()
|
||||||
c := NewAkamaiProvider(config)
|
domfilter := endpoint.DomainFilter{}
|
||||||
c.client = client
|
idfilter := provider.NewZoneIDFilter([]string{"Test"})
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
stub.setOutput("zone", []interface{}{"test1.testzone.com", "test2.testzone.com"})
|
||||||
|
|
||||||
x, _ := c.fetchZones()
|
x, _ := c.fetchZones()
|
||||||
y, _ := json.Marshal(x)
|
y, _ := json.Marshal(x)
|
||||||
if assert.NotNil(t, y) {
|
if assert.NotNil(t, y) {
|
||||||
assert.Equal(t, "{\"zones\":[{\"contractId\":\"Test\",\"zone\":\"example.com\"}]}", string(y))
|
assert.Equal(t, "{\"zones\":[{\"contractId\":\"contract\",\"zone\":\"test1.testzone.com\"},{\"contractId\":\"contract\",\"zone\":\"test2.testzone.com\"}]}", string(y))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
func TestFetchZonesEmpty(t *testing.T) {
|
func TestFetchZonesEmpty(t *testing.T) {
|
||||||
config := AkamaiConfig{
|
|
||||||
DomainFilter: endpoint.NewDomainFilter([]string{"Nonexistent"}),
|
|
||||||
ZoneIDFilter: provider.NewZoneIDFilter([]string{"Nonexistent"}),
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
stub := newStub()
|
||||||
c := NewAkamaiProvider(config)
|
domfilter := endpoint.NewDomainFilter([]string{"Nonexistent"})
|
||||||
c.client = client
|
idfilter := provider.NewZoneIDFilter([]string{"Nonexistent"})
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
stub.setOutput("zone", []interface{}{})
|
||||||
|
|
||||||
x, _ := c.fetchZones()
|
x, _ := c.fetchZones()
|
||||||
y, _ := json.Marshal(x)
|
y, _ := json.Marshal(x)
|
||||||
if assert.NotNil(t, y) {
|
if assert.NotNil(t, y) {
|
||||||
assert.Equal(t, "{\"zones\":null}", string(y))
|
assert.Equal(t, "{\"zones\":[]}", 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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAkamaiRecords tests record endpoint
|
||||||
func TestAkamaiRecords(t *testing.T) {
|
func TestAkamaiRecords(t *testing.T) {
|
||||||
config := AkamaiConfig{}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
|
||||||
c := NewAkamaiProvider(config)
|
|
||||||
c.client = client
|
|
||||||
|
|
||||||
|
stub := newStub()
|
||||||
|
domfilter := endpoint.DomainFilter{}
|
||||||
|
idfilter := provider.ZoneIDFilter{}
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
stub.setOutput("zone", []interface{}{"test1.testzone.com"})
|
||||||
|
recordsets := make([]interface{}, 0)
|
||||||
|
recordsets = append(recordsets, dns.Recordset{
|
||||||
|
Name: "www.example.com",
|
||||||
|
Type: endpoint.RecordTypeA,
|
||||||
|
Rdata: []string{"10.0.0.2", "10.0.0.3"},
|
||||||
|
})
|
||||||
|
recordsets = append(recordsets, dns.Recordset{
|
||||||
|
Name: "www.example.com",
|
||||||
|
Type: endpoint.RecordTypeTXT,
|
||||||
|
Rdata: []string{"heritage=external-dns,external-dns/owner=default"},
|
||||||
|
})
|
||||||
|
recordsets = append(recordsets, dns.Recordset{
|
||||||
|
Name: "www.exclude.me",
|
||||||
|
Type: endpoint.RecordTypeA,
|
||||||
|
Rdata: []string{"192.168.0.1", "192.168.0.2"},
|
||||||
|
})
|
||||||
|
stub.setOutput("recordset", recordsets)
|
||||||
endpoints := make([]*endpoint.Endpoint, 0)
|
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.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.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||||
@ -170,29 +230,43 @@ func TestAkamaiRecords(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
func TestAkamaiRecordsEmpty(t *testing.T) {
|
func TestAkamaiRecordsEmpty(t *testing.T) {
|
||||||
config := AkamaiConfig{
|
|
||||||
ZoneIDFilter: provider.NewZoneIDFilter([]string{"Nonexistent"}),
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
stub := newStub()
|
||||||
c := NewAkamaiProvider(config)
|
domfilter := endpoint.DomainFilter{}
|
||||||
c.client = client
|
idfilter := provider.NewZoneIDFilter([]string{"Nonexistent"})
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
stub.setOutput("zone", []interface{}{"test1.testzone.com"})
|
||||||
|
recordsets := make([]interface{}, 0)
|
||||||
|
stub.setOutput("recordset", recordsets)
|
||||||
|
|
||||||
x, _ := c.Records(context.Background())
|
x, _ := c.Records(context.Background())
|
||||||
assert.Nil(t, x)
|
assert.Nil(t, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
func TestAkamaiRecordsFilters(t *testing.T) {
|
func TestAkamaiRecordsFilters(t *testing.T) {
|
||||||
config := AkamaiConfig{
|
|
||||||
DomainFilter: endpoint.NewDomainFilter([]string{"www.exclude.me"}),
|
|
||||||
ZoneIDFilter: provider.NewZoneIDFilter([]string{"Exclude-Me"}),
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
|
||||||
c := NewAkamaiProvider(config)
|
|
||||||
c.client = client
|
|
||||||
|
|
||||||
|
stub := newStub()
|
||||||
|
domfilter := endpoint.NewDomainFilter([]string{"www.exclude.me"})
|
||||||
|
idfilter := provider.ZoneIDFilter{}
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
stub.setOutput("zone", []interface{}{"www.exclude.me"})
|
||||||
|
recordsets := make([]interface{}, 0)
|
||||||
|
recordsets = append(recordsets, dns.Recordset{
|
||||||
|
Name: "www.example.com",
|
||||||
|
Type: endpoint.RecordTypeA,
|
||||||
|
Rdata: []string{"10.0.0.2", "10.0.0.3"},
|
||||||
|
})
|
||||||
|
recordsets = append(recordsets, dns.Recordset{
|
||||||
|
Name: "www.exclude.me",
|
||||||
|
Type: endpoint.RecordTypeA,
|
||||||
|
Rdata: []string{"192.168.0.1", "192.168.0.2"},
|
||||||
|
})
|
||||||
|
stub.setOutput("recordset", recordsets)
|
||||||
endpoints := make([]*endpoint.Endpoint, 0)
|
endpoints := make([]*endpoint.Endpoint, 0)
|
||||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.exclude.me", endpoint.RecordTypeA, "192.168.0.1", "192.168.0.2"))
|
endpoints = append(endpoints, endpoint.NewEndpoint("www.exclude.me", endpoint.RecordTypeA, "192.168.0.1", "192.168.0.2"))
|
||||||
|
|
||||||
@ -202,32 +276,32 @@ func TestAkamaiRecordsFilters(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCreateRecords tests create function
|
||||||
|
// (p AkamaiProvider) createRecordsets(zoneNameIDMapper provider.ZoneIDName, endpoints []*endpoint.Endpoint) error
|
||||||
func TestCreateRecords(t *testing.T) {
|
func TestCreateRecords(t *testing.T) {
|
||||||
config := AkamaiConfig{}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
stub := newStub()
|
||||||
c := NewAkamaiProvider(config)
|
domfilter := endpoint.DomainFilter{}
|
||||||
c.client = client
|
idfilter := provider.ZoneIDFilter{}
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
||||||
endpoints := make([]*endpoint.Endpoint, 0)
|
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.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.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||||
|
|
||||||
x, _ := c.createRecords(zoneNameIDMapper, endpoints)
|
err = c.createRecordsets(zoneNameIDMapper, endpoints)
|
||||||
if assert.NotNil(t, x) {
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, endpoints, x)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateRecordsDomainFilter(t *testing.T) {
|
func TestCreateRecordsDomainFilter(t *testing.T) {
|
||||||
config := AkamaiConfig{
|
|
||||||
DomainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
stub := newStub()
|
||||||
c := NewAkamaiProvider(config)
|
domfilter := endpoint.DomainFilter{}
|
||||||
c.client = client
|
idfilter := provider.ZoneIDFilter{}
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
||||||
endpoints := make([]*endpoint.Endpoint, 0)
|
endpoints := make([]*endpoint.Endpoint, 0)
|
||||||
@ -235,38 +309,36 @@ func TestCreateRecordsDomainFilter(t *testing.T) {
|
|||||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
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"))
|
exclude := append(endpoints, endpoint.NewEndpoint("www.exclude.me", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||||
|
|
||||||
x, _ := c.createRecords(zoneNameIDMapper, exclude)
|
err = c.createRecordsets(zoneNameIDMapper, exclude)
|
||||||
if assert.NotNil(t, x) {
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, endpoints, x)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDeleteRecords validate delete
|
||||||
func TestDeleteRecords(t *testing.T) {
|
func TestDeleteRecords(t *testing.T) {
|
||||||
config := AkamaiConfig{}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
stub := newStub()
|
||||||
c := NewAkamaiProvider(config)
|
domfilter := endpoint.DomainFilter{}
|
||||||
c.client = client
|
idfilter := provider.ZoneIDFilter{}
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
||||||
endpoints := make([]*endpoint.Endpoint, 0)
|
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.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.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||||
|
|
||||||
x, _ := c.deleteRecords(zoneNameIDMapper, endpoints)
|
err = c.deleteRecordsets(zoneNameIDMapper, endpoints)
|
||||||
if assert.NotNil(t, x) {
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, endpoints, x)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
func TestDeleteRecordsDomainFilter(t *testing.T) {
|
func TestDeleteRecordsDomainFilter(t *testing.T) {
|
||||||
config := AkamaiConfig{
|
|
||||||
DomainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
stub := newStub()
|
||||||
c := NewAkamaiProvider(config)
|
domfilter := endpoint.NewDomainFilter([]string{"example.com"})
|
||||||
c.client = client
|
idfilter := provider.ZoneIDFilter{}
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
||||||
endpoints := make([]*endpoint.Endpoint, 0)
|
endpoints := make([]*endpoint.Endpoint, 0)
|
||||||
@ -274,38 +346,36 @@ func TestDeleteRecordsDomainFilter(t *testing.T) {
|
|||||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
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"))
|
exclude := append(endpoints, endpoint.NewEndpoint("www.exclude.me", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||||
|
|
||||||
x, _ := c.deleteRecords(zoneNameIDMapper, exclude)
|
err = c.deleteRecordsets(zoneNameIDMapper, exclude)
|
||||||
if assert.NotNil(t, x) {
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, endpoints, x)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test record update func
|
||||||
func TestUpdateRecords(t *testing.T) {
|
func TestUpdateRecords(t *testing.T) {
|
||||||
config := AkamaiConfig{}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
stub := newStub()
|
||||||
c := NewAkamaiProvider(config)
|
domfilter := endpoint.DomainFilter{}
|
||||||
c.client = client
|
idfilter := provider.ZoneIDFilter{}
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
||||||
endpoints := make([]*endpoint.Endpoint, 0)
|
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.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.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
||||||
|
|
||||||
x, _ := c.updateNewRecords(zoneNameIDMapper, endpoints)
|
err = c.updateNewRecordsets(zoneNameIDMapper, endpoints)
|
||||||
if assert.NotNil(t, x) {
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, endpoints, x)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
func TestUpdateRecordsDomainFilter(t *testing.T) {
|
func TestUpdateRecordsDomainFilter(t *testing.T) {
|
||||||
config := AkamaiConfig{
|
|
||||||
DomainFilter: endpoint.NewDomainFilter([]string{"example.com"}),
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
stub := newStub()
|
||||||
c := NewAkamaiProvider(config)
|
domfilter := endpoint.NewDomainFilter([]string{"example.com"})
|
||||||
c.client = client
|
idfilter := provider.ZoneIDFilter{}
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
zoneNameIDMapper := provider.ZoneIDName{"example.com": "example.com"}
|
||||||
endpoints := make([]*endpoint.Endpoint, 0)
|
endpoints := make([]*endpoint.Endpoint, 0)
|
||||||
@ -313,19 +383,19 @@ func TestUpdateRecordsDomainFilter(t *testing.T) {
|
|||||||
endpoints = append(endpoints, endpoint.NewEndpoint("www.example.com", endpoint.RecordTypeTXT, "heritage=external-dns,external-dns/owner=default"))
|
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"))
|
exclude := append(endpoints, endpoint.NewEndpoint("www.exclude.me", endpoint.RecordTypeA, "10.0.0.2", "10.0.0.3"))
|
||||||
|
|
||||||
x, _ := c.updateNewRecords(zoneNameIDMapper, exclude)
|
err = c.updateNewRecordsets(zoneNameIDMapper, exclude)
|
||||||
if assert.NotNil(t, x) {
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, endpoints, x)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAkamaiApplyChanges(t *testing.T) {
|
func TestAkamaiApplyChanges(t *testing.T) {
|
||||||
config := AkamaiConfig{}
|
|
||||||
|
|
||||||
client := &mockAkamaiClient{}
|
stub := newStub()
|
||||||
c := NewAkamaiProvider(config)
|
domfilter := endpoint.NewDomainFilter([]string{"example.com"})
|
||||||
c.client = client
|
idfilter := provider.ZoneIDFilter{}
|
||||||
|
c, err := createAkamaiStubProvider(stub, domfilter, idfilter)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
stub.setOutput("zone", []interface{}{"example.com"})
|
||||||
changes := &plan.Changes{}
|
changes := &plan.Changes{}
|
||||||
changes.Create = []*endpoint.Endpoint{
|
changes.Create = []*endpoint.Endpoint{
|
||||||
{DNSName: "www.example.com", RecordType: "A", Targets: endpoint.Targets{"target"}, RecordTTL: 300},
|
{DNSName: "www.example.com", RecordType: "A", Targets: endpoint.Targets{"target"}, RecordTTL: 300},
|
||||||
|
Loading…
Reference in New Issue
Block a user