76 lines
2.3 KiB
Go
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
|
|
}
|