github.com/jenkins-x/jx/v2@v2.1.155/pkg/cloud/amazon/session/common.go (about)

     1  package session
     2  
     3  import (
     4  	"os"
     5  	"regexp"
     6  	"runtime"
     7  
     8  	"github.com/aws/aws-sdk-go/aws"
     9  	"github.com/aws/aws-sdk-go/aws/session"
    10  	"github.com/aws/aws-sdk-go/service/eks"
    11  	"github.com/jenkins-x/jx/v2/pkg/kube"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  const DefaultRegion = "us-west-2"
    16  
    17  func NewAwsSession(profileOption string, regionOption string) (*session.Session, error) {
    18  	config := aws.Config{}
    19  	if regionOption != "" {
    20  		config.Region = aws.String(regionOption)
    21  	}
    22  
    23  	sessionOptions := session.Options{
    24  		SharedConfigState: session.SharedConfigEnable,
    25  		Config:            config,
    26  	}
    27  
    28  	if profileOption != "" {
    29  		sessionOptions.Profile = profileOption
    30  	}
    31  
    32  	awsSession, err := session.NewSessionWithOptions(sessionOptions)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	if *awsSession.Config.Region == "" {
    38  		awsSession.Config.Region = aws.String(DefaultRegion)
    39  	}
    40  
    41  	return awsSession, nil
    42  }
    43  
    44  func NewAwsSessionWithoutOptions() (*session.Session, error) {
    45  	return NewAwsSession("", "")
    46  }
    47  
    48  func ResolveRegion(profileOption string, regionOption string) (string, error) {
    49  	session, err := NewAwsSession(profileOption, regionOption)
    50  	if err != nil {
    51  		return "", err
    52  	}
    53  	return *session.Config.Region, nil
    54  }
    55  
    56  func ResolveRegionWithoutOptions() (string, error) {
    57  	return ResolveRegion("", "")
    58  }
    59  
    60  // GetClusterNameAndRegionFromAWS uses the AWS SDK to parse through each EKS cluster until it finds one that matches the endpoint in
    61  // the kubeconfig. From there it will retrieve the cluster name
    62  func GetClusterNameAndRegionFromAWS(profileOption string, regionOption string, kubeEndpoint string) (string, string, error) {
    63  	session, err := NewAwsSession(profileOption, regionOption)
    64  	if err != nil {
    65  		return "", "", errors.Wrapf(err, "Error creating AWS Session")
    66  	}
    67  	svc := eks.New(session)
    68  
    69  	input := &eks.ListClustersInput{}
    70  	result, err := svc.ListClusters(input)
    71  	if err != nil {
    72  		return "", "", errors.Wrapf(err, "Error calling Eks List Clusters")
    73  	}
    74  
    75  	for _, cluster := range result.Clusters {
    76  		input := &eks.DescribeClusterInput{
    77  			Name: aws.String(*cluster),
    78  		}
    79  		result, err := svc.DescribeCluster(input)
    80  		if err != nil {
    81  			return "", "", errors.Wrapf(err, "Error calling Describe Cluster on "+*cluster)
    82  		}
    83  
    84  		if *result.Cluster.Endpoint == kubeEndpoint {
    85  			return *result.Cluster.Name, *session.Config.Region, nil
    86  		}
    87  	}
    88  
    89  	return "", "", errors.Errorf("Unable to get cluster name from AWS")
    90  }
    91  
    92  // ParseContext parses the EKS cluster context to extract the cluster name and the region
    93  func ParseContext(context string) (string, string, error) {
    94  	// First check if context name matches <cluster-name>.<region>.*
    95  	reg := regexp.MustCompile(`([a-zA-Z][-a-zA-Z0-9]*)\.((us(-gov)?|ap|ca|cn|eu|sa)-(central|(north|south)?(east|west)?)-\d)\.*`)
    96  	result := reg.FindStringSubmatch(context)
    97  	if len(result) >= 3 {
    98  		return result[1], result[2], nil
    99  	}
   100  
   101  	// or else if the context name matchesAWS ARN format as defined:
   102  	// https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
   103  	reg = regexp.MustCompile(`arn:aws:eks:((?:us(?:-gov)?|ap|ca|cn|eu|sa)-(?:central|(?:north|south)?(?:east|west)?)-\d):[0-9]*:cluster\/([a-zA-Z][-a-zA-Z0-9]*)`)
   104  	result = reg.FindStringSubmatch(context)
   105  	if len(result) >= 3 {
   106  		return result[2], result[1], nil
   107  	}
   108  
   109  	return "", "", errors.Errorf("unable to parse %s as <cluster_name>.<region>.* or arn:aws:<region>:account-id:cluster/<cluster_name>", context)
   110  
   111  }
   112  
   113  // GetCurrentlyConnectedRegionAndClusterName gets the current context for the connected cluster and parses it
   114  // to extract both the Region and the ClusterName
   115  func GetCurrentlyConnectedRegionAndClusterName() (string, string, error) {
   116  	kubeConfig, _, err := kube.NewKubeConfig().LoadConfig()
   117  	if err != nil {
   118  		return "", "", errors.Wrapf(err, "loading kubeconfig")
   119  	}
   120  
   121  	context := kube.Cluster(kubeConfig)
   122  	server := kube.CurrentServer(kubeConfig)
   123  	currentClusterName, currentRegion, err := GetClusterNameAndRegionFromAWS("", "", server)
   124  	if err != nil {
   125  		currentClusterName, currentRegion, err := ParseContext(context)
   126  		if err != nil {
   127  			return "", "", errors.Wrapf(err, "parsing the current Kubernetes context %s", context)
   128  		}
   129  		return currentClusterName, currentRegion, nil
   130  	}
   131  	return currentClusterName, currentRegion, nil
   132  }
   133  
   134  // UserHomeDir returns the home directory for the user the process is running under.
   135  // This is a copy of shareddefaults.UserHomeDir in the internal AWS package.
   136  // We can't user user.Current().HomeDir as we want to override this during testing. :-|
   137  func UserHomeDir() string {
   138  	if runtime.GOOS == "windows" { // Windows
   139  		return os.Getenv("USERPROFILE")
   140  	}
   141  
   142  	// *nix
   143  	return os.Getenv("HOME")
   144  }