Files
mailgoserver/internal/dnspublish/gcloud.go
T

76 lines
2.3 KiB
Go

package dnspublish
import (
"context"
"fmt"
"strings"
"golang.org/x/oauth2/google"
dnsv1 "google.golang.org/api/dns/v1"
"google.golang.org/api/option"
)
// GCloudAPIBase is overridden by tests to point the client at a fake httptest.Server
// instead of the real Cloud DNS API.
var GCloudAPIBase string
// setTXTRecordGCloud resolves creds.ZoneName to its Cloud DNS managed zone (by listing
// every managed zone in the project and matching DnsName — Cloud DNS has no
// lookup-by-DNS-name call), then applies a Change: Cloud DNS has no direct upsert, so
// an existing record set with the same name/type is deleted in the same atomic change
// that adds the new one.
func setTXTRecordGCloud(creds Credentials, recordFQDN, value string) error {
ctx := context.Background()
jwtCfg, err := google.JWTConfigFromJSON([]byte(creds.GCloudServiceAccountJSON), dnsv1.NdevClouddnsReadwriteScope)
if err != nil {
return fmt.Errorf("gcloud: parse service account JSON: %w", err)
}
opts := []option.ClientOption{option.WithHTTPClient(jwtCfg.Client(ctx))}
if GCloudAPIBase != "" {
opts = append(opts, option.WithEndpoint(GCloudAPIBase))
}
svc, err := dnsv1.NewService(ctx, opts...)
if err != nil {
return fmt.Errorf("gcloud: create DNS client: %w", err)
}
zoneName := creds.ZoneName
if !strings.HasSuffix(zoneName, ".") {
zoneName += "."
}
zones, err := svc.ManagedZones.List(creds.GCloudProject).Do()
if err != nil {
return fmt.Errorf("gcloud: list managed zones: %w", err)
}
var zoneID string
for _, z := range zones.ManagedZones {
if z.DnsName == zoneName {
zoneID = z.Name
break
}
}
if zoneID == "" {
return fmt.Errorf("gcloud: no managed zone found for %q", creds.ZoneName)
}
fqdn := recordFQDN
if !strings.HasSuffix(fqdn, ".") {
fqdn += "."
}
existing, err := svc.ResourceRecordSets.List(creds.GCloudProject, zoneID).Name(fqdn).Type("TXT").Do()
if err != nil {
return fmt.Errorf("gcloud: list existing record: %w", err)
}
change := &dnsv1.Change{
Additions: []*dnsv1.ResourceRecordSet{{
Name: fqdn, Type: "TXT", Ttl: txtTTL, Rrdatas: []string{value},
}},
Deletions: existing.Rrsets,
}
if _, err := svc.Changes.Create(creds.GCloudProject, zoneID, change).Do(); err != nil {
return fmt.Errorf("gcloud: apply change: %w", err)
}
return nil
}