From f3445270ca7ee9405e3b60a69393ef2d253b55d0 Mon Sep 17 00:00:00 2001 From: Robert Volkmann Date: Mon, 22 Apr 2024 11:16:08 +0200 Subject: [PATCH 1/3] Activate PowerDNS --- cmd/compound/main.go | 1 + cmd/dedicated/main.go | 1 + go.mod | 1 + go.sum | 5 + .../powerdns/controller/controller.go | 16 ++ pkg/controller/provider/powerdns/execution.go | 100 +++++++++ pkg/controller/provider/powerdns/factory.go | 25 +++ pkg/controller/provider/powerdns/handler.go | 201 ++++++++++++++++++ 8 files changed, 350 insertions(+) create mode 100644 pkg/controller/provider/powerdns/controller/controller.go create mode 100644 pkg/controller/provider/powerdns/execution.go create mode 100644 pkg/controller/provider/powerdns/factory.go create mode 100644 pkg/controller/provider/powerdns/handler.go diff --git a/cmd/compound/main.go b/cmd/compound/main.go index bd225190..406d61db 100644 --- a/cmd/compound/main.go +++ b/cmd/compound/main.go @@ -36,6 +36,7 @@ import ( _ "github.com/gardener/external-dns-management/pkg/controller/provider/infoblox" _ "github.com/gardener/external-dns-management/pkg/controller/provider/netlify" _ "github.com/gardener/external-dns-management/pkg/controller/provider/openstack" + _ "github.com/gardener/external-dns-management/pkg/controller/provider/powerdns" _ "github.com/gardener/external-dns-management/pkg/controller/provider/remote" _ "github.com/gardener/external-dns-management/pkg/controller/provider/rfc2136" _ "github.com/gardener/external-dns-management/pkg/controller/remoteaccesscertificates" diff --git a/cmd/dedicated/main.go b/cmd/dedicated/main.go index 68b7cc66..1c117774 100644 --- a/cmd/dedicated/main.go +++ b/cmd/dedicated/main.go @@ -30,6 +30,7 @@ import ( _ "github.com/gardener/external-dns-management/pkg/controller/provider/infoblox/controller" _ "github.com/gardener/external-dns-management/pkg/controller/provider/netlify/controller" _ "github.com/gardener/external-dns-management/pkg/controller/provider/openstack/controller" + _ "github.com/gardener/external-dns-management/pkg/controller/provider/powerdns/controller" _ "github.com/gardener/external-dns-management/pkg/controller/provider/remote/controller" _ "github.com/gardener/external-dns-management/pkg/controller/provider/rfc2136/controller" _ "github.com/gardener/external-dns-management/pkg/controller/remoteaccesscertificates" diff --git a/go.mod b/go.mod index 225e0d30..7fa40405 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/gophercloud/gophercloud v0.24.0 github.com/gophercloud/utils v0.0.0-20220307143606-8e7800759d16 github.com/infobloxopen/infoblox-go-client/v2 v2.1.0 + github.com/joeig/go-powerdns/v3 v3.10.0 github.com/miekg/dns v1.1.51 github.com/netlify/open-api v1.1.0 github.com/onsi/ginkgo/v2 v2.13.0 diff --git a/go.sum b/go.sum index 65eb2c0d..9352204c 100644 --- a/go.sum +++ b/go.sum @@ -367,12 +367,16 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ironcore-dev/vgopath v0.1.3 h1:/g3QJ29VrUkYEy52kcUhtvQ3mxfbMIlI1uvEbmt6S4E= github.com/ironcore-dev/vgopath v0.1.3/go.mod h1:edfsCmU2M4r2N+t4RebSluq//tF3vzogyiDDhcf7MXs= +github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= +github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joeig/go-powerdns/v3 v3.10.0 h1:pUhC/8kdDJW4Y7/J/QawPXWlp0WcNrynABk0yX0WmRk= +github.com/joeig/go-powerdns/v3 v3.10.0/go.mod h1:SA9nmMT7kJr4vgSFTlYLMbomSwPxydacVWTPqSUoPFA= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -431,6 +435,7 @@ github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/miekg/dns v1.1.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo= github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= diff --git a/pkg/controller/provider/powerdns/controller/controller.go b/pkg/controller/provider/powerdns/controller/controller.go new file mode 100644 index 00000000..6e47c4ad --- /dev/null +++ b/pkg/controller/provider/powerdns/controller/controller.go @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package controller + +import ( + "github.com/gardener/external-dns-management/pkg/controller/provider/powerdns" + "github.com/gardener/external-dns-management/pkg/dns/provider" +) + +func init() { + provider.DNSController("", powerdns.Factory). + FinalizerDomain("dns.gardener.cloud"). + MustRegister(provider.CONTROLLER_GROUP_DNS_CONTROLLERS) +} diff --git a/pkg/controller/provider/powerdns/execution.go b/pkg/controller/provider/powerdns/execution.go new file mode 100644 index 00000000..1065a024 --- /dev/null +++ b/pkg/controller/provider/powerdns/execution.go @@ -0,0 +1,100 @@ +// SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package powerdns + +import ( + "fmt" + + "github.com/gardener/controller-manager-library/pkg/logger" + "github.com/joeig/go-powerdns/v3" + + "github.com/gardener/external-dns-management/pkg/dns" + "github.com/gardener/external-dns-management/pkg/dns/provider" +) + +type RecordSet struct { + Name string + RecordType powerdns.RRType + TTL uint32 + Content []string +} + +type Execution struct { + logger.LogContext + handler *Handler + zone provider.DNSHostedZone +} + +func NewExecution(logger logger.LogContext, h *Handler, zone provider.DNSHostedZone) *Execution { + return &Execution{LogContext: logger, handler: h, zone: zone} +} + +func (exec *Execution) buildRecordSet(req *provider.ChangeRequest) (*RecordSet, error) { + var dnsset *dns.DNSSet + + switch req.Action { + case provider.R_CREATE, provider.R_UPDATE: + dnsset = req.Addition + case provider.R_DELETE: + dnsset = req.Deletion + } + + name, rset := dns.MapToProvider(req.Type, dnsset, exec.zone.Domain()) + + if name.SetIdentifier != "" || dnsset.RoutingPolicy != nil { + return nil, fmt.Errorf("routing policies not supported for " + TYPE_CODE) + } + + if name.DNSName == "" || len(rset.Records) == 0 { + return nil, nil + } + + exec.Infof("Desired %s: %s record set %s[%s] with TTL %d: %s", req.Action, rset.Type, name.DNSName, exec.zone.Id(), rset.TTL, rset.RecordString()) + + recordSet := RecordSet{ + Name: name.DNSName, + RecordType: powerdns.RRType(rset.Type), + } + + switch req.Action { + case provider.R_CREATE, provider.R_UPDATE: + var content []string + for _, record := range rset.Records { + content = append(content, record.Value) + } + + recordSet.Content = content + recordSet.TTL = uint32(rset.TTL) + } + + return &recordSet, nil +} + +func (exec *Execution) apply(action string, rset *RecordSet, metrics provider.Metrics) error { + var err error + switch action { + case provider.R_CREATE, provider.R_UPDATE: + err = exec.update(rset, metrics) + case provider.R_DELETE: + err = exec.delete(rset, metrics) + } + return err +} + +func (exec *Execution) update(rset *RecordSet, metrics provider.Metrics) error { + exec.handler.config.RateLimiter.Accept() + zoneID := exec.zone.Id().ID + err := exec.handler.powerdns.Records.Change(exec.handler.ctx, zoneID, rset.Name, rset.RecordType, rset.TTL, rset.Content) + metrics.AddZoneRequests(zoneID, provider.M_UPDATERECORDS, 1) + return err +} + +func (exec *Execution) delete(rset *RecordSet, metrics provider.Metrics) error { + exec.handler.config.RateLimiter.Accept() + zoneID := exec.zone.Id().ID + err := exec.handler.powerdns.Records.Delete(exec.handler.ctx, zoneID, rset.Name, rset.RecordType) + metrics.AddZoneRequests(zoneID, provider.M_DELETERECORDS, 1) + return err +} diff --git a/pkg/controller/provider/powerdns/factory.go b/pkg/controller/provider/powerdns/factory.go new file mode 100644 index 00000000..94156dae --- /dev/null +++ b/pkg/controller/provider/powerdns/factory.go @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package powerdns + +import ( + "github.com/gardener/external-dns-management/pkg/controller/provider/compound" + "github.com/gardener/external-dns-management/pkg/dns/provider" +) + +const TYPE_CODE = "powerdns" + +var rateLimiterDefaults = provider.RateLimiterOptions{ + Enabled: true, + QPS: 50, + Burst: 10, +} + +var Factory = provider.NewDNSHandlerFactory(TYPE_CODE, NewHandler). + SetGenericFactoryOptionDefaults(provider.GenericFactoryOptionDefaults.SetRateLimiterOptions(rateLimiterDefaults)) + +func init() { + compound.MustRegister(Factory) +} diff --git a/pkg/controller/provider/powerdns/handler.go b/pkg/controller/provider/powerdns/handler.go new file mode 100644 index 00000000..9573957a --- /dev/null +++ b/pkg/controller/provider/powerdns/handler.go @@ -0,0 +1,201 @@ +// SPDX-FileCopyrightText: SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package powerdns + +import ( + "context" + "crypto/tls" + "crypto/x509" + "fmt" + "net/http" + + "github.com/gardener/controller-manager-library/pkg/logger" + miekgdns "github.com/miekg/dns" + + "github.com/joeig/go-powerdns/v3" + + "github.com/gardener/external-dns-management/pkg/dns" + "github.com/gardener/external-dns-management/pkg/dns/provider" +) + +type Handler struct { + provider.DefaultDNSHandler + config provider.DNSHandlerConfig + cache provider.ZoneCache + ctx context.Context + + powerdns *powerdns.Client +} + +var _ provider.DNSHandler = &Handler{} + +// tsigAlgs are the supported TSIG algorithms +var tsigAlgs = []string{miekgdns.HmacSHA1, miekgdns.HmacSHA224, miekgdns.HmacSHA256, miekgdns.HmacSHA384, miekgdns.HmacSHA512} + +func NewHandler(c *provider.DNSHandlerConfig) (provider.DNSHandler, error) { + h := &Handler{ + DefaultDNSHandler: provider.NewDefaultDNSHandler(TYPE_CODE), + config: *c, + } + + h.ctx = c.Context + + server, err := c.GetRequiredProperty("Server", "server") + if err != nil { + return nil, err + } + + apiKey, err := c.GetRequiredProperty("ApiKey", "apiKey") + if err != nil { + return nil, err + } + + virtualHost := c.GetProperty("VirtualHost", "virtualHost") + + insecureSkipVerify, err := c.GetDefaultedBoolProperty("InsecureSkipVerify", false, "insecureSkipVerify") + if err != nil { + return nil, err + } + + trustedCaCert := c.GetProperty("TrustedCaCert", "trustedCaCert") + + headers := map[string]string{"X-API-Key": apiKey} + httpClient := newHttpClient(insecureSkipVerify, trustedCaCert) + + h.powerdns = powerdns.NewClient(server, virtualHost, headers, httpClient) + + h.cache, err = c.ZoneCacheFactory.CreateZoneCache(provider.CacheZoneState, c.Metrics, h.getZones, h.getZoneState) + if err != nil { + return nil, err + } + + return h, nil +} + +func newHttpClient(insecureSkipVerify bool, trustedCaCert string) *http.Client { + httpClient := http.DefaultClient + + if insecureSkipVerify { + httpClient.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + } + + if trustedCaCert != "" { + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM([]byte(trustedCaCert)) + httpClient.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{ + RootCAs: caCertPool, + InsecureSkipVerify: false, + MinVersion: tls.VersionTLS12, + }, + } + } + + return httpClient +} + +func (h *Handler) Release() { + h.cache.Release() +} + +func (h *Handler) GetZones() (provider.DNSHostedZones, error) { + return h.cache.GetZones() +} + +func (h *Handler) getZones(_ provider.ZoneCache) (provider.DNSHostedZones, error) { + hostedZones := provider.DNSHostedZones{} + zones, err := h.powerdns.Zones.List(h.ctx) + if err != nil { + return nil, err + } + for _, z := range zones { + id := powerdns.StringValue(z.ID) + domain := dns.NormalizeHostname(powerdns.StringValue(z.Name)) + hostedZone := provider.NewDNSHostedZone(h.ProviderType(), id, domain, id, false) + hostedZones = append(hostedZones, hostedZone) + } + h.config.Metrics.AddGenericRequests(provider.M_LISTZONES, 1) + return hostedZones, nil +} + +func (h *Handler) GetZoneState(zone provider.DNSHostedZone) (provider.DNSZoneState, error) { + return h.cache.GetZoneState(zone) +} + +func (h *Handler) getZoneState(zone provider.DNSHostedZone, _ provider.ZoneCache) (provider.DNSZoneState, error) { + dnssets := dns.DNSSets{} + + h.config.RateLimiter.Accept() + + state, err := h.powerdns.Zones.Get(h.ctx, zone.Id().ID) + if err != nil { + return nil, err + } + + for _, rrset := range state.RRsets { + h.config.Metrics.AddZoneRequests(zone.Id().ID, provider.M_LISTRECORDS, 1) + if rrset.Type == nil { + h.config.Logger.Warnf("Missing type for RRSet %s from Zone %s", powerdns.StringValue(rrset.Name), zone.Id().ID) + continue + } + rs := dns.NewRecordSet(powerdns.StringValue((*string)(rrset.Type)), int64(powerdns.Uint32Value(rrset.TTL)), nil) + for _, rr := range rrset.Records { + rs.Add(&dns.Record{Value: powerdns.StringValue(rr.Content)}) + } + dnssets.AddRecordSetFromProvider(powerdns.StringValue(rrset.Name), rs) + } + return provider.NewDNSZoneState(dnssets), nil +} + +func (h *Handler) ReportZoneStateConflict(zone provider.DNSHostedZone, err error) bool { + return h.cache.ReportZoneStateConflict(zone, err) +} + +func (h *Handler) ExecuteRequests(logger logger.LogContext, zone provider.DNSHostedZone, state provider.DNSZoneState, reqs []*provider.ChangeRequest) error { + err := h.executeRequests(logger, zone, state, reqs) + h.cache.ApplyRequests(logger, err, zone, reqs) + return err +} + +func (h *Handler) executeRequests(logger logger.LogContext, zone provider.DNSHostedZone, _ provider.DNSZoneState, reqs []*provider.ChangeRequest) error { + exec := NewExecution(logger, h, zone) + + var succeeded, failed int + for _, req := range reqs { + rset, err := exec.buildRecordSet(req) + if err != nil { + if req.Done != nil { + req.Done.SetInvalid(err) + } + continue + } + + err = exec.apply(req.Action, rset, h.config.Metrics) + if err != nil { + failed++ + logger.Infof("Apply failed with %s", err.Error()) + if req.Done != nil { + req.Done.Failed(err) + } + } else { + succeeded++ + if req.Done != nil { + req.Done.Succeeded() + } + } + } + + if succeeded > 0 { + logger.Infof("Succeeded updates for records in apiKey %s: %d", zone.Id(), succeeded) + } + if failed > 0 { + logger.Infof("Failed updates for records in apiKey %s: %d", zone.Id(), failed) + return fmt.Errorf("%d changes failed", failed) + } + + return nil +} From b8be230e2f6f74fbda7b9230de3c7ac01340a629 Mon Sep 17 00:00:00 2001 From: Robert Volkmann Date: Wed, 7 Aug 2024 06:48:57 +0200 Subject: [PATCH 2/3] DROP: Remove dependabot --- .github/dependabot.yaml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .github/dependabot.yaml diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml deleted file mode 100644 index 40fa5f47..00000000 --- a/.github/dependabot.yaml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -updates: -# Create PRs for golang version updates -- package-ecosystem: docker - directory: /build - schedule: - interval: daily From daa0db40672355a321f379c8d1a0f5b12c877fec Mon Sep 17 00:00:00 2001 From: Robert Volkmann Date: Wed, 7 Aug 2024 06:49:07 +0200 Subject: [PATCH 3/3] DROP: Build and push image --- .github/workflows/docker.yaml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/docker.yaml diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 00000000..28494de8 --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,29 @@ +--- +name: Docker Build Action +on: + push: + +jobs: + build: + name: Docker Build + runs-on: ubuntu-latest + + steps: + - name: Log in to the container registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ secrets.DOCKER_REGISTRY_USER }} + password: ${{ secrets.DOCKER_REGISTRY_TOKEN }} + + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Build and push image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ghcr.io/metal-stack/dns-controller-manager:powerdns