From 054921802405c0e9bf70d40c229a05e8437858e6 Mon Sep 17 00:00:00 2001 From: Yu Xia Date: Mon, 3 Jun 2024 11:42:55 -0700 Subject: [PATCH] Update mrn tcld with region validation (#348) --- app/namespace.go | 15 +++++++++-- utils/region.go | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 utils/region.go diff --git a/app/namespace.go b/app/namespace.go index 7f5c07c..54c9bf1 100644 --- a/app/namespace.go +++ b/app/namespace.go @@ -15,6 +15,7 @@ import ( "github.com/temporalio/tcld/protogen/api/auth/v1" "github.com/temporalio/tcld/protogen/api/cloud/cloudservice/v1" "github.com/temporalio/tcld/protogen/api/sink/v1" + "github.com/temporalio/tcld/utils" "github.com/kylelemons/godebug/diff" "github.com/urfave/cli/v2" @@ -298,9 +299,14 @@ func (c *NamespaceClient) addRegion(ctx *cli.Context) error { return fmt.Errorf("namespace cloud provider is required") } + targetRegion := fmt.Sprintf("%s-%s", cloudProvider, region) + if err := utils.ValidateCloudProviderAndRegion(targetRegion); err != nil { + return err + } + res, err := c.cloudAPIClient.AddNamespaceRegion(c.ctx, &cloudservice.AddNamespaceRegionRequest{ Namespace: ctx.String(NamespaceFlagName), - Region: fmt.Sprintf("%s-%s", cloudProvider, region), + Region: targetRegion, ResourceVersion: ns.GetResourceVersion(), AsyncOperationId: ctx.String(RequestIDFlagName), }) @@ -448,9 +454,14 @@ func (c *NamespaceClient) failoverNamespace(ctx *cli.Context) error { return fmt.Errorf("cloud provider is required") } + targetRegion := fmt.Sprintf("%s-%s", cloudProvider, region) + if err := utils.ValidateCloudProviderAndRegion(targetRegion); err != nil { + return err + } + res, err := c.cloudAPIClient.FailoverNamespaceRegion(c.ctx, &cloudservice.FailoverNamespaceRegionRequest{ Namespace: namespace, - Region: fmt.Sprintf("%s-%s", cloudProvider, ctx.String(namespaceRegionFlagName)), + Region: targetRegion, AsyncOperationId: ctx.String(RequestIDFlagName), }) if err != nil { diff --git a/utils/region.go b/utils/region.go new file mode 100644 index 0000000..388e5b4 --- /dev/null +++ b/utils/region.go @@ -0,0 +1,68 @@ +package utils + +import ( + "fmt" + "strings" +) + +const ( + providerAWS = "aws" + providerGCP = "gcp" +) + +// compiled from https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html#concepts-available-regions +var awsRegionToLocationMap = map[string]string{ + "af-south-1": "Africa (Cape Town)", + "ap-east-1": "Asia Pacific (Hong Kong)", + "ap-northeast-1": "Asia Pacific (Tokyo)", + "ap-northeast-2": "Asia Pacific (Seoul)", + "ap-northeast-3": "Asia Pacific (Osaka)", + "ap-south-1": "Asia Pacific (Mumbai)", + "ap-south-2": "Asia Pacific (Hyderabad)", + "ap-southeast-1": "Asia Pacific (Singapore)", + "ap-southeast-2": "Asia Pacific (Sydney)", + "ap-southeast-3": "Asia Pacific (Jakarta)", + "ap-southeast-4": "Asia Pacific (Melbourne)", + "ca-central-1": "Canada (Central)", + "eu-central-1": "Europe (Frankfurt)", + "eu-central-2": "Europe (Zurich)", + "eu-north-1": "Europe (Stockholm)", + "eu-south-1": "Europe (Milan)", + "eu-south-2": "Europe (Spain)", + "eu-west-1": "Europe (Ireland)", + "eu-west-2": "Europe (London)", + "eu-west-3": "Europe (Paris)", + "il-central-1": "Israel (Tel Aviv)", + "me-central-1": "Middle East (UAE)", + "me-south-1": "Middle East (Bahrain)", + "sa-east-1": "South America (Sao Paulo)", + "us-east-1": "US East (N. Virginia)", + "us-east-2": "US East (Ohio)", + "us-west-1": "US West (N. California)", + "us-west-2": "US West (Oregon)", +} + +func ValidateCloudProviderAndRegion(region string) error { + switch { + case strings.HasPrefix(region, providerAWS+"-"): + awsRegion := region[len(providerAWS)+1:] + if _, ok := AWSLocationFromRegion(awsRegion); !ok { + return fmt.Errorf("invalid aws region: %s", region) + } + return nil + case strings.HasPrefix(region, providerGCP+"-"): + gcpRegion := region[len(providerGCP)+1:] + if len(gcpRegion) == 0 { + return fmt.Errorf("invalid google cloud region: %s", region) + } + // TODO: validate GCP region + return nil + default: + return fmt.Errorf("invalid region format: %s, required: -", region) + } +} + +func AWSLocationFromRegion(region string) (string, bool) { + location, ok := awsRegionToLocationMap[region] + return location, ok +}