@@ -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
1120var _ clusterapi.Provider = (* Provider )(nil )
1221var _ clusterapi.PreProvider = (* Provider )(nil )
22+ var _ clusterapi.InfraReadyProvider = (* Provider )(nil )
1323
1424// Provider implements AWS CAPI installation.
1525type 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