From d9fdb226b266f8d6183d37e1200c82ac271e0e3d Mon Sep 17 00:00:00 2001 From: Stephen Soltesz Date: Tue, 10 Sep 2024 13:43:16 -0400 Subject: [PATCH] Add RegisterDNS to adminx.Org Setup (#45) * Add RegisterDNS to Org.Setup * Add DNS for NewOrg setup --- cmd/orgadm/main.go | 15 +++++-- internal/adminx/org.go | 35 ++++++++++++++- internal/adminx/org_test.go | 86 ++++++++++++++++++++++++++++++++++++- 3 files changed, 131 insertions(+), 5 deletions(-) diff --git a/cmd/orgadm/main.go b/cmd/orgadm/main.go index 1e85910..aca8964 100644 --- a/cmd/orgadm/main.go +++ b/cmd/orgadm/main.go @@ -9,8 +9,12 @@ import ( "github.com/m-lab/autojoin/internal/adminx" "github.com/m-lab/autojoin/internal/adminx/crmiface" "github.com/m-lab/autojoin/internal/adminx/iamiface" + "github.com/m-lab/autojoin/internal/dnsname" + "github.com/m-lab/autojoin/internal/dnsx" + "github.com/m-lab/autojoin/internal/dnsx/dnsiface" "github.com/m-lab/go/rtx" "google.golang.org/api/cloudresourcemanager/v1" + "google.golang.org/api/dns/v1" iam "google.golang.org/api/iam/v1" ) @@ -38,13 +42,18 @@ func main() { defer sc.Close() ic, err := iam.NewService(ctx) rtx.Must(err, "failed to create iam service client") - log.Println("Creating SAM & KEYS") nn := adminx.NewNamer(project) crm, err := cloudresourcemanager.NewService(ctx) rtx.Must(err, "failed to allocate new cloud resource manager client") sa := adminx.NewServiceAccountsManager(iamiface.NewIAM(ic), nn) rtx.Must(err, "failed to create sam") sm := adminx.NewSecretManager(sc, nn, sa) - o := adminx.NewOrg(project, crmiface.NewCRM(project, crm), sa, sm) - o.Setup(ctx, org) + ds, err := dns.NewService(ctx) + rtx.Must(err, "failed to create new dns service") + d := dnsx.NewManager(dnsiface.NewCloudDNSService(ds), project, dnsname.ProjectZone(project)) + + o := adminx.NewOrg(project, crmiface.NewCRM(project, crm), sa, sm, d) + err = o.Setup(ctx, org) + rtx.Must(err, "failed to set up new organization: "+org) + log.Println("okay") } diff --git a/internal/adminx/org.go b/internal/adminx/org.go index 00ba818..ac5fe88 100644 --- a/internal/adminx/org.go +++ b/internal/adminx/org.go @@ -5,9 +5,11 @@ import ( "fmt" "log" + "github.com/m-lab/autojoin/internal/dnsname" "golang.org/x/exp/slices" "google.golang.org/api/cloudresourcemanager/v1" + "google.golang.org/api/dns/v1" "google.golang.org/api/iam/v1" ) @@ -20,6 +22,12 @@ var ( ` resource.name.startsWith("projects/_/buckets/staging-%s")`) ) +// DNS is a simplified interface to the Google Cloud DNS API. +type DNS interface { + RegisterZone(ctx context.Context, zone *dns.ManagedZone) (*dns.ManagedZone, error) + RegisterZoneSplit(ctx context.Context, zone *dns.ManagedZone) (*dns.ResourceRecordSet, error) +} + // CRM is a simplified interface to the Google Cloud Resource Manager API. type CRM interface { GetIamPolicy(ctx context.Context, req *cloudresourcemanager.GetIamPolicyRequest) (*cloudresourcemanager.Policy, error) @@ -32,15 +40,17 @@ type Org struct { crm CRM sam *ServiceAccountsManager sm *SecretManager + dns DNS } // NewOrg creates a new Org instance for setting up a new organization. -func NewOrg(project string, crm CRM, sam *ServiceAccountsManager, sm *SecretManager) *Org { +func NewOrg(project string, crm CRM, sam *ServiceAccountsManager, sm *SecretManager, dns DNS) *Org { return &Org{ Project: project, crm: crm, sam: sam, sm: sm, + dns: dns, } } @@ -60,6 +70,29 @@ func (o *Org) Setup(ctx context.Context, org string) error { if err != nil { return err } + // Create DNS zone and zone split. + return o.RegisterDNS(ctx, org) +} + +// RegisterDNS creates the organization zone and the zone split within the project zone. +func (o *Org) RegisterDNS(ctx context.Context, org string) error { + zone, err := o.dns.RegisterZone(ctx, &dns.ManagedZone{ + Description: "Autojoin registered nodes from org: " + org, + Name: dnsname.OrgZone(org, o.Project), + DnsName: dnsname.OrgDNS(org, o.Project), + DnssecConfig: &dns.ManagedZoneDnsSecConfig{ + State: "on", + }, + }) + if err != nil { + log.Println("failed to register zone:", dnsname.OrgZone(org, o.Project), err) + return err + } + _, err = o.dns.RegisterZoneSplit(ctx, zone) + if err != nil { + log.Println("failed to register zone split:", dnsname.OrgZone(org, o.Project), err) + return err + } return nil } diff --git a/internal/adminx/org_test.go b/internal/adminx/org_test.go index b2983af..cfc1285 100644 --- a/internal/adminx/org_test.go +++ b/internal/adminx/org_test.go @@ -8,7 +8,9 @@ import ( "testing" "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb" + "github.com/m-lab/autojoin/internal/dnsname" "google.golang.org/api/cloudresourcemanager/v1" + "google.golang.org/api/dns/v1" "google.golang.org/api/iam/v1" ) @@ -33,6 +35,21 @@ func (f *fakeCRM) SetIamPolicy(ctx context.Context, req *cloudresourcemanager.Se return f.setPolicyErr } +type fakeDNS struct { + regZone *dns.ManagedZone + regZoneErr error + regSplit *dns.ResourceRecordSet + regSplitErr error +} + +func (f *fakeDNS) RegisterZone(ctx context.Context, zone *dns.ManagedZone) (*dns.ManagedZone, error) { + return f.regZone, f.regZoneErr +} + +func (f *fakeDNS) RegisterZoneSplit(ctx context.Context, zone *dns.ManagedZone) (*dns.ResourceRecordSet, error) { + return f.regSplit, f.regSplitErr +} + func TestOrg_Setup(t *testing.T) { tests := []struct { name string @@ -40,6 +57,7 @@ func TestOrg_Setup(t *testing.T) { crm CRM sam IAMService smc SecretManagerClient + dns DNS org string wantErr bool }{ @@ -63,6 +81,66 @@ func TestOrg_Setup(t *testing.T) { smc: &fakeSMC{ getSec: &secretmanagerpb.Secret{Name: "okay"}, }, + dns: &fakeDNS{ + regZone: &dns.ManagedZone{ + Name: dnsname.OrgZone("foo", "mlab-foo"), + DnsName: dnsname.OrgDNS("foo", "mlab-foo"), + }, + }, + }, + { + name: "error-register-zone", + crm: &fakeCRM{ + getPolicy: &cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Members: []string{"foo"}, + Role: "roles/fooWriter", + }, + }, + }, + }, + sam: &fakeIAMService{ + getAcct: &iam.ServiceAccount{ + Name: "foo", + }, + }, + smc: &fakeSMC{ + getSec: &secretmanagerpb.Secret{Name: "okay"}, + }, + dns: &fakeDNS{ + regZoneErr: fmt.Errorf("fake zone registration error"), + }, + wantErr: true, + }, + { + name: "error-register-split", + crm: &fakeCRM{ + getPolicy: &cloudresourcemanager.Policy{ + Bindings: []*cloudresourcemanager.Binding{ + { + Members: []string{"foo"}, + Role: "roles/fooWriter", + }, + }, + }, + }, + sam: &fakeIAMService{ + getAcct: &iam.ServiceAccount{ + Name: "foo", + }, + }, + smc: &fakeSMC{ + getSec: &secretmanagerpb.Secret{Name: "okay"}, + }, + dns: &fakeDNS{ + regZone: &dns.ManagedZone{ + Name: dnsname.OrgZone("foo", "mlab-foo"), + DnsName: dnsname.OrgDNS("foo", "mlab-foo"), + }, + regSplitErr: fmt.Errorf("fake split register error"), + }, + wantErr: true, }, { name: "success-equal-bindings", @@ -92,6 +170,12 @@ func TestOrg_Setup(t *testing.T) { smc: &fakeSMC{ getSec: &secretmanagerpb.Secret{Name: "okay"}, }, + dns: &fakeDNS{ + regZone: &dns.ManagedZone{ + Name: dnsname.OrgZone("foo", "mlab-foo"), + DnsName: dnsname.OrgDNS("foo", "mlab-foo"), + }, + }, }, { name: "error-create-service-account", @@ -160,7 +244,7 @@ func TestOrg_Setup(t *testing.T) { n := NewNamer("mlab-foo") sam := NewServiceAccountsManager(tt.sam, n) sm := NewSecretManager(tt.smc, n, sam) - o := NewOrg("mlab-foo", tt.crm, sam, sm) + o := NewOrg("mlab-foo", tt.crm, sam, sm, tt.dns) if err := o.Setup(context.Background(), "foobar"); (err != nil) != tt.wantErr { t.Errorf("Org.Setup() error = %v, wantErr %v", err, tt.wantErr) }