github.com/openshift/installer@v1.4.17/pkg/infrastructure/gcp/clusterapi/dns.go (about)

     1  package clusterapi
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	"google.golang.org/api/dns/v1"
    10  	"google.golang.org/api/option"
    11  
    12  	"github.com/openshift/installer/pkg/asset/installconfig"
    13  	gcpic "github.com/openshift/installer/pkg/asset/installconfig/gcp"
    14  	"github.com/openshift/installer/pkg/types"
    15  )
    16  
    17  var (
    18  	errNotFound = errors.New("not found")
    19  )
    20  
    21  func getDNSZoneName(ctx context.Context, ic *installconfig.InstallConfig, isPublic bool) (string, error) {
    22  	ctx, cancel := context.WithTimeout(ctx, time.Minute*1)
    23  	defer cancel()
    24  
    25  	client, err := gcpic.NewClient(ctx)
    26  	if err != nil {
    27  		return "", fmt.Errorf("failed to create new client: %w", err)
    28  	}
    29  
    30  	cctx, ccancel := context.WithTimeout(ctx, time.Minute*1)
    31  	defer ccancel()
    32  
    33  	domain := ic.Config.ClusterDomain()
    34  	if isPublic {
    35  		domain = ic.Config.BaseDomain
    36  	}
    37  
    38  	zone, err := client.GetDNSZone(cctx, ic.Config.GCP.ProjectID, domain, isPublic)
    39  	if err != nil {
    40  		return "", fmt.Errorf("failed to get dns zone name: %w", err)
    41  	}
    42  
    43  	if zone != nil {
    44  		return zone.Name, nil
    45  	}
    46  
    47  	return "", errNotFound
    48  }
    49  
    50  type recordSet struct {
    51  	projectID string
    52  	zoneName  string
    53  	record    *dns.ResourceRecordSet
    54  }
    55  
    56  // createRecordSets will create a list of records that will be created during the install.
    57  func createRecordSets(ctx context.Context, ic *installconfig.InstallConfig, clusterID, apiIP, apiIntIP string) ([]recordSet, error) {
    58  	ctx, cancel := context.WithTimeout(ctx, time.Minute*1)
    59  	defer cancel()
    60  
    61  	// A shared VPC install allows a user to preconfigure a private zone. If there is a private zone found,
    62  	// use the existing private zone otherwise default to the installer private zone pattern below.
    63  	privateZoneName, err := getDNSZoneName(ctx, ic, false)
    64  	if err != nil {
    65  		if !errors.Is(err, errNotFound) {
    66  			return nil, fmt.Errorf("failed to find private zone: %w", err)
    67  		}
    68  		privateZoneName = fmt.Sprintf("%s-private-zone", clusterID)
    69  	}
    70  
    71  	records := []recordSet{
    72  		{
    73  			// api_internal
    74  			projectID: ic.Config.GCP.ProjectID,
    75  			zoneName:  privateZoneName,
    76  			record: &dns.ResourceRecordSet{
    77  				Name:    fmt.Sprintf("api-int.%s.", ic.Config.ClusterDomain()),
    78  				Type:    "A",
    79  				Ttl:     60,
    80  				Rrdatas: []string{apiIntIP},
    81  			},
    82  		},
    83  		{
    84  			// api_external_internal_zone
    85  			projectID: ic.Config.GCP.ProjectID,
    86  			zoneName:  privateZoneName,
    87  			record: &dns.ResourceRecordSet{
    88  				Name:    fmt.Sprintf("api.%s.", ic.Config.ClusterDomain()),
    89  				Type:    "A",
    90  				Ttl:     60,
    91  				Rrdatas: []string{apiIntIP},
    92  			},
    93  		},
    94  	}
    95  
    96  	if ic.Config.Publish == types.ExternalPublishingStrategy {
    97  		existingPublicZoneName, err := getDNSZoneName(ctx, ic, true)
    98  		if err != nil {
    99  			return nil, fmt.Errorf("failed to find a public zone: %w", err)
   100  		}
   101  
   102  		apiRecord := recordSet{
   103  			projectID: ic.Config.GCP.ProjectID,
   104  			zoneName:  existingPublicZoneName,
   105  			record: &dns.ResourceRecordSet{
   106  				Name:    fmt.Sprintf("api.%s.", ic.Config.ClusterDomain()),
   107  				Type:    "A",
   108  				Ttl:     60,
   109  				Rrdatas: []string{apiIP},
   110  			},
   111  		}
   112  		records = append(records, apiRecord)
   113  	}
   114  
   115  	return records, nil
   116  }
   117  
   118  // createDNSRecords will get the list of records to be created and execute their creation through the gcp dns api.
   119  func createDNSRecords(ctx context.Context, ic *installconfig.InstallConfig, clusterID, apiIP, apiIntIP string) error {
   120  	ssn, err := gcpic.GetSession(ctx)
   121  	if err != nil {
   122  		return fmt.Errorf("failed to get session: %w", err)
   123  	}
   124  	// TODO: use the opts for the service to restrict scopes see google.golang.org/api/option.WithScopes
   125  	dnsService, err := dns.NewService(ctx, option.WithCredentials(ssn.Credentials))
   126  	if err != nil {
   127  		return fmt.Errorf("failed to create the gcp dns service: %w", err)
   128  	}
   129  
   130  	records, err := createRecordSets(ctx, ic, clusterID, apiIP, apiIntIP)
   131  	if err != nil {
   132  		return err
   133  	}
   134  
   135  	// 1 minute timeout for each record
   136  	ctx, cancel := context.WithTimeout(ctx, time.Minute*time.Duration(len(records)))
   137  	defer cancel()
   138  
   139  	for _, record := range records {
   140  		if _, err := dnsService.ResourceRecordSets.Create(record.projectID, record.zoneName, record.record).Context(ctx).Do(); err != nil {
   141  			return fmt.Errorf("failed to create record set %s: %w", record.record.Name, err)
   142  		}
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  // createPrivateManagedZone will create a private managed zone in the GCP project specified in the install config. The
   149  // private managed zone should only be created when one is not specified in the install config.
   150  func createPrivateManagedZone(ctx context.Context, ic *installconfig.InstallConfig, clusterID, network string) error {
   151  	// TODO: use the opts for the service to restrict scopes see google.golang.org/api/option.WithScopes
   152  	ssn, err := gcpic.GetSession(ctx)
   153  	if err != nil {
   154  		return fmt.Errorf("failed to get session: %w", err)
   155  	}
   156  	dnsService, err := dns.NewService(ctx, option.WithCredentials(ssn.Credentials))
   157  	if err != nil {
   158  		return fmt.Errorf("failed to create the gcp dns service: %w", err)
   159  	}
   160  
   161  	managedZone := &dns.ManagedZone{
   162  		Name:        fmt.Sprintf("%s-private-zone", clusterID),
   163  		Description: resourceDescription,
   164  		DnsName:     fmt.Sprintf("%s.", ic.Config.ClusterDomain()),
   165  		Visibility:  "private",
   166  		Labels:      mergeLabels(ic, clusterID),
   167  		PrivateVisibilityConfig: &dns.ManagedZonePrivateVisibilityConfig{
   168  			Networks: []*dns.ManagedZonePrivateVisibilityConfigNetwork{
   169  				{
   170  					NetworkUrl: network,
   171  				},
   172  			},
   173  		},
   174  	}
   175  
   176  	ctx, cancel := context.WithTimeout(ctx, time.Minute*1)
   177  	defer cancel()
   178  
   179  	if _, err = dnsService.ManagedZones.Create(ic.Config.GCP.ProjectID, managedZone).Context(ctx).Do(); err != nil {
   180  		return fmt.Errorf("failed to create private managed zone: %w", err)
   181  	}
   182  
   183  	return nil
   184  }