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  }