diff --git a/config/crds/hive_v1_dnszone.yaml b/config/crds/hive_v1_dnszone.yaml index 42fa338096e..6f32ca2f3a4 100644 --- a/config/crds/hive_v1_dnszone.yaml +++ b/config/crds/hive_v1_dnszone.yaml @@ -52,6 +52,10 @@ spec: description: CredentialsSecretRef contains a reference to a secret that contains AWS credentials for CRUD operations type: object + region: + description: Region is the AWS region to use for route53 operations. + This defaults to us-east-1. For AWS China, use cn-northwest-1. + type: string type: object gcp: description: GCP specifies GCP-specific cloud configuration diff --git a/config/crds/hive_v1_hiveconfig.yaml b/config/crds/hive_v1_hiveconfig.yaml index 68e416b447f..89c27992af1 100644 --- a/config/crds/hive_v1_hiveconfig.yaml +++ b/config/crds/hive_v1_hiveconfig.yaml @@ -115,6 +115,10 @@ spec: parent ManageDNSConfig object. Secret should have AWS keys named 'aws_access_key_id' and 'aws_secret_access_key'. type: object + region: + description: Region is the AWS region to use for route53 operations. + This defaults to us-east-1. For AWS China, use cn-northwest-1. + type: string type: object domains: description: Domains is the list of domains that hive will be diff --git a/pkg/apis/hive/v1/dnszone_types.go b/pkg/apis/hive/v1/dnszone_types.go index 0abae3083b0..41bcd32cafd 100644 --- a/pkg/apis/hive/v1/dnszone_types.go +++ b/pkg/apis/hive/v1/dnszone_types.go @@ -45,6 +45,12 @@ type AWSDNSZoneSpec struct { // to these tags,the DNS Zone controller will set a hive.openhsift.io/hostedzone tag // identifying the HostedZone record that it belongs to. AdditionalTags []AWSResourceTag `json:"additionalTags,omitempty"` + + // Region is the AWS region to use for route53 operations. + // This defaults to us-east-1. + // For AWS China, use cn-northwest-1. + // +optional + Region string `json:"region,omitempty"` } // AWSResourceTag represents a tag that is applied to an AWS cloud resource diff --git a/pkg/apis/hive/v1/hiveconfig_types.go b/pkg/apis/hive/v1/hiveconfig_types.go index 061953b3218..7e936dccee9 100644 --- a/pkg/apis/hive/v1/hiveconfig_types.go +++ b/pkg/apis/hive/v1/hiveconfig_types.go @@ -129,6 +129,12 @@ type ManageDNSAWSConfig struct { // Secret should have AWS keys named 'aws_access_key_id' and 'aws_secret_access_key'. // +optional CredentialsSecretRef corev1.LocalObjectReference `json:"credentialsSecretRef,omitempty"` + + // Region is the AWS region to use for route53 operations. + // This defaults to us-east-1. + // For AWS China, use cn-northwest-1. + // +optional + Region string `json:"region,omitempty"` } // ManageDNSGCPConfig contains GCP-specific info to manage a given domain. diff --git a/pkg/awsclient/client.go b/pkg/awsclient/client.go index 241403b7ada..360cd943c09 100644 --- a/pkg/awsclient/client.go +++ b/pkg/awsclient/client.go @@ -14,6 +14,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" @@ -27,6 +28,8 @@ import ( "github.com/aws/aws-sdk-go/service/route53/route53iface" "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3iface" + + "github.com/openshift/hive/pkg/constants" ) const ( @@ -52,7 +55,7 @@ func init() { // Client is a wrapper object for actual AWS SDK clients to allow for easier testing. type Client interface { - //EC2 + // EC2 DescribeAvailabilityZones(*ec2.DescribeAvailabilityZonesInput) (*ec2.DescribeAvailabilityZonesOutput, error) DescribeImages(*ec2.DescribeImagesInput) (*ec2.DescribeImagesOutput, error) DescribeVpcs(*ec2.DescribeVpcsInput) (*ec2.DescribeVpcsOutput, error) @@ -62,10 +65,10 @@ type Client interface { DescribeInstances(*ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) TerminateInstances(*ec2.TerminateInstancesInput) (*ec2.TerminateInstancesOutput, error) - //ELB + // ELB RegisterInstancesWithLoadBalancer(*elb.RegisterInstancesWithLoadBalancerInput) (*elb.RegisterInstancesWithLoadBalancerOutput, error) - //IAM + // IAM CreateAccessKey(*iam.CreateAccessKeyInput) (*iam.CreateAccessKeyOutput, error) CreateUser(*iam.CreateUserInput) (*iam.CreateUserOutput, error) DeleteAccessKey(*iam.DeleteAccessKeyInput) (*iam.DeleteAccessKeyOutput, error) @@ -76,15 +79,15 @@ type Client interface { ListUserPolicies(*iam.ListUserPoliciesInput) (*iam.ListUserPoliciesOutput, error) PutUserPolicy(*iam.PutUserPolicyInput) (*iam.PutUserPolicyOutput, error) - //S3 + // S3 CreateBucket(*s3.CreateBucketInput) (*s3.CreateBucketOutput, error) DeleteBucket(*s3.DeleteBucketInput) (*s3.DeleteBucketOutput, error) ListBuckets(*s3.ListBucketsInput) (*s3.ListBucketsOutput, error) - //Custom + // Custom GetS3API() s3iface.S3API - //Route53 + // Route53 CreateHostedZone(input *route53.CreateHostedZoneInput) (*route53.CreateHostedZoneOutput, error) GetHostedZone(*route53.GetHostedZoneInput) (*route53.GetHostedZoneOutput, error) ListTagsForResource(*route53.ListTagsForResourceInput) (*route53.ListTagsForResourceOutput, error) @@ -300,7 +303,10 @@ func NewClient(kubeClient client.Client, secretName, namespace, region string) ( // // Pass a nil secret to load credentials from the standard AWS environment variables. func NewClientFromSecret(secret *corev1.Secret, region string) (Client, error) { - awsConfig := &aws.Config{Region: aws.String(region)} + awsConfig := &aws.Config{ + Region: aws.String(region), + EndpointResolver: endpoints.ResolverFunc(awsChinaEndpointResolver), + } // Special case to not use a secret to gather credentials. if secret != nil { @@ -339,3 +345,14 @@ func NewClientFromSecret(secret *corev1.Secret, region string) (Client, error) { tagClient: resourcegroupstaggingapi.New(s), }, nil } + +func awsChinaEndpointResolver(service, region string, optFns ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) { + if service != route53.EndpointsID || region != constants.AWSChinaRoute53Region { + return endpoints.DefaultResolver().EndpointFor(service, region, optFns...) + } + + return endpoints.ResolvedEndpoint{ + URL: "https://route53.amazonaws.com.cn", + PartitionID: "aws-cn", + }, nil +} diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index 66dce05534d..6989a07b443 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -140,6 +140,15 @@ const ( // PasswordSecretKey is a key used to store a password inside of a secret containing username / password credentials PasswordSecretKey = "password" + + // AWSRoute53Region is the region to use for route53 operations. + AWSRoute53Region = "us-east-1" + + // AWSChinaRoute53Region is the region to use for AWS China route53 operations. + AWSChinaRoute53Region = "cn-northwest-1" + + // AWSChinaRegionPrefix is the prefix for regions in AWS China. + AWSChinaRegionPrefix = "cn-" ) // GetMergedPullSecretName returns name for merged pull secret name per cluster deployment diff --git a/pkg/controller/clusterdeployment/clusterdeployment_controller.go b/pkg/controller/clusterdeployment/clusterdeployment_controller.go index 5547b92ab83..ebcb9d70f1d 100644 --- a/pkg/controller/clusterdeployment/clusterdeployment_controller.go +++ b/pkg/controller/clusterdeployment/clusterdeployment_controller.go @@ -6,6 +6,7 @@ import ( "os" "reflect" "sort" + "strings" "time" "github.com/pkg/errors" @@ -1358,9 +1359,14 @@ func (r *ReconcileClusterDeployment) createManagedDNSZone(cd *hivev1.ClusterDepl for k, v := range cd.Spec.Platform.AWS.UserTags { additionalTags = append(additionalTags, hivev1.AWSResourceTag{Key: k, Value: v}) } + region := "" + if strings.HasPrefix(cd.Spec.Platform.AWS.Region, constants.AWSChinaRegionPrefix) { + region = constants.AWSChinaRoute53Region + } dnsZone.Spec.AWS = &hivev1.AWSDNSZoneSpec{ CredentialsSecretRef: cd.Spec.Platform.AWS.CredentialsSecretRef, AdditionalTags: additionalTags, + Region: region, } case cd.Spec.Platform.GCP != nil: dnsZone.Spec.GCP = &hivev1.GCPDNSZoneSpec{ diff --git a/pkg/controller/dnsendpoint/dnsendpoint_controller.go b/pkg/controller/dnsendpoint/dnsendpoint_controller.go index df665974256..5646c1dbae5 100644 --- a/pkg/controller/dnsendpoint/dnsendpoint_controller.go +++ b/pkg/controller/dnsendpoint/dnsendpoint_controller.go @@ -22,6 +22,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" hivev1 "github.com/openshift/hive/pkg/apis/hive/v1" + "github.com/openshift/hive/pkg/constants" "github.com/openshift/hive/pkg/controller/dnsendpoint/nameserver" hivemetrics "github.com/openshift/hive/pkg/controller/metrics" controllerutils "github.com/openshift/hive/pkg/controller/utils" @@ -277,7 +278,11 @@ func createNameServerQuery(c client.Client, logger log.FieldLogger, managedDomai if managedDomain.AWS != nil { secretName := managedDomain.AWS.CredentialsSecretRef.Name logger.Infof("using aws creds for managed domains stored in %q secret", secretName) - return nameserver.NewAWSQuery(c, secretName) + region := managedDomain.AWS.Region + if region == "" { + region = constants.AWSRoute53Region + } + return nameserver.NewAWSQuery(c, secretName, region) } if managedDomain.GCP != nil { secretName := managedDomain.GCP.CredentialsSecretRef.Name diff --git a/pkg/controller/dnsendpoint/nameserver/aws.go b/pkg/controller/dnsendpoint/nameserver/aws.go index 9473ff9e223..aa03a655dd9 100644 --- a/pkg/controller/dnsendpoint/nameserver/aws.go +++ b/pkg/controller/dnsendpoint/nameserver/aws.go @@ -17,10 +17,10 @@ import ( ) // NewAWSQuery creates a new name server query for AWS. -func NewAWSQuery(c client.Client, credsSecretName string) Query { +func NewAWSQuery(c client.Client, credsSecretName string, region string) Query { return &awsQuery{ getAWSClient: func() (awsclient.Client, error) { - awsClient, err := awsclient.NewClient(c, credsSecretName, constants.HiveNamespace, "us-east-1") + awsClient, err := awsclient.NewClient(c, credsSecretName, constants.HiveNamespace, region) return awsClient, errors.Wrap(err, "error creating AWS client") }, } diff --git a/pkg/controller/dnszone/awsactuator.go b/pkg/controller/dnszone/awsactuator.go index de10c2075ec..04d0f7d1a12 100644 --- a/pkg/controller/dnszone/awsactuator.go +++ b/pkg/controller/dnszone/awsactuator.go @@ -17,11 +17,11 @@ import ( hivev1 "github.com/openshift/hive/pkg/apis/hive/v1" awsclient "github.com/openshift/hive/pkg/awsclient" + "github.com/openshift/hive/pkg/constants" ) const ( - hiveDNSZoneAWSTag = "hive.openshift.io/dnszone" - defaultRegionEndpoint = "us-east-1" + hiveDNSZoneAWSTag = "hive.openshift.io/dnszone" ) // Ensure AWSActuator implements the Actuator interface. This will fail at compile time when false. @@ -54,9 +54,11 @@ func NewAWSActuator( dnsZone *hivev1.DNSZone, awsClientBuilder awsClientBuilderType, ) (*AWSActuator, error) { - // Route53 is a regionless service, we specify a default region just for the purpose of creating - // our client configuration. - awsClient, err := awsClientBuilder(secret, defaultRegionEndpoint) + region := dnsZone.Spec.AWS.Region + if region == "" { + region = constants.AWSRoute53Region + } + awsClient, err := awsClientBuilder(secret, region) if err != nil { logger.WithError(err).Error("Error creating AWSClient") return nil, err diff --git a/pkg/operator/assets/bindata.go b/pkg/operator/assets/bindata.go index b2f73c689f0..ead0b3af81a 100644 --- a/pkg/operator/assets/bindata.go +++ b/pkg/operator/assets/bindata.go @@ -2859,6 +2859,10 @@ spec: description: CredentialsSecretRef contains a reference to a secret that contains AWS credentials for CRUD operations type: object + region: + description: Region is the AWS region to use for route53 operations. + This defaults to us-east-1. For AWS China, use cn-northwest-1. + type: string type: object gcp: description: GCP specifies GCP-specific cloud configuration @@ -3083,6 +3087,10 @@ spec: parent ManageDNSConfig object. Secret should have AWS keys named 'aws_access_key_id' and 'aws_secret_access_key'. type: object + region: + description: Region is the AWS region to use for route53 operations. + This defaults to us-east-1. For AWS China, use cn-northwest-1. + type: string type: object domains: description: Domains is the list of domains that hive will be