Skip to content

Commit 5f4e6cd

Browse files
Merge pull request #9792 from barbacbd/CORS-4044
CORS-4044, CORS-4045, CORS-4046, CORS-4047, CORS-4048, CORS-4049, CORS-4050, CORS-4051: Add private dns zone section to GCP install config
2 parents f1df503 + ebc05e6 commit 5f4e6cd

File tree

18 files changed

+699
-157
lines changed

18 files changed

+699
-157
lines changed

data/data/install.openshift.io_installconfigs.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5813,6 +5813,28 @@ spec:
58135813
type: string
58145814
type: array
58155815
type: object
5816+
dns:
5817+
description: |-
5818+
DNS contains the dns zone information for the cluster. The DNS information can
5819+
only be supplied during Shared VPC (XPN) installs.
5820+
properties:
5821+
privateZone:
5822+
description: |-
5823+
PrivateZone contains the information for a private DNS zone. The Private DNS Zone can
5824+
only be supplied during Shared VPC (XPN) installs. The PrivateZone can exist or be
5825+
created in a second service project; a project other than the one matching projectID
5826+
or networkProjectID.
5827+
properties:
5828+
name:
5829+
description: Name is the name of the dns-managed zone.
5830+
type: string
5831+
projectID:
5832+
description: ProjectID is the project where the zone resides.
5833+
type: string
5834+
required:
5835+
- name
5836+
type: object
5837+
type: object
58165838
network:
58175839
description: |-
58185840
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)