diff --git a/go.mod b/go.mod index 681e3fbd..9a8d2ca6 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/apparentlymart/go-cidr v1.1.0 - github.com/aws/aws-sdk-go-v2 v1.26.1 + github.com/aws/aws-sdk-go-v2 v1.30.0 github.com/aws/aws-sdk-go-v2/config v1.27.9 github.com/aws/aws-sdk-go-v2/credentials v1.17.9 github.com/aws/aws-sdk-go-v2/service/cloudformation v1.48.0 @@ -12,6 +12,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing v1.24.3 github.com/aws/aws-sdk-go-v2/service/iam v1.27.1 github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 + github.com/aws/aws-sdk-go-v2/service/ram v1.26.1 github.com/aws/aws-sdk-go-v2/service/route53 v1.40.3 github.com/aws/aws-sdk-go-v2/service/sts v1.28.5 github.com/go-jose/go-jose/v4 v4.0.2 @@ -34,8 +35,8 @@ require ( require ( github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.12 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.12 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.35.1 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect diff --git a/go.sum b/go.sum index 3619d26c..7b9135d6 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= -github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA= -github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= +github.com/aws/aws-sdk-go-v2 v1.30.0 h1:6qAwtzlfcTtcL8NHtbDQAqgM5s6NDipQTkPxyH/6kAA= +github.com/aws/aws-sdk-go-v2 v1.30.0/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2/go.mod h1:lPprDr1e6cJdyYeGXnRaJoP4Md+cDBvi2eOj00BlGmg= github.com/aws/aws-sdk-go-v2/config v1.27.9 h1:gRx/NwpNEFSk+yQlgmk1bmxxvQ5TyJ76CWXs9XScTqg= @@ -10,10 +10,10 @@ github.com/aws/aws-sdk-go-v2/credentials v1.17.9 h1:N8s0/7yW+h8qR8WaRlPQeJ6czVMN github.com/aws/aws-sdk-go-v2/credentials v1.17.9/go.mod h1:446YhIdmSV0Jf/SLafGZalQo+xr2iw7/fzXGDPTU1yQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 h1:af5YzcLf80tv4Em4jWVD75lpnOHSBkPUZxZfGkrI3HI= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0/go.mod h1:nQ3how7DMnFMWiU1SpECohgC82fpn4cKZ875NDMmwtA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.12 h1:SJ04WXGTwnHlWIODtC5kJzKbeuHt+OUNOgKg7nfnUGw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.12/go.mod h1:FkpvXhA92gb3GE9LD6Og0pHHycTxW7xGpnEh5E7Opwo= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.12 h1:hb5KgeYfObi5MHkSSZMEudnIvX30iB+E21evI4r6BnQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.12/go.mod h1:CroKe/eWJdyfy9Vx4rljP5wTUjNJfb+fPz1uMYUhEGM= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/service/cloudformation v1.48.0 h1:uMlYsoHdd2Gr9sDGq2ieUR5jVu7F5AqPYz6UBJmdRhY= @@ -32,6 +32,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 h1:b+E7zIUHM github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6/go.mod h1:S2fNV0rxrP78NhPbCZeQgY8H9jdDMeGtwcfZIRxzBqU= github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 h1:yS0JkEdV6h9JOo8sy2JSpjX+i7vsKifU8SIeHrqiDhU= github.com/aws/aws-sdk-go-v2/service/kms v1.30.0/go.mod h1:+I8VUUSVD4p5ISQtzpgSva4I8cJ4SQ4b1dcBcof7O+g= +github.com/aws/aws-sdk-go-v2/service/ram v1.26.1 h1:1UcUsMsHB7ZnpcUYNwBTX90hFjIZrhf8Xu00R9Vo+Kg= +github.com/aws/aws-sdk-go-v2/service/ram v1.26.1/go.mod h1:e/3wE+afnOAeolpqyg8fKAQK/kKya+ycDW62/X4vjK8= github.com/aws/aws-sdk-go-v2/service/route53 v1.40.3 h1:wr5gulbwbb8PSRMWjCROoP0TIMccpF8x5A7hEk2SjpA= github.com/aws/aws-sdk-go-v2/service/route53 v1.40.3/go.mod h1:/Gyl9xjGcjIVe80ar75YlmA8m6oFh0A4XfLciBmdS8s= github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 h1:mnbuWHOcM70/OFUlZZ5rcdfA8PflGXXiefU/O+1S3+8= diff --git a/pkg/aws/aws_client/client.go b/pkg/aws/aws_client/client.go index ff5428e6..382e52c4 100644 --- a/pkg/aws/aws_client/client.go +++ b/pkg/aws/aws_client/client.go @@ -2,8 +2,6 @@ package aws_client import ( "context" - "os" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" @@ -12,7 +10,9 @@ import ( "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/iam" "github.com/aws/aws-sdk-go-v2/service/kms" + "github.com/aws/aws-sdk-go-v2/service/ram" "github.com/aws/aws-sdk-go-v2/service/sts" + "os" "github.com/openshift-online/ocm-common/pkg/log" @@ -35,6 +35,7 @@ type AWSClient struct { KmsClient *kms.Client CloudWatchLogsClient *cloudwatchlogs.Client AWSConfig *aws.Config + RamClient *ram.Client } type AccessKeyMod struct { @@ -42,35 +43,44 @@ type AccessKeyMod struct { SecretAccessKey string `ini:"aws_secret_access_key,omitempty"` } -func CreateAWSClient(profileName string, region string) (*AWSClient, error) { +func CreateAWSClient(profileName string, region string, awsSharedCredentialFile ...string) (*AWSClient, error) { var cfg aws.Config var err error - if envCredential() { - log.LogInfo("Got AWS_ACCESS_KEY_ID env settings, going to build the config with the env") + if len(awsSharedCredentialFile) > 0 { + file := awsSharedCredentialFile[0] + log.LogInfo("Got aws shared credential file path: %s ", file) cfg, err = config.LoadDefaultConfig(context.TODO(), config.WithRegion(region), - config.WithCredentialsProvider( - credentials.NewStaticCredentialsProvider( - os.Getenv("AWS_ACCESS_KEY_ID"), - os.Getenv("AWS_SECRET_ACCESS_KEY"), - "")), + config.WithSharedCredentialsFiles([]string{file}), ) } else { - if envAwsProfile() { - file := os.Getenv("AWS_SHARED_CREDENTIALS_FILE") - log.LogInfo("Got file path: %s from env variable AWS_SHARED_CREDENTIALS_FILE\n", file) + if envCredential() { + log.LogInfo("Got AWS_ACCESS_KEY_ID env settings, going to build the config with the env") cfg, err = config.LoadDefaultConfig(context.TODO(), config.WithRegion(region), - config.WithSharedCredentialsFiles([]string{file}), + config.WithCredentialsProvider( + credentials.NewStaticCredentialsProvider( + os.Getenv("AWS_ACCESS_KEY_ID"), + os.Getenv("AWS_SECRET_ACCESS_KEY"), + "")), ) } else { - cfg, err = config.LoadDefaultConfig(context.TODO(), - config.WithRegion(region), - config.WithSharedConfigProfile(profileName), - ) - } + if envAwsProfile() { + file := os.Getenv("AWS_SHARED_CREDENTIALS_FILE") + log.LogInfo("Got file path: %s from env variable AWS_SHARED_CREDENTIALS_FILE\n", file) + cfg, err = config.LoadDefaultConfig(context.TODO(), + config.WithRegion(region), + config.WithSharedCredentialsFiles([]string{file}), + ) + } else { + cfg, err = config.LoadDefaultConfig(context.TODO(), + config.WithRegion(region), + config.WithSharedConfigProfile(profileName), + ) + } + } } if err != nil { @@ -88,6 +98,7 @@ func CreateAWSClient(profileName string, region string) (*AWSClient, error) { ClientContext: context.TODO(), KmsClient: kms.NewFromConfig(cfg), AWSConfig: &cfg, + RamClient: ram.NewFromConfig(cfg), } awsClient.AccountID = awsClient.GetAWSAccountID() return awsClient, nil diff --git a/pkg/aws/aws_client/ram.go b/pkg/aws/aws_client/ram.go new file mode 100644 index 00000000..8e0e105e --- /dev/null +++ b/pkg/aws/aws_client/ram.go @@ -0,0 +1,32 @@ +package aws_client + +import ( + "context" + "github.com/aws/aws-sdk-go-v2/service/ram" + "github.com/openshift-online/ocm-common/pkg/log" +) + +func (awsClient AWSClient) CreateResourceShare(resourceShareName string, resourceArns []string, principles []string) (*ram.CreateResourceShareOutput, error) { + input := &ram.CreateResourceShareInput{ + Name: &resourceShareName, + ResourceArns: resourceArns, + Principals: principles, + } + + resp, err := awsClient.RamClient.CreateResourceShare(context.TODO(), input) + if err != nil { + log.LogError("Create resource share failed with name %s: %s", resourceShareName, err.Error()) + } else { + log.LogInfo("Create resource share succeed with name %s", resourceShareName) + } + return resp, err +} + +func (awsClient AWSClient) DeleteResourceShare(resourceShareArn string) error { + input := &ram.DeleteResourceShareInput{ + ResourceShareArn: &resourceShareArn, + } + + _, err := awsClient.RamClient.DeleteResourceShare(context.TODO(), input) + return err +} diff --git a/pkg/aws/aws_client/role.go b/pkg/aws/aws_client/role.go index 50730fcb..b516396e 100644 --- a/pkg/aws/aws_client/role.go +++ b/pkg/aws/aws_client/role.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/openshift-online/ocm-common/pkg/log" "time" "github.com/aws/aws-sdk-go-v2/aws" @@ -395,3 +396,45 @@ func (client *AWSClient) UntagRole(roleName string, tagKeys []string) error { _, err := client.IamClient.UntagRole(context.TODO(), input) return err } + +func (client *AWSClient) CreateRoleForSharedVPC(roleName, installerRoleArn string, ingressOperatorRoleArn string) (types.Role, error) { + statement := map[string]interface{}{ + "Sid": "Statement1", + "Effect": "Allow", + "Principal": map[string]interface{}{ + "AWS": []string{installerRoleArn, ingressOperatorRoleArn}, + }, + "Action": "sts:AssumeRole", + } + + assumeRolePolicyDocument, err := completeRolePolicyDocument(statement) + if err != nil { + log.LogError("Failed to convert Role Policy Document into JSON: ", err.Error()) + return types.Role{}, err + } + + return client.CreateRole(roleName, string(assumeRolePolicyDocument), "", make(map[string]string), "/") +} + +func (client *AWSClient) CreatePolicyForSharedVPC(policyName string) (string, error) { + statement := map[string]interface{}{ + "Sid": "Statement1", + "Effect": "Allow", + "Action": []string{ + "route53:GetChange", + "route53:GetHostedZone", + "route53:ChangeResourceRecordSets", + "route53:ListHostedZones", + "route53:ListHostedZonesByName", + "route53:ListResourceRecordSets", + "route53:ChangeTagsForResource", + "route53:GetAccountLimit", + "route53:ListTagsForResource", + "route53:UpdateHostedZoneComment", + "tag:GetResources", + "tag:UntagResources", + }, + "Resource": "*", + } + return client.CreatePolicy(policyName, statement) +} diff --git a/pkg/aws/aws_client/route53.go b/pkg/aws/aws_client/route53.go index 4829d9d5..416dc53a 100644 --- a/pkg/aws/aws_client/route53.go +++ b/pkg/aws/aws_client/route53.go @@ -8,24 +8,30 @@ import ( "github.com/openshift-online/ocm-common/pkg/log" ) -func (awsClient AWSClient) CreateHostedZone(hostedZoneName string, vpcID string, private bool) (*route53.CreateHostedZoneOutput, error) { +func (awsClient AWSClient) CreateHostedZone(hostedZoneName string, callerReference string, vpcID string, region string, private bool) (*route53.CreateHostedZoneOutput, error) { + // CreateHostedZone is a function used for hosted zone creation on AWS + // callReference is a required field of CreateHostedZoneInput struct, which used to identifies the request as a unique string. + // Usually random string or date/time stamp can be used as callReference. input := &route53.CreateHostedZoneInput{ - Name: &hostedZoneName, + Name: &hostedZoneName, + CallerReference: &callerReference, HostedZoneConfig: &types.HostedZoneConfig{ PrivateZone: private, }, } if vpcID != "" { vpc := &types.VPC{ - VPCId: &vpcID, + VPCId: &vpcID, + VPCRegion: types.VPCRegion(region), } input.VPC = vpc } + resp, err := awsClient.Route53Client.CreateHostedZone(context.TODO(), input) if err != nil { log.LogError("Create hosted zone failed for vpc %s with name %s: %s", vpcID, hostedZoneName, err.Error()) } else { - log.LogError("Create hosted zone succeed for vpc %s with name %s", vpcID, hostedZoneName) + log.LogInfo("Create hosted zone succeed for vpc %s with name %s", vpcID, hostedZoneName) } return resp, err } @@ -47,3 +53,12 @@ func (awsClient AWSClient) ListHostedZoneByDNSName(hostedZoneName string) (*rout return awsClient.Route53Client.ListHostedZonesByName(context.TODO(), input) } + +func (awsClient AWSClient) DeleteHostedZone(hostedZoneID string) error { + input := &route53.DeleteHostedZoneInput{ + Id: &hostedZoneID, + } + + _, err := awsClient.Route53Client.DeleteHostedZone(context.TODO(), input) + return err +} diff --git a/pkg/test/vpc_client/vpc.go b/pkg/test/vpc_client/vpc.go index 0689cfc8..656853b2 100644 --- a/pkg/test/vpc_client/vpc.go +++ b/pkg/test/vpc_client/vpc.go @@ -149,7 +149,10 @@ func (vpc *VPC) DeleteVPCChain(totalClean ...bool) error { // Just be careful once you use checkExisting, the vpc may have subnets not existing in your zones. And maybe multi subnets in the zones // Try vpc.PreparePairSubnets by zone for further implementation to get a pair of // Zones will be customized if you want. Otherwise, it will use the default zone "a" -func PrepareVPC(vpcName string, region string, vpcCIDR string, checkExisting bool, zones ...string) (*VPC, error) { +func PrepareVPC(vpcName string, region string, vpcCIDR string, checkExisting bool, awsSharedCredentialFile string, zones ...string) (*VPC, error) { + var awsclient *aws_client.AWSClient + var err error + if vpcCIDR == "" { vpcCIDR = CON.DefaultVPCCIDR } @@ -160,7 +163,12 @@ func PrepareVPC(vpcName string, region string, vpcCIDR string, checkExisting boo vpcName, region, vpcCIDR) } log.LogInfo(logMessage) - awsclient, err := aws_client.CreateAWSClient("", region) + if awsSharedCredentialFile == "" { + awsclient, err = aws_client.CreateAWSClient("", region) + } else { + awsclient, err = aws_client.CreateAWSClient("", region, awsSharedCredentialFile) + } + if err != nil { log.LogError("Create AWS Client due to error: %s", err.Error()) return nil, err @@ -217,8 +225,9 @@ func PrepareVPC(vpcName string, region string, vpcCIDR string, checkExisting boo // GenerateVPCByID will return a VPC with CIDRpool and subnets // If you know the vpc ID on AWS, then try to generate it -func GenerateVPCByID(vpcID string, region string) (*VPC, error) { - awsClient, err := aws_client.CreateAWSClient("", region) +func GenerateVPCByID(vpcID string, region string, awsSharedCredentialFile ...string) (*VPC, error) { + awsClient, err := aws_client.CreateAWSClient("", region, awsSharedCredentialFile...) + if err != nil { return nil, err }