Skip to content

Commit ebc05e6

Browse files
committed
CORS-4044: Add private dns zone section to GCP install config
install.openshift.io_installconfigs.yaml: ** Updated fields from the types/installconfig/gcp CORS-4047: Add private Zone Validation pkg/types/gcp/platform.go: ** Add the user specified private dns zone ** Add static validation pkg/asset/installconfig/gcp/validation.go: ** When private dns zone information is provided, ensure that the project and zone are used for validation. CORS-4045: Update Clsuter Metadata ** Add the GCP private zone information to the cluster metadata CORS-4048: Update TFVars to include private zone info CORS-4049: Find the correct project for the dns zones ** Update the DNS Manifest to take the correct private zone project when specified. ** Note: Need to update DNS Spec to take in a project. CORS-4046: Delete Private Zones pkg/destroy/gcp: ** Use the cluster metadata to update the gcp cluster uninstaller. ** Find DNS zones in the correct project. Delete the zones that can and should be deleted. ** Delete the DNS records in the private and public zones. pkg/destroy/gcp: ** Destroy DNS zones if they have the "owned" label. installconfig/gcp: ** Generate a new Client function to find private DNS zones where the base domain and zone name are both provided. manifests/dns: ** Use the new client function to ensure that we find the correct private zone when private zone information is provided in the install config file. clusterapi/dns: ** Use the new client function to ensure that we find the correct private zone when private zone information is provided in the install config file. Adding the "shared" tag when the installer does not create the private managed zone. ** On Destroy, search the private dns zone for the labels. If the shared label with a key matching the cluster ID exists, remove the label.
1 parent 282920e commit ebc05e6

File tree

18 files changed

+700
-158
lines changed

18 files changed

+700
-158
lines changed

data/data/install.openshift.io_installconfigs.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5805,6 +5805,28 @@ spec:
58055805
type: string
58065806
type: array
58075807
type: object
5808+
dns:
5809+
description: |-
5810+
DNS contains the dns zone information for the cluster. The DNS information can
5811+
only be supplied during Shared VPC (XPN) installs.
5812+
properties:
5813+
privateZone:
5814+
description: |-
5815+
PrivateZone contains the information for a private DNS zone. The Private DNS Zone can
5816+
only be supplied during Shared VPC (XPN) installs. The PrivateZone can exist or be
5817+
created in a second service project; a project other than the one matching projectID
5818+
or networkProjectID.
5819+
properties:
5820+
name:
5821+
description: Name is the name of the dns-managed zone.
5822+
type: string
5823+
projectID:
5824+
description: ProjectID is the project where the zone resides.
5825+
type: string
5826+
required:
5827+
- name
5828+
type: object
5829+
type: object
58085830
network:
58095831
description: |-
58105832
Network specifies an existing VPC where the cluster should be created

