diff --git a/pkg/model/awsmodel/iam.go b/pkg/model/awsmodel/iam.go index 044be0e851..58581f5ac1 100644 --- a/pkg/model/awsmodel/iam.go +++ b/pkg/model/awsmodel/iam.go @@ -17,12 +17,14 @@ limitations under the License. package awsmodel import ( + "context" "fmt" "sort" "strings" + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/ec2" awsiam "github.com/aws/aws-sdk-go-v2/service/iam" - "github.com/aws/aws-sdk-go/aws/endpoints" "k8s.io/apimachinery/pkg/types" "k8s.io/klog/v2" "k8s.io/kops/pkg/apis/kops" @@ -404,15 +406,21 @@ func (b *IAMModelBuilder) buildPolicy(policyString string) (*iam.Policy, error) // IAMServiceEC2 returns the name of the IAM service for EC2 in the current region. // It is ec2.amazonaws.com in the default aws partition, but different in other isolated/custom partitions -func IAMServiceEC2(region string) string { - partitions := endpoints.DefaultPartitions() - for _, p := range partitions { - if _, ok := p.Regions()[region]; ok { - ep := "ec2." + p.DNSSuffix() - return ep - } +func IAMServiceEC2(region string) (string, error) { + ctx := context.TODO() + resolver := ec2.NewDefaultEndpointResolverV2() + ep, err := resolver.ResolveEndpoint(ctx, ec2.EndpointParameters{Region: aws.String(region)}) + if err != nil { + return "", fmt.Errorf("failed to resolve endpoint: %v", err) } - return "ec2.amazonaws.com" + if ep.URI.Host != "" { + // Remove the region from the hostname. Examples: + // ec2.us-east-1.amazonaws.com -> ec2.amazonaws.com + // ec2.cn-west-1.amazonaws.com.cn -> ec2.amazonaws.com.cn + // ec2.us-gov-west-1.amazonaws.com -> ec2.amazonaws.com + return strings.ReplaceAll(ep.URI.Host, fmt.Sprintf("%v.", region), ""), nil + } + return "ec2.amazonaws.com", nil } func formatAWSIAMStatement(accountId, partition, oidcProvider, namespace, name string) (*iam.Statement, error) { @@ -466,7 +474,11 @@ func (b *IAMModelBuilder) buildAWSIAMRolePolicy(role iam.Subject) (fi.Resource, } else { // We don't generate using json.Marshal here, it would create whitespace changes in the policy for existing clusters. - policy = strings.ReplaceAll(NodeRolePolicyTemplate, "{{ IAMServiceEC2 }}", IAMServiceEC2(b.Region)) + ec2Service, err := IAMServiceEC2(b.Region) + if err != nil { + return nil, err + } + policy = strings.ReplaceAll(NodeRolePolicyTemplate, "{{ IAMServiceEC2 }}", ec2Service) } return fi.NewStringResource(policy), nil diff --git a/pkg/model/awsmodel/iam_test.go b/pkg/model/awsmodel/iam_test.go index a8fa453656..dcb8e01182 100644 --- a/pkg/model/awsmodel/iam_test.go +++ b/pkg/model/awsmodel/iam_test.go @@ -34,7 +34,10 @@ func TestIAMServiceEC2(t *testing.T) { } for region, expect := range expectations { - principal := IAMServiceEC2(region) + principal, err := IAMServiceEC2(region) + if err != nil { + t.Errorf("unexpected error: %v", err) + } if principal != expect { t.Errorf("expected %s for %s, but received %s", expect, region, principal) } diff --git a/pkg/zones/wellknown.go b/pkg/zones/wellknown.go index c84f264bdc..1908b749a0 100644 --- a/pkg/zones/wellknown.go +++ b/pkg/zones/wellknown.go @@ -17,16 +17,10 @@ limitations under the License. package zones import ( - "context" "sort" - "strings" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/scaleway/scaleway-sdk-go/scw" "k8s.io/kops/pkg/apis/kops" - "k8s.io/kops/upup/pkg/fi/cloudup/awsup" ) // These lists allow us to infer from certain well-known zones to a cloud @@ -170,6 +164,116 @@ var hetznerZones = []string{ "hil", } +var awsZones = []string{ + "af-south-1a", + "af-south-1b", + "af-south-1c", + "ap-east-1a", + "ap-east-1b", + "ap-east-1c", + "ap-northeast-1a", + "ap-northeast-1b", + "ap-northeast-1c", + "ap-northeast-1d", + "ap-northeast-2a", + "ap-northeast-2b", + "ap-northeast-2c", + "ap-northeast-2d", + "ap-northeast-3a", + "ap-northeast-3b", + "ap-northeast-3c", + "ap-south-1a", + "ap-south-1b", + "ap-south-1c", + "ap-south-2a", + "ap-south-2b", + "ap-south-2c", + "ap-southeast-1a", + "ap-southeast-1b", + "ap-southeast-1c", + "ap-southeast-2a", + "ap-southeast-2b", + "ap-southeast-2c", + "ap-southeast-3a", + "ap-southeast-3b", + "ap-southeast-3c", + "ap-southeast-4a", + "ap-southeast-4b", + "ap-southeast-4c", + "ca-central-1a", + "ca-central-1b", + "ca-central-1c", + "ca-west-1a", + "ca-west-1b", + "ca-west-1c", + "eu-central-1a", + "eu-central-1b", + "eu-central-1c", + "eu-central-2a", + "eu-central-2b", + "eu-central-2c", + "eu-north-1a", + "eu-north-1b", + "eu-north-1c", + "eu-south-1a", + "eu-south-1b", + "eu-south-1c", + "eu-south-2a", + "eu-south-2b", + "eu-south-2c", + "eu-west-1a", + "eu-west-1b", + "eu-west-1c", + "eu-west-2a", + "eu-west-2b", + "eu-west-2c", + "eu-west-3a", + "eu-west-3b", + "eu-west-3c", + "il-central-1a", + "il-central-1b", + "il-central-1c", + "me-central-1a", + "me-central-1b", + "me-central-1c", + "me-south-1a", + "me-south-1b", + "me-south-1c", + "sa-east-1a", + "sa-east-1b", + "sa-east-1c", + "us-east-1a", + "us-east-1b", + "us-east-1c", + "us-east-1d", + "us-east-1e", + "us-east-1f", + "us-east-2a", + "us-east-2b", + "us-east-2c", + "us-west-1a", + "us-west-1b", + "us-west-1c", + "us-west-2a", + "us-west-2b", + "us-west-2c", + "us-west-2d", + + "us-gov-east-1a", + "us-gov-east-1b", + "us-gov-east-1c", + "us-gov-west-1a", + "us-gov-west-1b", + "us-gov-west-1c", + + "cn-north-1a", + "cn-north-1b", + "cn-north-1c", + "cn-northwest-1a", + "cn-northwest-1b", + "cn-northwest-1c", +} + var azureZones = []string{ "asia", "asiapacific", @@ -245,45 +349,10 @@ func scwZones() []string { } func WellKnownZonesForCloud(matchCloud kops.CloudProviderID, prefix string) []string { - ctx := context.Background() - var found []string switch matchCloud { case kops.CloudProviderAWS: - prefix = strings.ToLower(prefix) - for _, partition := range endpoints.DefaultResolver().(endpoints.EnumPartitions).Partitions() { - for _, region := range partition.Regions() { - regionName := strings.ToLower(region.ID()) - if prefix == regionName || strings.HasPrefix(prefix, regionName+"-") { - // If the prefix is a region name or a Local Zone or a Wavelength Zone, - // return all its matching zones as the completion options. - awsCloud, err := awsup.NewAWSCloud(regionName, map[string]string{}) - if err != nil { - continue - } - var zones *ec2.DescribeAvailabilityZonesOutput - zones, err = awsCloud.EC2().DescribeAvailabilityZones(ctx, &ec2.DescribeAvailabilityZonesInput{ - AllAvailabilityZones: aws.Bool(true), - }) - if err != nil { - continue - } - for _, zone := range zones.AvailabilityZones { - found = append(found, *zone.ZoneName) - } - } else if strings.HasPrefix(regionName, prefix) { - // Return the region name as the completion option. After the user completes - // that much, the code will then look up the specific zone options. - found = append(found, regionName) - } else { - // If the zone name is in the form of single-letter zones - // belonging to a region, that's good enough. - if len(prefix) == len(regionName)+1 && strings.HasPrefix(prefix, regionName) { - found = append(found, prefix) - } - } - } - } + found = awsZones case kops.CloudProviderAzure: found = azureZones case kops.CloudProviderDO: diff --git a/upup/pkg/fi/cloudup/awsup/aws_utils.go b/upup/pkg/fi/cloudup/awsup/aws_utils.go index b119300f5a..47d1aadf84 100644 --- a/upup/pkg/fi/cloudup/awsup/aws_utils.go +++ b/upup/pkg/fi/cloudup/awsup/aws_utils.go @@ -34,7 +34,6 @@ import ( elbtypes "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancing/types" elbv2types "github.com/aws/aws-sdk-go-v2/service/elasticloadbalancingv2/types" iamtypes "github.com/aws/aws-sdk-go-v2/service/iam/types" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/smithy-go" "k8s.io/klog/v2" "k8s.io/kops/pkg/apis/kops" @@ -156,9 +155,6 @@ func FindELBV2Tag(tags []elbv2types.Tag, key string) (string, bool) { // AWSErrorCode returns the aws error code, if it is an awserr.Error or smithy.APIError, otherwise "" func AWSErrorCode(err error) string { - if awsError, ok := err.(awserr.Error); ok { - return awsError.Code() - } var apiErr smithy.APIError if errors.As(err, &apiErr) { return apiErr.ErrorCode() @@ -168,9 +164,6 @@ func AWSErrorCode(err error) string { // AWSErrorMessage returns the aws error message, if it is an awserr.Error or smithy.APIError, otherwise "" func AWSErrorMessage(err error) string { - if awsError, ok := err.(awserr.Error); ok { - return awsError.Message() - } var apiErr smithy.APIError if errors.As(err, &apiErr) { return apiErr.ErrorMessage()