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 }