Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 30 additions & 17 deletions cloudsupport/v1/ekssupport.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"net/url"
"os"
"regexp"
"strings"

"github.com/aws/aws-sdk-go-v2/aws"
Expand Down Expand Up @@ -35,6 +36,12 @@ type IEKSSupport interface {
type EKSSupport struct {
}

var (
awsRegionPattern = regexp.MustCompile(`^[a-z]{2}(?:-[a-z]+)?-[a-z]+-\d+$`)
eksArnRegionPattern = regexp.MustCompile(`^arn:[^:]+:eks:([a-z]{2}(?:-[a-z]+)?-[a-z]+-\d+):`)
eksDashedArnRegionPattern = regexp.MustCompile(`^arn-(?:[a-z]+-)+eks-([a-z]{2}(?:-[a-z]+)?-[a-z]+-\d+)-`)
)

const (
awsauthconfigmap = "aws-auth"
)
Expand Down Expand Up @@ -114,28 +121,34 @@ func (eksSupport *EKSSupport) GetName(describe *eks.DescribeClusterOutput) strin
// GetRegion returns the region in which eks cluster is running.
func (eksSupport *EKSSupport) GetRegion(cluster string) (string, error) {
region, present := os.LookupEnv(KS_CLOUD_REGION_ENV_VAR)
if present {
if present && region != "" {
return region, nil
}

if matches := eksArnRegionPattern.FindStringSubmatch(cluster); len(matches) == 2 {
return matches[1], nil
}

if matches := eksDashedArnRegionPattern.FindStringSubmatch(cluster); len(matches) == 2 {
return matches[1], nil
}

splittedClusterContext := strings.Split(cluster, ".")
if len(splittedClusterContext) >= 2 && awsRegionPattern.MatchString(splittedClusterContext[1]) {
return splittedClusterContext[1], nil
}

if len(splittedClusterContext) < 2 {
splittedClusterContext := strings.Split(cluster, ":")
if len(splittedClusterContext) < 4 {
splittedClusterContext := strings.Split(cluster, "-")
if len(splittedClusterContext) < 4 {
return "", fmt.Errorf("failed to get region")
} else if len(splittedClusterContext) >= 6 {
return strings.Join(splittedClusterContext[3:6], "-"), nil
} else {
return "", fmt.Errorf("failed to get region")
}
}
region = splittedClusterContext[3]
} else {
region = splittedClusterContext[1]
region, present = os.LookupEnv("AWS_REGION")
if present && region != "" {
return region, nil
}
return region, nil

awsConfig, err := config.LoadDefaultConfig(context.TODO())
if err == nil && awsConfig.Region != "" {
return awsConfig.Region, nil
}

return "", fmt.Errorf("failed to get region: tried KS_CLOUD_REGION, cluster name parsing, AWS_REGION, and AWS config")
}

// Context can be in one of 3 ways:
Expand Down
207 changes: 205 additions & 2 deletions cloudsupport/v1/ekssupport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1

import (
"os"
"path/filepath"
"strings"
"testing"

Expand All @@ -10,7 +11,59 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

func setEnv(t *testing.T, key, value string) {
t.Helper()

previousValue, hadPreviousValue := os.LookupEnv(key)
if value == "" {
if err := os.Unsetenv(key); err != nil {
t.Fatalf("unset %s: %v", key, err)
}
} else {
if err := os.Setenv(key, value); err != nil {
t.Fatalf("set %s: %v", key, err)
}
}

t.Cleanup(func() {
var err error
if hadPreviousValue {
err = os.Setenv(key, previousValue)
} else {
err = os.Unsetenv(key)
}

if err != nil {
t.Fatalf("restore %s: %v", key, err)
}
})
}

func isolateAWSRegionSources(t *testing.T) {
t.Helper()

configPath := filepath.Join(t.TempDir(), "config")
credentialsPath := filepath.Join(t.TempDir(), "credentials")

if err := os.WriteFile(configPath, nil, 0o600); err != nil {
t.Fatalf("write %s: %v", configPath, err)
}
if err := os.WriteFile(credentialsPath, nil, 0o600); err != nil {
t.Fatalf("write %s: %v", credentialsPath, err)
}

setEnv(t, KS_CLOUD_REGION_ENV_VAR, "")
setEnv(t, "AWS_REGION", "")
setEnv(t, "AWS_DEFAULT_REGION", "")
setEnv(t, "AWS_PROFILE", "")
setEnv(t, "AWS_EC2_METADATA_DISABLED", "true")
setEnv(t, "AWS_CONFIG_FILE", configPath)
setEnv(t, "AWS_SHARED_CREDENTIALS_FILE", credentialsPath)
}

func TestGetContextName(t *testing.T) {
isolateAWSRegionSources(t)

defer tearDown()

// Test ARN context names
Expand Down Expand Up @@ -134,9 +187,10 @@ func TestGetRegion(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isolateAWSRegionSources(t)

if tt.envRegion != "" {
os.Setenv(KS_CLOUD_REGION_ENV_VAR, tt.envRegion)
defer os.Unsetenv(KS_CLOUD_REGION_ENV_VAR)
setEnv(t, KS_CLOUD_REGION_ENV_VAR, tt.envRegion)
}

eksSupport := &EKSSupport{}
Expand All @@ -150,4 +204,153 @@ func TestGetRegion(t *testing.T) {
}
})
}

t.Run("AWS_REGION environment variable is checked", func(t *testing.T) {
isolateAWSRegionSources(t)

awsRegion := "ap-southeast-2"
setEnv(t, "AWS_REGION", awsRegion)

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion("my-cluster")

assert.NoError(t, err)
assert.Equal(t, awsRegion, region)
})

t.Run("KS_CLOUD_REGION takes precedence over AWS_REGION", func(t *testing.T) {
isolateAWSRegionSources(t)

ksRegion := "us-west-2"
awsRegion := "eu-west-1"
setEnv(t, KS_CLOUD_REGION_ENV_VAR, ksRegion)
setEnv(t, "AWS_REGION", awsRegion)

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion("my-cluster")

assert.NoError(t, err)
assert.Equal(t, ksRegion, region)
})

t.Run("KS_CLOUD_REGION takes precedence over cluster name parsing", func(t *testing.T) {
isolateAWSRegionSources(t)
setEnv(t, KS_CLOUD_REGION_ENV_VAR, "us-west-2")

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion("arn:aws:eks:eu-north-1:123456789:cluster/test-cluster")

assert.NoError(t, err)
assert.Equal(t, "us-west-2", region)
})

t.Run("Cluster name parsing takes precedence over AWS default config", func(t *testing.T) {
isolateAWSRegionSources(t)
setEnv(t, "AWS_DEFAULT_REGION", "us-east-1")

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion("arn:aws:eks:eu-north-1:123456789:cluster/test-cluster")

assert.NoError(t, err)
assert.Equal(t, "eu-north-1", region)
})

t.Run("Cluster name parsing takes precedence over AWS_REGION", func(t *testing.T) {
isolateAWSRegionSources(t)
setEnv(t, "AWS_REGION", "us-east-1")

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion("arn:aws:eks:eu-north-1:123456789:cluster/test-cluster")

assert.NoError(t, err)
assert.Equal(t, "eu-north-1", region)
})

t.Run("Partitioned ARN parsing takes precedence over AWS_REGION", func(t *testing.T) {
tests := []struct {
name string
cluster string
region string
}{
{
name: "GovCloud standard ARN",
cluster: "arn:aws-us-gov:eks:us-gov-west-1:123456789012:cluster/test-cluster",
region: "us-gov-west-1",
},
{
name: "China standard ARN",
cluster: "arn:aws-cn:eks:cn-north-1:123456789012:cluster/test-cluster",
region: "cn-north-1",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
isolateAWSRegionSources(t)
setEnv(t, "AWS_REGION", "us-east-1")

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion(tt.cluster)

assert.NoError(t, err)
assert.Equal(t, tt.region, region)
})
}
})

t.Run("Dashed ARN parsing supports GovCloud regions", func(t *testing.T) {
isolateAWSRegionSources(t)
setEnv(t, "AWS_REGION", "us-east-1")

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion("arn-aws-us-gov-eks-us-gov-west-1-123456789012-cluster-test-cluster")

assert.NoError(t, err)
assert.Equal(t, "us-gov-west-1", region)
})

t.Run("Valid dotted cluster name takes precedence over AWS fallbacks", func(t *testing.T) {
isolateAWSRegionSources(t)
setEnv(t, "AWS_REGION", "us-east-1")
setEnv(t, "AWS_DEFAULT_REGION", "ap-southeast-1")

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion("cluster.us-west-2.eksctl.io")

assert.NoError(t, err)
assert.Equal(t, "us-west-2", region)
})

t.Run("Invalid dotted cluster name falls back to AWS_REGION", func(t *testing.T) {
isolateAWSRegionSources(t)
setEnv(t, "AWS_REGION", "us-west-2")

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion("foo.bar")

assert.NoError(t, err)
assert.Equal(t, "us-west-2", region)
})

t.Run("Invalid parsed region falls back to AWS default config", func(t *testing.T) {
isolateAWSRegionSources(t)
setEnv(t, "AWS_DEFAULT_REGION", "ap-southeast-1")

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion("arn:aws:eks:not-a-region:123456789:cluster/test-cluster")

assert.NoError(t, err)
assert.Equal(t, "ap-southeast-1", region)
})

t.Run("AWS default config is used after cluster parsing fails", func(t *testing.T) {
isolateAWSRegionSources(t)
setEnv(t, "AWS_DEFAULT_REGION", "ap-southeast-1")

eksSupport := &EKSSupport{}
region, err := eksSupport.GetRegion("my-cluster")

assert.NoError(t, err)
assert.Equal(t, "ap-southeast-1", region)
})
}
Loading