Skip to content

Commit c423d63

Browse files
committed
CORS-2894: capi/aws: Create DNS resources and PHZ
1 parent 310a787 commit c423d63

File tree

1 file changed

+122
-0
lines changed
  • pkg/infrastructure/aws/clusterapi

1 file changed

+122
-0
lines changed

pkg/infrastructure/aws/clusterapi/aws.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,22 @@ import (
44
"context"
55
"fmt"
66

7+
"github.com/aws/aws-sdk-go/aws"
8+
"github.com/aws/aws-sdk-go/aws/session"
9+
"github.com/aws/aws-sdk-go/service/ec2"
10+
"github.com/sirupsen/logrus"
11+
capa "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
12+
k8sClient "sigs.k8s.io/controller-runtime/pkg/client"
13+
14+
awsconfig "github.com/openshift/installer/pkg/asset/installconfig/aws"
15+
"github.com/openshift/installer/pkg/asset/manifests/capiutils"
716
"github.com/openshift/installer/pkg/infrastructure/clusterapi"
817
awstypes "github.com/openshift/installer/pkg/types/aws"
918
)
1019

1120
var _ clusterapi.Provider = (*Provider)(nil)
1221
var _ clusterapi.PreProvider = (*Provider)(nil)
22+
var _ clusterapi.InfraReadyProvider = (*Provider)(nil)
1323

1424
// Provider implements AWS CAPI installation.
1525
type Provider struct{}
@@ -24,3 +34,115 @@ func (*Provider) PreProvision(ctx context.Context, in clusterapi.PreProvisionInp
2434
}
2535
return nil
2636
}
37+
38+
// InfraReady creates private hosted zone and DNS records.
39+
func (*Provider) InfraReady(ctx context.Context, in clusterapi.InfraReadyInput) error {
40+
awsCluster := &capa.AWSCluster{}
41+
key := k8sClient.ObjectKey{
42+
Name: in.InfraID,
43+
Namespace: capiutils.Namespace,
44+
}
45+
if err := in.Client.Get(ctx, key, awsCluster); err != nil {
46+
return fmt.Errorf("failed to get AWSCluster: %w", err)
47+
}
48+
49+
awsSession, err := in.InstallConfig.AWS.Session(ctx)
50+
if err != nil {
51+
return fmt.Errorf("failed to get aws session: %w", err)
52+
}
53+
54+
subnetIDs := make([]string, 0, len(awsCluster.Spec.NetworkSpec.Subnets))
55+
for _, s := range awsCluster.Spec.NetworkSpec.Subnets {
56+
subnetIDs = append(subnetIDs, s.ResourceID)
57+
}
58+
59+
vpcID := awsCluster.Spec.NetworkSpec.VPC.ID
60+
if len(subnetIDs) > 0 && len(vpcID) == 0 {
61+
// All subnets belong to the same VPC, so we only need one
62+
vpcID, err = getVPCFromSubnets(ctx, awsSession, awsCluster.Spec.Region, subnetIDs[:1])
63+
if err != nil {
64+
return err
65+
}
66+
}
67+
68+
tags := map[string]string{
69+
fmt.Sprintf("kubernetes.io/cluster/%s", in.InfraID): "owned",
70+
}
71+
for k, v := range awsCluster.Spec.AdditionalTags {
72+
tags[k] = v
73+
}
74+
75+
client := awsconfig.NewClient(awsSession)
76+
77+
phzID := in.InstallConfig.Config.AWS.HostedZone
78+
if len(phzID) == 0 {
79+
logrus.Infoln("Creating private Hosted Zone")
80+
res, err := client.CreateHostedZone(ctx, &awsconfig.HostedZoneInput{
81+
InfraID: in.InfraID,
82+
VpcID: vpcID,
83+
Region: awsCluster.Spec.Region,
84+
Name: in.InstallConfig.Config.ClusterDomain(),
85+
Role: in.InstallConfig.Config.AWS.HostedZoneRole,
86+
UserTags: tags,
87+
})
88+
if err != nil {
89+
return fmt.Errorf("failed to create private hosted zone: %w", err)
90+
}
91+
phzID = aws.StringValue(res.Id)
92+
}
93+
94+
logrus.Infoln("Creating Route53 records for control plane load balancer")
95+
apiHost := awsCluster.Status.Network.SecondaryAPIServerELB.DNSName
96+
if awsCluster.Status.Network.APIServerELB.Scheme == capa.ELBSchemeInternetFacing {
97+
apiHost = awsCluster.Status.Network.APIServerELB.DNSName
98+
}
99+
apiIntHost := awsCluster.Spec.ControlPlaneEndpoint.Host
100+
err = client.CreateOrUpdateRecord(ctx, in.InstallConfig.Config, apiHost, apiIntHost, phzID)
101+
if err != nil {
102+
return fmt.Errorf("failed to create route53 records: %w", err)
103+
}
104+
105+
return nil
106+
}
107+
108+
func getVPCFromSubnets(ctx context.Context, awsSession *session.Session, region string, subnetIDs []string) (string, error) {
109+
var vpcID string
110+
var lastError error
111+
client := ec2.New(awsSession, aws.NewConfig().WithRegion(region))
112+
err := client.DescribeSubnetsPagesWithContext(
113+
ctx,
114+
&ec2.DescribeSubnetsInput{SubnetIds: aws.StringSlice(subnetIDs)},
115+
func(results *ec2.DescribeSubnetsOutput, lastPage bool) bool {
116+
for _, subnet := range results.Subnets {
117+
if subnet.SubnetId == nil {
118+
continue
119+
}
120+
if subnet.SubnetArn == nil {
121+
lastError = fmt.Errorf("%s has no ARN", *subnet.SubnetId)
122+
return false
123+
}
124+
if subnet.VpcId == nil {
125+
lastError = fmt.Errorf("%s has no VPC", *subnet.SubnetId)
126+
return false
127+
}
128+
if subnet.AvailabilityZone == nil {
129+
lastError = fmt.Errorf("%s has no availability zone", *subnet.SubnetId)
130+
return false
131+
}
132+
// All subnets belong to the same VPC
133+
vpcID = aws.StringValue(subnet.VpcId)
134+
lastError = nil
135+
return true
136+
}
137+
return !lastPage
138+
},
139+
)
140+
if err == nil {
141+
err = lastError
142+
}
143+
if err != nil {
144+
return "", fmt.Errorf("failed to get VPC from subnets: %w", err)
145+
}
146+
147+
return vpcID, nil
148+
}

0 commit comments

Comments
 (0)