-
Notifications
You must be signed in to change notification settings - Fork 2
fix(gcp): give every data center its own DNS records #628
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
f7da12c
fix(gcp): give every data center its own DNS records
NJona 94c6bf9
chore(docs): Auto-update docs and licenses
NJona 7190264
refactor(gcp): move DNS record handling into dns.go and drop stray sc…
NJona 14a9013
fix(gcp): satisfy revive on the moved DNS code
NJona 143f727
chore(docs): Auto-update docs and licenses
NJona File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| // Copyright (c) Codesphere Inc. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package gcp | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| "github.com/codesphere-cloud/oms/internal/bootstrap/datacenter" | ||
| "google.golang.org/api/dns/v1" | ||
| ) | ||
|
|
||
| // DNSRecordName identifies a DNS record set that OMS manages. | ||
| type DNSRecordName struct { | ||
| Name string `json:"name"` | ||
| Rtype string `json:"rtype"` | ||
| } | ||
|
|
||
| // GetDNSRecordNames returns the DNS record names a single-data-center bootstrap creates for a | ||
| // given base domain. It is the fallback for infra files written before multi-DC support, which | ||
| // do not record the created records. | ||
| func GetDNSRecordNames(baseDomain string) []DNSRecordName { | ||
| return []DNSRecordName{ | ||
| {fmt.Sprintf("cs.%s.", baseDomain), "A"}, | ||
| {fmt.Sprintf("*.cs.%s.", baseDomain), "A"}, | ||
| {fmt.Sprintf("ws.%s.", baseDomain), "A"}, | ||
| {fmt.Sprintf("*.ws.%s.", baseDomain), "A"}, | ||
| {fmt.Sprintf("*.ssh.cs.%s.", baseDomain), "A"}, | ||
| } | ||
| } | ||
|
|
||
| // DataCenterDNSRecordNames returns every DNS record OMS creates for the given data center | ||
| // layout: the shared platform gateway names plus each data center's workspace and SSH names. | ||
| func DataCenterDNSRecordNames(baseDomain string, dcs []*datacenter.DataCenter) []DNSRecordName { | ||
| records := []DNSRecordName{ | ||
| {fmt.Sprintf("cs.%s.", baseDomain), "A"}, | ||
| {fmt.Sprintf("*.cs.%s.", baseDomain), "A"}, | ||
| } | ||
| for _, dc := range dcs { | ||
| records = append(records, | ||
| DNSRecordName{fmt.Sprintf("%s.", dc.WorkspaceHostingBaseDomain), "A"}, | ||
| DNSRecordName{fmt.Sprintf("*.%s.", dc.WorkspaceHostingBaseDomain), "A"}, | ||
| DNSRecordName{fmt.Sprintf("*.%s.", dc.SSHBaseDomain), "A"}, | ||
| ) | ||
| if len(dcs) > 1 { | ||
| records = append(records, | ||
| DNSRecordName{fmt.Sprintf("%s.", dc.PlatformDomain(baseDomain)), "A"}, | ||
| DNSRecordName{fmt.Sprintf("*.%s.", dc.PlatformDomain(baseDomain)), "A"}, | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| return records | ||
| } | ||
|
|
||
| // ensureDNSPermissions grants the cloud-controller service account DNS admin on the project that | ||
| // hosts the managed zone, which is the DNS project when one is configured. | ||
| func (b *GCPBootstrapper) ensureDNSPermissions() error { | ||
| dnsProject := b.Env.DNSProjectID | ||
| if b.Env.DNSProjectID == "" { | ||
| dnsProject = b.Env.ProjectID | ||
| } | ||
|
|
||
| err := b.ensureIAMRoleWithRetry(dnsProject, "cloud-controller", b.Env.ProjectID, []string{"roles/dns.admin"}) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // EnsureDNSRecords creates the managed zone and the A records for the platform, every data | ||
| // center's workspace and SSH names and, with more than one data center, each one's own platform | ||
| // host. It records the created names in the environment so cleanup deletes exactly those. | ||
| func (b *GCPBootstrapper) EnsureDNSRecords() error { | ||
| if err := b.ensureDataCenters(); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| gcpProject := b.Env.DNSProjectID | ||
| if b.Env.DNSProjectID == "" { | ||
| gcpProject = b.Env.ProjectID | ||
| } | ||
|
|
||
| zoneName := b.Env.DNSZoneName | ||
|
|
||
| err := b.GCPClient.EnsureDNSManagedZone(gcpProject, zoneName, b.Env.BaseDomain+".", "Codesphere DNS zone") | ||
| if err != nil { | ||
| return fmt.Errorf("failed to ensure DNS managed zone: %w", err) | ||
| } | ||
|
|
||
| // The platform is served from one domain shared by all data centers, pointing at the | ||
| // primary data center's gateway. | ||
| records := []*dns.ResourceRecordSet{ | ||
| dnsARecord(fmt.Sprintf("cs.%s.", b.Env.BaseDomain), b.primaryDC().GatewayIP), | ||
| dnsARecord(fmt.Sprintf("*.cs.%s.", b.Env.BaseDomain), b.primaryDC().GatewayIP), | ||
| } | ||
| // Workspaces and their SSH endpoints resolve per data center, so each one gets its own | ||
| // names pointing at its own public gateway and SSH proxy. | ||
| for _, dc := range b.Env.DataCenters { | ||
| records = append(records, | ||
| dnsARecord(fmt.Sprintf("%s.", dc.WorkspaceHostingBaseDomain), dc.PublicGatewayIP), | ||
| dnsARecord(fmt.Sprintf("*.%s.", dc.WorkspaceHostingBaseDomain), dc.PublicGatewayIP), | ||
| dnsARecord(fmt.Sprintf("*.%s.", dc.SSHBaseDomain), dc.SSHProxyIP), | ||
| ) | ||
| // The platform calls each data center's own services at <dc-id>.cs.<base-domain>, which | ||
| // the wildcard above would send to the primary data center's gateway. A single data | ||
| // center is that primary, so it needs no record of its own. | ||
| if len(b.Env.DataCenters) > 1 { | ||
| platformDomain := dc.PlatformDomain(b.Env.BaseDomain) | ||
| records = append(records, | ||
| dnsARecord(fmt.Sprintf("%s.", platformDomain), dc.GatewayIP), | ||
| dnsARecord(fmt.Sprintf("*.%s.", platformDomain), dc.GatewayIP), | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| err = b.GCPClient.EnsureDNSRecordSets(gcpProject, zoneName, records) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to ensure DNS record sets: %w", err) | ||
| } | ||
|
|
||
| // Record what was created so cleanup deletes exactly these records instead of recomputing | ||
| // the list from the base domain. | ||
| b.Env.DNSRecords = DataCenterDNSRecordNames(b.Env.BaseDomain, b.Env.DataCenters) | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // dnsARecord builds a short-TTL A record set, as used during initial setup. | ||
| func dnsARecord(name, ip string) *dns.ResourceRecordSet { | ||
| return &dns.ResourceRecordSet{ | ||
| Name: name, | ||
| Type: "A", | ||
| Ttl: 300, | ||
| Rrdatas: []string{ip}, | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| // Copyright (c) Codesphere Inc. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package gcp_test | ||
|
|
||
| import ( | ||
| . "github.com/onsi/ginkgo/v2" | ||
| . "github.com/onsi/gomega" | ||
|
|
||
| "github.com/codesphere-cloud/oms/internal/bootstrap/gcp" | ||
| ) | ||
|
|
||
| var _ = Describe("DataCenterDNSRecordNames", func() { | ||
| It("returns the single-DC records for one data center", func() { | ||
| dcs := gcp.BuildDataCenters(&gcp.CodesphereEnvironment{BaseDomain: "example.com"}) | ||
|
|
||
| Expect(gcp.DataCenterDNSRecordNames("example.com", dcs)).To(ConsistOf(gcp.GetDNSRecordNames("example.com"))) | ||
| }) | ||
|
|
||
| It("shares the platform names and scopes the workspace names per data center", func() { | ||
| dcs := gcp.BuildDataCenters(&gcp.CodesphereEnvironment{MultiDC: true, BaseDomain: "example.com"}) | ||
|
|
||
| Expect(gcp.DataCenterDNSRecordNames("example.com", dcs)).To(Equal([]gcp.DNSRecordName{ | ||
| {Name: "cs.example.com.", Rtype: "A"}, | ||
| {Name: "*.cs.example.com.", Rtype: "A"}, | ||
| {Name: "1.ws.example.com.", Rtype: "A"}, | ||
| {Name: "*.1.ws.example.com.", Rtype: "A"}, | ||
| {Name: "*.1.ssh.cs.example.com.", Rtype: "A"}, | ||
| {Name: "1.cs.example.com.", Rtype: "A"}, | ||
| {Name: "*.1.cs.example.com.", Rtype: "A"}, | ||
| {Name: "2.ws.example.com.", Rtype: "A"}, | ||
| {Name: "*.2.ws.example.com.", Rtype: "A"}, | ||
| {Name: "*.2.ssh.cs.example.com.", Rtype: "A"}, | ||
| {Name: "2.cs.example.com.", Rtype: "A"}, | ||
| {Name: "*.2.cs.example.com.", Rtype: "A"}, | ||
| })) | ||
| }) | ||
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.