mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 09:06:58 +02:00
provider/bluecat: add full deploy functionality
New configuration options created for setting the DNS deployment type, as well as the DNS server to deploy. A DNS server name must be provided and a valid DNS deployment type must be set in order for a deployment to be initiated. Currently, the only supported deployment type is "full deploy", however "quick deploy" and "selective deploy" could be added in the future.
This commit is contained in:
parent
261bcadd7c
commit
8aef3e089f
@ -82,6 +82,8 @@ The options for configuring the Bluecat Provider are available through the JSON
|
||||
| dnsConfiguration | Yes |
|
||||
| dnsView | Yes |
|
||||
| rootZone | Yes |
|
||||
| dnsServerName | No |
|
||||
| dnsDeployType | No |
|
||||
| skipTLSVerify | No (default false) |
|
||||
|
||||
#### Deploy
|
||||
|
2
main.go
2
main.go
@ -212,7 +212,7 @@ func main() {
|
||||
case "azure-private-dns":
|
||||
p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneIDFilter, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.DryRun)
|
||||
case "bluecat":
|
||||
p, err = bluecat.NewBluecatProvider(cfg.BluecatConfigFile, cfg.BluecatDNSConfiguration, cfg.BluecatDNSView, cfg.BluecatGatewayHost, cfg.BluecatRootZone, domainFilter, zoneIDFilter, cfg.DryRun, cfg.BluecatSkipTLSVerify)
|
||||
p, err = bluecat.NewBluecatProvider(cfg.BluecatConfigFile, cfg.BluecatDNSConfiguration, cfg.BluecatDNSServerName, cfg.BluecatDNSDeployType, cfg.BluecatDNSView, cfg.BluecatGatewayHost, cfg.BluecatRootZone, domainFilter, zoneIDFilter, cfg.DryRun, cfg.BluecatSkipTLSVerify)
|
||||
case "vinyldns":
|
||||
p, err = vinyldns.NewVinylDNSProvider(domainFilter, zoneIDFilter, cfg.DryRun)
|
||||
case "vultr":
|
||||
|
@ -96,6 +96,8 @@ type Config struct {
|
||||
BluecatDNSView string
|
||||
BluecatGatewayHost string
|
||||
BluecatRootZone string
|
||||
BluecatDNSServerName string
|
||||
BluecatDNSDeployType string
|
||||
BluecatSkipTLSVerify bool
|
||||
CloudflareProxied bool
|
||||
CloudflareZonesPerPage int
|
||||
@ -228,6 +230,7 @@ var defaultConfig = &Config{
|
||||
AzureResourceGroup: "",
|
||||
AzureSubscriptionID: "",
|
||||
BluecatConfigFile: "/etc/kubernetes/bluecat.json",
|
||||
BluecatDNSDeployType: "no-deploy",
|
||||
CloudflareProxied: false,
|
||||
CloudflareZonesPerPage: 50,
|
||||
CoreDNSPrefix: "/skydns/",
|
||||
@ -426,6 +429,8 @@ func (cfg *Config) ParseFlags(args []string) error {
|
||||
app.Flag("bluecat-gateway-host", "When using the Bluecat provider, specify the Bluecat Gateway Host (optional when --provider=bluecat)").Default("").StringVar(&cfg.BluecatGatewayHost)
|
||||
app.Flag("bluecat-root-zone", "When using the Bluecat provider, specify the Bluecat root zone (optional when --provider=bluecat)").Default("").StringVar(&cfg.BluecatRootZone)
|
||||
app.Flag("bluecat-skip-tls-verify", "When using the Bluecat provider, specify to skip TLS verification (optional when --provider=bluecat) (default: false)").BoolVar(&cfg.BluecatSkipTLSVerify)
|
||||
app.Flag("bluecat-dns-server-name", "When using the Bluecat provider, specify the Bluecat DNS Server to initiate deploys against. This is only used if --bluecat-dns-deploy-type is not 'no-deploy' (optional when --provider=bluecat)").Default("").StringVar(&cfg.BluecatDNSServerName)
|
||||
app.Flag("bluecat-dns-deploy-type", "When using the Bluecat provider, specify the type of DNS deployment to initiate after records are updated. Valid options are 'full-deploy' and 'no-deploy'. Deploy will only execute if --bluecat-dns-server-name is set (optional when --provider=bluecat)").Default(defaultConfig.BluecatDNSDeployType).StringVar(&cfg.BluecatDNSDeployType)
|
||||
|
||||
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)
|
||||
|
@ -67,10 +67,12 @@ var (
|
||||
AzureResourceGroup: "",
|
||||
AzureSubscriptionID: "",
|
||||
BluecatDNSConfiguration: "",
|
||||
BluecatDNSServerName: "",
|
||||
BluecatConfigFile: "/etc/kubernetes/bluecat.json",
|
||||
BluecatDNSView: "",
|
||||
BluecatGatewayHost: "",
|
||||
BluecatRootZone: "",
|
||||
BluecatDNSDeployType: defaultConfig.BluecatDNSDeployType,
|
||||
BluecatSkipTLSVerify: false,
|
||||
CloudflareProxied: false,
|
||||
CloudflareZonesPerPage: 50,
|
||||
@ -162,10 +164,12 @@ var (
|
||||
AzureResourceGroup: "arg",
|
||||
AzureSubscriptionID: "arg",
|
||||
BluecatDNSConfiguration: "arg",
|
||||
BluecatDNSServerName: "arg",
|
||||
BluecatConfigFile: "bluecat.json",
|
||||
BluecatDNSView: "arg",
|
||||
BluecatGatewayHost: "arg",
|
||||
BluecatRootZone: "arg",
|
||||
BluecatDNSDeployType: "full-deploy",
|
||||
BluecatSkipTLSVerify: true,
|
||||
CloudflareProxied: true,
|
||||
CloudflareZonesPerPage: 20,
|
||||
@ -270,8 +274,10 @@ func TestParseFlags(t *testing.T) {
|
||||
"--bluecat-dns-configuration=arg",
|
||||
"--bluecat-config-file=bluecat.json",
|
||||
"--bluecat-dns-view=arg",
|
||||
"--bluecat-dns-server-name=arg",
|
||||
"--bluecat-gateway-host=arg",
|
||||
"--bluecat-root-zone=arg",
|
||||
"--bluecat-dns-deploy-type=full-deploy",
|
||||
"--bluecat-skip-tls-verify",
|
||||
"--cloudflare-proxied",
|
||||
"--cloudflare-zones-per-page=20",
|
||||
@ -379,6 +385,8 @@ func TestParseFlags(t *testing.T) {
|
||||
"EXTERNAL_DNS_AZURE_RESOURCE_GROUP": "arg",
|
||||
"EXTERNAL_DNS_AZURE_SUBSCRIPTION_ID": "arg",
|
||||
"EXTERNAL_DNS_BLUECAT_DNS_CONFIGURATION": "arg",
|
||||
"EXTERNAL_DNS_BLUECAT_DNS_SERVER_NAME": "arg",
|
||||
"EXTERNAL_DNS_BLUECAT_DNS_DEPLOY_TYPE": "full-deploy",
|
||||
"EXTERNAL_DNS_BLUECAT_CONFIG_FILE": "bluecat.json",
|
||||
"EXTERNAL_DNS_BLUECAT_DNS_VIEW": "arg",
|
||||
"EXTERNAL_DNS_BLUECAT_GATEWAY_HOST": "arg",
|
||||
|
@ -44,6 +44,8 @@ type bluecatConfig struct {
|
||||
GatewayUsername string `json:"gatewayUsername,omitempty"`
|
||||
GatewayPassword string `json:"gatewayPassword,omitempty"`
|
||||
DNSConfiguration string `json:"dnsConfiguration"`
|
||||
DNSServerName string `json:"dnsServerName"`
|
||||
DNSDeployType string `json:"dnsDeployType"`
|
||||
View string `json:"dnsView"`
|
||||
RootZone string `json:"rootZone"`
|
||||
SkipTLSVerify bool `json:"skipTLSVerify"`
|
||||
@ -57,6 +59,8 @@ type BluecatProvider struct {
|
||||
dryRun bool
|
||||
RootZone string
|
||||
DNSConfiguration string
|
||||
DNSServerName string
|
||||
DNSDeployType string
|
||||
View string
|
||||
gatewayClient GatewayClient
|
||||
}
|
||||
@ -76,6 +80,7 @@ type GatewayClient interface {
|
||||
getTXTRecord(name string, record *BluecatTXTRecord) error
|
||||
createTXTRecord(zone string, req *bluecatCreateTXTRecordRequest) (res interface{}, err error)
|
||||
deleteTXTRecord(name string, zone string) error
|
||||
serverFullDeploy() error
|
||||
}
|
||||
|
||||
// GatewayClientConfig defines new client on bluecat gateway
|
||||
@ -86,6 +91,7 @@ type GatewayClientConfig struct {
|
||||
DNSConfiguration string
|
||||
View string
|
||||
RootZone string
|
||||
DNSServerName string
|
||||
SkipTLSVerify bool
|
||||
}
|
||||
|
||||
@ -144,10 +150,14 @@ type bluecatCreateTXTRecordRequest struct {
|
||||
Text string `json:"txt"`
|
||||
}
|
||||
|
||||
type bluecatServerFullDeployRequest struct {
|
||||
ServerName string `json:"server_name"`
|
||||
}
|
||||
|
||||
// NewBluecatProvider creates a new Bluecat provider.
|
||||
//
|
||||
// Returns a pointer to the provider or an error if a provider could not be created.
|
||||
func NewBluecatProvider(configFile, dnsConfiguration, dnsView, gatewayHost, rootZone string, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun, skipTLSVerify bool) (*BluecatProvider, error) {
|
||||
func NewBluecatProvider(configFile, dnsConfiguration, dnsServerName, dnsDeployType, dnsView, gatewayHost, rootZone string, domainFilter endpoint.DomainFilter, zoneIDFilter provider.ZoneIDFilter, dryRun, skipTLSVerify bool) (*BluecatProvider, error) {
|
||||
cfg := bluecatConfig{}
|
||||
contents, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
@ -155,6 +165,8 @@ func NewBluecatProvider(configFile, dnsConfiguration, dnsView, gatewayHost, root
|
||||
cfg = bluecatConfig{
|
||||
GatewayHost: gatewayHost,
|
||||
DNSConfiguration: dnsConfiguration,
|
||||
DNSServerName: dnsServerName,
|
||||
DNSDeployType: dnsDeployType,
|
||||
View: dnsView,
|
||||
RootZone: rootZone,
|
||||
SkipTLSVerify: skipTLSVerify,
|
||||
@ -171,11 +183,15 @@ func NewBluecatProvider(configFile, dnsConfiguration, dnsView, gatewayHost, root
|
||||
}
|
||||
}
|
||||
|
||||
if !isValidDNSDeployType(cfg.DNSDeployType) {
|
||||
return nil, errors.Errorf("%v is not a valid deployment type", cfg.DNSDeployType)
|
||||
}
|
||||
|
||||
token, cookie, err := getBluecatGatewayToken(cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get API token from Bluecat Gateway")
|
||||
}
|
||||
gatewayClient := NewGatewayClient(cookie, token, cfg.GatewayHost, cfg.DNSConfiguration, cfg.View, cfg.RootZone, cfg.SkipTLSVerify)
|
||||
gatewayClient := NewGatewayClient(cookie, token, cfg.GatewayHost, cfg.DNSConfiguration, cfg.View, cfg.RootZone, cfg.DNSServerName, cfg.SkipTLSVerify)
|
||||
|
||||
provider := &BluecatProvider{
|
||||
domainFilter: domainFilter,
|
||||
@ -183,6 +199,8 @@ func NewBluecatProvider(configFile, dnsConfiguration, dnsView, gatewayHost, root
|
||||
dryRun: dryRun,
|
||||
gatewayClient: gatewayClient,
|
||||
DNSConfiguration: cfg.DNSConfiguration,
|
||||
DNSServerName: cfg.DNSServerName,
|
||||
DNSDeployType: cfg.DNSDeployType,
|
||||
View: cfg.View,
|
||||
RootZone: cfg.RootZone,
|
||||
}
|
||||
@ -190,7 +208,7 @@ func NewBluecatProvider(configFile, dnsConfiguration, dnsView, gatewayHost, root
|
||||
}
|
||||
|
||||
// NewGatewayClient creates and returns a new Bluecat gateway client
|
||||
func NewGatewayClient(cookie http.Cookie, token, gatewayHost, dnsConfiguration, view, rootZone string, skipTLSVerify bool) GatewayClientConfig {
|
||||
func NewGatewayClient(cookie http.Cookie, token, gatewayHost, dnsConfiguration, view, rootZone, dnsServerName string, skipTLSVerify bool) GatewayClientConfig {
|
||||
// TODO: do not handle defaulting here
|
||||
//
|
||||
// Right now the Bluecat gateway doesn't seem to have a way to get the root zone from the API. If the user
|
||||
@ -203,6 +221,7 @@ func NewGatewayClient(cookie http.Cookie, token, gatewayHost, dnsConfiguration,
|
||||
Token: token,
|
||||
Host: gatewayHost,
|
||||
DNSConfiguration: dnsConfiguration,
|
||||
DNSServerName: dnsServerName,
|
||||
View: view,
|
||||
RootZone: rootZone,
|
||||
SkipTLSVerify: skipTLSVerify,
|
||||
@ -292,8 +311,6 @@ func (p *BluecatProvider) Records(ctx context.Context) (endpoints []*endpoint.En
|
||||
}
|
||||
endpoints = append(endpoints, ep)
|
||||
}
|
||||
|
||||
// TODO: add bluecat deploy API call here
|
||||
}
|
||||
|
||||
log.Debugf("fetched %d records from Bluecat", len(endpoints))
|
||||
@ -316,6 +333,20 @@ func (p *BluecatProvider) ApplyChanges(ctx context.Context, changes *plan.Change
|
||||
p.deleteRecords(deleted)
|
||||
p.createRecords(created)
|
||||
|
||||
if p.DNSServerName != "" {
|
||||
switch p.DNSDeployType {
|
||||
case "full-deploy":
|
||||
err := p.gatewayClient.serverFullDeploy()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "no-deploy":
|
||||
log.Debug("Not executing deploy because DNSDeployType is set to 'no-deploy'")
|
||||
}
|
||||
} else {
|
||||
log.Debug("Not executing deploy because server name was not provided")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -936,6 +967,41 @@ func (c GatewayClientConfig) deleteTXTRecord(name string, zone string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c GatewayClientConfig) serverFullDeploy() error {
|
||||
log.Infof("Executing full deploy on server %s", c.DNSServerName)
|
||||
httpClient := newHTTPClient(c.SkipTLSVerify)
|
||||
url := c.Host + "/api/v1/configurations/" + c.DNSConfiguration + "/server/full_deploy/"
|
||||
requestBody := bluecatServerFullDeployRequest{
|
||||
ServerName: c.DNSServerName,
|
||||
}
|
||||
|
||||
body, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not marshal body for server full deploy")
|
||||
}
|
||||
|
||||
request, err := c.buildHTTPRequest("POST", url, bytes.NewBuffer(body))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error building http request")
|
||||
}
|
||||
|
||||
request.Header.Add("Content-Type", "application/json")
|
||||
response, err := httpClient.Do(request)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error executing full deploy")
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusCreated {
|
||||
responseBody, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to read full deploy response body")
|
||||
}
|
||||
return errors.Errorf("got HTTP response code %v, detailed message: %v", response.StatusCode, string(responseBody))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//buildHTTPRequest builds a standard http Request and adds authentication headers required by Bluecat Gateway
|
||||
func (c GatewayClientConfig) buildHTTPRequest(method, url string, body io.Reader) (*http.Request, error) {
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
@ -961,6 +1027,17 @@ func splitProperties(props string) map[string]string {
|
||||
return propMap
|
||||
}
|
||||
|
||||
// isValidDNSDeployType validates the deployment type provided by a users configuration is supported by the Bluecat Provider.
|
||||
func isValidDNSDeployType(deployType string) bool {
|
||||
validDNSDeployTypes := []string{"no-deploy", "full-deploy"}
|
||||
for _, t := range validDNSDeployTypes {
|
||||
if t == deployType {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//expandZone takes an absolute domain name such as 'example.com' and returns a zone hierarchy used by Bluecat Gateway,
|
||||
//such as '/zones/com/zones/example/zones/'
|
||||
func expandZone(zone string) string {
|
||||
|
@ -109,6 +109,9 @@ func (g mockGatewayClient) deleteTXTRecord(name string, zone string) error {
|
||||
*g.mockBluecatTXTs = nil
|
||||
return nil
|
||||
}
|
||||
func (g mockGatewayClient) serverFullDeploy() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g mockGatewayClient) buildHTTPRequest(method, url string, body io.Reader) (*http.Request, error) {
|
||||
request, _ := http.NewRequest("GET", fmt.Sprintf("%s/users", "http://some.com/api/v1"), nil)
|
||||
@ -374,11 +377,12 @@ func TestBluecatNewGatewayClient(t *testing.T) {
|
||||
testToken := "exampleToken"
|
||||
testgateWayHost := "exampleHost"
|
||||
testDNSConfiguration := "exampleDNSConfiguration"
|
||||
testDNSServer := "exampleServer"
|
||||
testView := "testView"
|
||||
testZone := "example.com"
|
||||
testVerify := true
|
||||
|
||||
client := NewGatewayClient(testCookie, testToken, testgateWayHost, testDNSConfiguration, testView, testZone, testVerify)
|
||||
client := NewGatewayClient(testCookie, testToken, testgateWayHost, testDNSConfiguration, testView, testZone, testDNSServer, testVerify)
|
||||
|
||||
if client.Cookie.Value != testCookie.Value || client.Cookie.Name != testCookie.Name || client.Token != testToken || client.Host != testgateWayHost || client.DNSConfiguration != testDNSConfiguration || client.View != testView || client.RootZone != testZone || client.SkipTLSVerify != testVerify {
|
||||
t.Fatal("Client values dont match")
|
||||
@ -475,6 +479,21 @@ func TestBluecatRecordset(t *testing.T) {
|
||||
assert.Equal(t, cnameActual.res, cnameExpected.res)
|
||||
}
|
||||
|
||||
func TestValidDeployTypes(t *testing.T) {
|
||||
validTypes := []string{"no-deploy", "full-deploy"}
|
||||
invalidTypes := []string{"anything-else"}
|
||||
for _, i := range validTypes {
|
||||
if !isValidDNSDeployType(i) {
|
||||
t.Fatalf("%s should be a valid deploy type", i)
|
||||
}
|
||||
}
|
||||
for _, i := range invalidTypes {
|
||||
if isValidDNSDeployType(i) {
|
||||
t.Fatalf("%s should be a invalid deploy type", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateEndpoints(t *testing.T, actual, expected []*endpoint.Endpoint) {
|
||||
assert.True(t, testutils.SameEndpoints(actual, expected), "actual and expected endpoints don't match. %s:%s", actual, expected)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user