pkg/asset/cluster/gcp/gcp.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,22 @@ import (
1212
func Metadata(config *types.InstallConfig) *gcp.Metadata {
1313
// leave the private zone domain blank when not using a pre-created private zone
1414
privateZoneDomain := fmt.Sprintf("%s.", config.ClusterDomain())
15+
privateZoneProject := config.GCP.ProjectID
1516
if config.GCP.Network == "" || config.GCP.NetworkProjectID == "" {
1617
privateZoneDomain = ""
1718
}
19+
if config.GCP.DNS != nil && config.GCP.DNS.PrivateZone != nil {
20+
if config.GCP.DNS.PrivateZone.ProjectID != "" {
21+
privateZoneProject = config.GCP.DNS.PrivateZone.ProjectID
22+
}
23+
}
1824

1925
return &gcp.Metadata{
20-
Region: config.Platform.GCP.Region,
21-
ProjectID: config.Platform.GCP.ProjectID,
22-
NetworkProjectID: config.Platform.GCP.NetworkProjectID,
23-
PrivateZoneDomain: privateZoneDomain,
24-
ServiceEndpoints: config.Platform.GCP.ServiceEndpoints,
26+
Region: config.Platform.GCP.Region,
27+
ProjectID: config.Platform.GCP.ProjectID,
28+
NetworkProjectID: config.Platform.GCP.NetworkProjectID,
29+
PrivateZoneDomain: privateZoneDomain,
30+
PrivateZoneProjectID: privateZoneProject,
31+
ServiceEndpoints: config.Platform.GCP.ServiceEndpoints,
2532
}
2633
}

pkg/asset/cluster/tfvars/tfvars.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ func (t *TerraformVariables) Generate(ctx context.Context, parents asset.Parents
517517
}
518518

519519
// Set the private zone
520-
privateZoneName, err = manifests.GetGCPPrivateZoneName(ctx, client, installConfig, clusterID.InfraID)
520+
privateZoneName, _, err = manifests.GetGCPPrivateZoneName(ctx, client, installConfig, clusterID.InfraID)
521521
if err != nil {
522522
return fmt.Errorf("failed to find gcp private dns zone: %w", err)
523523
}

pkg/asset/installconfig/gcp/client.go

Lines changed: 125 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type API interface {
4242
GetMachineTypeWithZones(ctx context.Context, project, region, machineType string) (*compute.MachineType, sets.Set[string], error)
4343
GetPublicDomains(ctx context.Context, project string) ([]string, error)
4444
GetDNSZone(ctx context.Context, project, baseDomain string, isPublic bool) (*dns.ManagedZone, error)
45+
GetDNSZoneFromParams(ctx context.Context, params gcptypes.DNSZoneParams) (*dns.ManagedZone, error)
4546
GetDNSZoneByName(ctx context.Context, project, zoneName string) (*dns.ManagedZone, error)
4647
GetSubnetworks(ctx context.Context, network, project, region string) ([]*compute.Subnetwork, error)
4748
GetProjects(ctx context.Context) (map[string]string, error)
@@ -58,6 +59,7 @@ type API interface {
5859
GetProjectTags(ctx context.Context, projectID string) (sets.Set[string], error)
5960
GetNamespacedTagValue(ctx context.Context, tagNamespacedName string) (*cloudresourcemanager.TagValue, error)
6061
GetKeyRing(ctx context.Context, kmsKeyRef *gcptypes.KMSKeyReference) (*kmspb.KeyRing, error)
62+
UpdateDNSPrivateZoneLabels(ctx context.Context, baseDomain, project, zoneName string, labels map[string]string) error
6163
}
6264

6365
// Client makes calls to the GCP API.
@@ -243,41 +245,138 @@ func (c *Client) GetPublicDomains(ctx context.Context, project string) ([]string
243245
return publicZones, nil
244246
}
245247

248+
func getDNSZoneByName(ctx context.Context, svc *dns.Service, project, zoneName string) (*dns.ManagedZone, error) {
249+
returnedZone, err := svc.ManagedZones.Get(project, zoneName).Context(ctx).Do()
250+
if err != nil {
251+
return nil, fmt.Errorf("failed to get DNS Zones: %w", err)
252+
}
253+
return returnedZone, nil
254+
}
255+
246256
// GetDNSZoneByName returns a DNS zone matching the `zoneName` if the DNS zone exists
247257
// and can be seen (correct permissions for a private zone) in the project.
248258
func (c *Client) GetDNSZoneByName(ctx context.Context, project, zoneName string) (*dns.ManagedZone, error) {
249-
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
250-
defer cancel()
251-
252259
svc, err := c.getDNSService(ctx)
253260
if err != nil {
254261
return nil, err
255262
}
256-
returnedZone, err := svc.ManagedZones.Get(project, zoneName).Context(ctx).Do()
263+
return getDNSZoneByName(ctx, svc, project, zoneName)
264+
}
265+
266+
// UpdateDNSPrivateZoneLabels will find a private DNS zone in the project with the name passed in. The labels
267+
// for the zone will be updated to include the provided labels. The labels that match will be overwritten
268+
// and all other labels will remain.
269+
func (c *Client) UpdateDNSPrivateZoneLabels(ctx context.Context, baseDomain, project, zoneName string, labels map[string]string) error {
270+
params := gcptypes.DNSZoneParams{
271+
Project: project,
272+
Name: zoneName,
273+
BaseDomain: baseDomain,
274+
IsPublic: false,
275+
}
276+
zone, err := c.GetDNSZoneFromParams(ctx, params)
257277
if err != nil {
258-
return nil, errors.Wrap(err, "failed to get DNS Zones")
278+
return err
279+
}
280+
if zone == nil {
281+
return fmt.Errorf("failed to find matching DNS zone for %s in project %s", zoneName, project)
259282
}
260-
return returnedZone, nil
261-
}
262283

263-
// GetDNSZone returns a DNS zone for a basedomain.
264-
func (c *Client) GetDNSZone(ctx context.Context, project, baseDomain string, isPublic bool) (*dns.ManagedZone, error) {
265-
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)
266-
defer cancel()
284+
if zone.Labels == nil {
285+
zone.Labels = make(map[string]string)
286+
}
267287

268-
svc, err := c.getDNSService(ctx)
288+
for key, value := range labels {
289+
zone.Labels[key] = value
290+
}
291+
292+
if zone.Description == "" {
293+
// It is possible to create a managed zone without a description using the GCP web console.
294+
// If the description is missing the managed zone modification will fail.
295+
zone.Description = "Used by OpenShift Installer"
296+
}
297+
298+
dnsService, err := c.getDNSService(ctx)
269299
if err != nil {
270-
return nil, err
300+
return fmt.Errorf("failed to get dns service during dns managed zone update: %w", err)
301+
}
302+
303+
return UpdateDNSManagedZone(ctx, dnsService, project, zoneName, zone)
304+
}
305+
306+
// UpdateDNSManagedZone will update a dns managed zone with the matching name and project. The new zone
307+
// information is contained in the zone parameter.
308+
func UpdateDNSManagedZone(ctx context.Context, svc *dns.Service, project, zoneName string, zone *dns.ManagedZone) error {
309+
_, err := svc.ManagedZones.Update(project, zoneName, zone).Context(ctx).Do()
310+
if err != nil {
311+
return fmt.Errorf("failed updating DNS Zone %s in project %s: %w", zoneName, project, err)
271312
}
272-
if !strings.HasSuffix(baseDomain, ".") {
273-
baseDomain = fmt.Sprintf("%s.", baseDomain)
313+
return nil
314+
}
315+
316+
func formatBaseDomain(domain string) string {
317+
if !strings.HasSuffix(domain, ".") {
318+
domain = fmt.Sprintf("%s.", domain)
274319
}
320+
return domain
321+
}
275322

276-
// currently, only private and public are supported. All peering zones are private.
323+
func getZoneVisibility(isPublic bool) string {
277324
visibility := "private"
278325
if isPublic {
279326
visibility = "public"
280327
}
328+
return visibility
329+
}
330+
331+
// GetDNSZoneFromParams allows the user to enter parameters found in `DNSZoneParams` to find a
332+
// dns managed zone by name or by base domain.
333+
func GetDNSZoneFromParams(ctx context.Context, svc *dns.Service, params gcptypes.DNSZoneParams) (*dns.ManagedZone, error) {
334+
switch {
335+
case params.Name == "" && params.BaseDomain != "":
336+
return getDNSZone(ctx, svc, params.Project, params.BaseDomain, params.IsPublic)
337+
case params.Name != "":
338+
managedZone, err := getDNSZoneByName(ctx, svc, params.Project, params.Name)
339+
if params.BaseDomain == "" {
340+
return managedZone, err
341+
}
342+
if err != nil {
343+
if IsNotFound(err) {
344+
return nil, nil
345+
}
346+
return nil, err
347+
}
348+
if managedZone == nil {
349+
return nil, nil
350+
}
351+
baseDomain := formatBaseDomain(params.BaseDomain)
352+
if !strings.HasSuffix(managedZone.DnsName, baseDomain) {
353+
return nil, fmt.Errorf("failed to find matching DNS zone for %s with DNS name %s", params.Name, params.BaseDomain)
354+
}
355+
visibility := getZoneVisibility(params.IsPublic)
356+
if managedZone.Visibility != visibility {
357+
return nil, fmt.Errorf("failed to find matching DNS zone for %s with visibility %s", params.Name, visibility)
358+
}
359+
return managedZone, nil
360+
}
361+
return nil, fmt.Errorf("invalid dns zone parameters, please provide a base domain or name")
362+
}
363+
364+
// GetDNSZoneFromParams allows the user to enter parameters found in DNSZoneParams. The user must enter at
365+
// least a base domain or a zone name to make a valid request. When both fields are populated extra validation
366+
// steps occur to ensure that the correct zone is found.
367+
func (c *Client) GetDNSZoneFromParams(ctx context.Context, params gcptypes.DNSZoneParams) (*dns.ManagedZone, error) {
368+
svc, err := c.getDNSService(ctx)
369+
if err != nil {
370+
return nil, err
371+
}
372+
return GetDNSZoneFromParams(ctx, svc, params)
373+
}
374+
375+
func getDNSZone(ctx context.Context, svc *dns.Service, project, baseDomain string, isPublic bool) (*dns.ManagedZone, error) {
376+
baseDomain = formatBaseDomain(baseDomain)
377+
378+
// currently, only private and public are supported. All peering zones are private.
379+
visibility := getZoneVisibility(isPublic)
281380

282381
req := svc.ManagedZones.List(project).DnsName(baseDomain).Context(ctx)
283382
var res *dns.ManagedZone
@@ -290,7 +389,7 @@ func (c *Client) GetDNSZone(ctx context.Context, project, baseDomain string, isP
290389
}
291390
return nil
292391
}); err != nil {
293-
return nil, errors.Wrap(err, "failed to list DNS Zones")
392+
return nil, fmt.Errorf("failed to list DNS Zones: %w", err)
294393
}
295394
if res == nil {
296395
if isPublic {
@@ -305,6 +404,15 @@ func (c *Client) GetDNSZone(ctx context.Context, project, baseDomain string, isP
305404
return res, nil
306405
}
307406

407+
// GetDNSZone returns a DNS zone for a basedomain.
408+
func (c *Client) GetDNSZone(ctx context.Context, project, baseDomain string, isPublic bool) (*dns.ManagedZone, error) {
409+
svc, err := c.getDNSService(ctx)
410+
if err != nil {
411+
return nil, err
412+
}
413+
return getDNSZone(ctx, svc, project, baseDomain, isPublic)
414+
}
415+
308416
// GetRecordSets returns all the records for a DNS zone.
309417
func (c *Client) GetRecordSets(ctx context.Context, project, zone string) ([]*dns.ResourceRecordSet, error) {
310418
ctx, cancel := context.WithTimeout(ctx, defaultTimeout)

pkg/asset/installconfig/gcp/dns.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ func IsThrottled(err error) bool {
7070

7171
// IsNotFound checks whether a response from the GPC API was not found.
7272
func IsNotFound(err error) bool {
73-
gErr, ok := err.(*googleapi.Error)
74-
return ok && gErr.Code == http.StatusNotFound
73+
var gErr *googleapi.Error
74+
if errors.As(err, &gErr) {
75+
return gErr.Code == http.StatusNotFound
76+
}
77+
return false
7578
}

pkg/asset/installconfig/gcp/mock/gcpclient_generated.go

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)