github.com/openshift/installer@v1.4.17/pkg/asset/cluster/aws/aws.go (about) 1 // Package aws extracts AWS metadata from install configurations. 2 package aws 3 4 import ( 5 "context" 6 "fmt" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/service/ec2" 10 "github.com/aws/aws-sdk-go/service/iam" 11 "github.com/aws/aws-sdk-go/service/route53" 12 "github.com/pkg/errors" 13 "github.com/sirupsen/logrus" 14 "k8s.io/apimachinery/pkg/util/sets" 15 16 "github.com/openshift/installer/pkg/asset/installconfig" 17 awsic "github.com/openshift/installer/pkg/asset/installconfig/aws" 18 "github.com/openshift/installer/pkg/types" 19 awstypes "github.com/openshift/installer/pkg/types/aws" 20 ) 21 22 // Metadata converts an install configuration to AWS metadata. 23 func Metadata(clusterID, infraID string, config *types.InstallConfig) *awstypes.Metadata { 24 return &awstypes.Metadata{ 25 Region: config.Platform.AWS.Region, 26 Identifier: []map[string]string{ 27 {fmt.Sprintf("kubernetes.io/cluster/%s", infraID): "owned"}, 28 {"openshiftClusterID": clusterID}, 29 {fmt.Sprintf("sigs.k8s.io/cluster-api-provider-aws/cluster/%s", infraID): "owned"}, 30 }, 31 ServiceEndpoints: config.AWS.ServiceEndpoints, 32 ClusterDomain: config.ClusterDomain(), 33 HostedZoneRole: config.AWS.HostedZoneRole, 34 } 35 } 36 37 // PreTerraform performs any infrastructure initialization which must 38 // happen before Terraform creates the remaining infrastructure. 39 func PreTerraform(ctx context.Context, clusterID string, installConfig *installconfig.InstallConfig) error { 40 if err := tagSharedVPCResources(ctx, clusterID, installConfig); err != nil { 41 return err 42 } 43 44 if err := tagSharedIAMRoles(ctx, clusterID, installConfig); err != nil { 45 return err 46 } 47 48 return tagSharedIAMProfiles(ctx, clusterID, installConfig) 49 } 50 51 func tagSharedVPCResources(ctx context.Context, clusterID string, installConfig *installconfig.InstallConfig) error { 52 if len(installConfig.Config.Platform.AWS.Subnets) == 0 { 53 return nil 54 } 55 56 privateSubnets, err := installConfig.AWS.PrivateSubnets(ctx) 57 if err != nil { 58 return err 59 } 60 61 publicSubnets, err := installConfig.AWS.PublicSubnets(ctx) 62 if err != nil { 63 return err 64 } 65 66 ids := make([]*string, 0, len(privateSubnets)+len(publicSubnets)) 67 for id := range privateSubnets { 68 ids = append(ids, aws.String(id)) 69 } 70 for id := range publicSubnets { 71 ids = append(ids, aws.String(id)) 72 } 73 74 session, err := installConfig.AWS.Session(ctx) 75 if err != nil { 76 return errors.Wrap(err, "could not create AWS session") 77 } 78 79 tagKey, tagValue := sharedTag(clusterID) 80 81 ec2Client := ec2.New(session, aws.NewConfig().WithRegion(installConfig.Config.Platform.AWS.Region)) 82 if _, err = ec2Client.CreateTagsWithContext(ctx, &ec2.CreateTagsInput{ 83 Resources: ids, 84 Tags: []*ec2.Tag{{Key: &tagKey, Value: &tagValue}}, 85 }); err != nil { 86 return errors.Wrap(err, "could not add tags to subnets") 87 } 88 89 if zone := installConfig.Config.AWS.HostedZone; zone != "" { 90 r53cfg := awsic.GetR53ClientCfg(session, installConfig.Config.AWS.HostedZoneRole) 91 route53Client := route53.New(session, r53cfg) 92 if _, err := route53Client.ChangeTagsForResourceWithContext(ctx, &route53.ChangeTagsForResourceInput{ 93 ResourceType: aws.String("hostedzone"), 94 ResourceId: aws.String(zone), 95 AddTags: []*route53.Tag{{Key: &tagKey, Value: &tagValue}}, 96 }); err != nil { 97 return errors.Wrap(err, "could not add tags to hosted zone") 98 } 99 } 100 101 return nil 102 } 103 104 func tagSharedIAMRoles(ctx context.Context, clusterID string, installConfig *installconfig.InstallConfig) error { 105 iamRoles := sets.New[string]() 106 { 107 mpool := awstypes.MachinePool{} 108 mpool.Set(installConfig.Config.AWS.DefaultMachinePlatform) 109 if mp := installConfig.Config.ControlPlane; mp != nil { 110 mpool.Set(mp.Platform.AWS) 111 } 112 if len(mpool.IAMRole) > 0 { 113 iamRoles.Insert(mpool.IAMRole) 114 } 115 } 116 117 for _, compute := range installConfig.Config.Compute { 118 mpool := awstypes.MachinePool{} 119 mpool.Set(installConfig.Config.AWS.DefaultMachinePlatform) 120 mpool.Set(compute.Platform.AWS) 121 if len(mpool.IAMRole) > 0 { 122 iamRoles.Insert(mpool.IAMRole) 123 } 124 } 125 126 // If compute stanza was not defined, it will inherit from DefaultMachinePlatform later on. 127 if installConfig.Config.Compute == nil { 128 mpool := installConfig.Config.AWS.DefaultMachinePlatform 129 if mpool != nil && len(mpool.IAMRole) > 0 { 130 iamRoles.Insert(mpool.IAMRole) 131 } 132 } 133 134 if iamRoles.Len() == 0 { 135 return nil 136 } 137 138 logrus.Debugf("Tagging shared instance roles: %v", sets.List(iamRoles)) 139 140 session, err := installConfig.AWS.Session(ctx) 141 if err != nil { 142 return fmt.Errorf("could not create AWS session: %w", err) 143 } 144 145 tagKey, tagValue := sharedTag(clusterID) 146 147 iamClient := iam.New(session, aws.NewConfig().WithRegion(installConfig.Config.Platform.AWS.Region)) 148 for role := range iamRoles { 149 if _, err := iamClient.TagRoleWithContext(ctx, &iam.TagRoleInput{ 150 RoleName: aws.String(role), 151 Tags: []*iam.Tag{ 152 {Key: aws.String(tagKey), Value: aws.String(tagValue)}, 153 }, 154 }); err != nil { 155 return fmt.Errorf("could not tag %q instance role: %w", role, err) 156 } 157 } 158 159 return nil 160 } 161 162 // tagSharedIAMProfiles tags users BYO instance profiles so they are not destroyed by the Installer. 163 func tagSharedIAMProfiles(ctx context.Context, clusterID string, installConfig *installconfig.InstallConfig) error { 164 iamProfileNames := sets.New[string]() 165 166 { 167 mpool := awstypes.MachinePool{} 168 mpool.Set(installConfig.Config.AWS.DefaultMachinePlatform) 169 170 if mp := installConfig.Config.ControlPlane; mp != nil { 171 mpool.Set(mp.Platform.AWS) 172 } 173 if len(mpool.IAMProfile) > 0 { 174 iamProfileNames.Insert(mpool.IAMProfile) 175 } 176 } 177 178 for _, compute := range installConfig.Config.Compute { 179 mpool := awstypes.MachinePool{} 180 mpool.Set(installConfig.Config.AWS.DefaultMachinePlatform) 181 mpool.Set(compute.Platform.AWS) 182 if len(mpool.IAMProfile) > 0 { 183 iamProfileNames.Insert(mpool.IAMProfile) 184 } 185 } 186 187 // If compute stanza was not defined in the install-config.yaml, it will inherit from the 188 // DefaultMachinePlatform later on. 189 if installConfig.Config.Compute == nil { 190 mpool := installConfig.Config.AWS.DefaultMachinePlatform 191 if mpool != nil && len(mpool.IAMProfile) > 0 { 192 iamProfileNames.Insert(mpool.IAMProfile) 193 } 194 } 195 196 if iamProfileNames.Len() == 0 { 197 return nil 198 } 199 200 logrus.Debugf("Tagging shared instance profiles: %v", sets.List(iamProfileNames)) 201 202 session, err := installConfig.AWS.Session(ctx) 203 if err != nil { 204 return errors.Wrap(err, "could not create AWS session") 205 } 206 iamClient := iam.New(session, aws.NewConfig().WithRegion(installConfig.Config.AWS.Region)) 207 208 tagKey, tagValue := sharedTag(clusterID) 209 for name := range iamProfileNames { 210 if _, err := iamClient.TagInstanceProfileWithContext(ctx, &iam.TagInstanceProfileInput{ 211 InstanceProfileName: aws.String(name), 212 Tags: []*iam.Tag{ 213 {Key: aws.String(tagKey), Value: aws.String(tagValue)}, 214 }, 215 }); err != nil { 216 return fmt.Errorf("could not tag %q instance profile: %w", name, err) 217 } 218 } 219 220 return nil 221 } 222 223 func sharedTag(clusterID string) (string, string) { 224 return fmt.Sprintf("kubernetes.io/cluster/%s", clusterID), "shared" 225 }