diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..ffe9a44 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,52 @@ +name: Test + +on: [pull_request] + +jobs: + test: + name: Test + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + path: src/github.com/kubenav/bind + + - name: Setup Go 1.13 + uses: actions/setup-go@v1 + with: + go-version: 1.13 + + - name: Set GOPATH + run: | + echo "##[set-env name=GOPATH;]$(dirname $GITHUB_WORKSPACE)/bind" + echo "##[add-path]$(dirname $GITHUB_WORKSPACE)/bind/bin" + shell: bash + + - name: Install Dependencies + run: | + cd $GOPATH/src/github.com/kubenav/bind + make dependencies + + - name: Generate Bindings (Android) + # We must build the Android bindings on Ubuntu, + # because the macOS used for GitHub Actions contains an outdated NDK version. + if: matrix.os == 'ubuntu-latest' + run: | + cd $GOPATH/src/github.com/kubenav/bind + gomobile init + make bindings-android + + - name: Generate Bindings (iOS) + if: matrix.os == 'macos-latest' + # We must unset the ANDROID_HOME and ANDROID_NDK_HOME environment variables, + # otherwise gomobile tries to generate the bindings for Android. + run: | + cd $GOPATH/src/github.com/kubenav/bind + unset ANDROID_HOME + unset ANDROID_NDK_HOME + gomobile init + make bindings-ios diff --git a/Makefile b/Makefile index c96900d..5772d8e 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ bindings-ios: dependencies: GO111MODULE=off go get -u golang.org/x/mobile/cmd/gomobile + GO111MODULE=off go get -u github.com/aws/aws-sdk-go/... release-major: $(eval MAJORVERSION=$(shell git describe --tags --abbrev=0 | sed s/v// | awk -F. '{print $$1+1".0.0"}')) diff --git a/request/request.go b/request/request.go index 28dd704..3e372ef 100644 --- a/request/request.go +++ b/request/request.go @@ -4,12 +4,19 @@ import ( "bytes" "crypto/tls" "crypto/x509" + "encoding/base64" "encoding/json" "fmt" "io/ioutil" "net" "net/http" "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/eks" + "github.com/aws/aws-sdk-go/service/sts" ) type APIError struct { @@ -113,3 +120,78 @@ func httpClientForRootCAs(certificateAuthorityData, clientCertificateData, clien return &tlsConfig, nil } + +// AWSGetClusters returns all EKS clusters from AWS. +func AWSGetClusters(accessKeyId, secretAccessKey, region string) (string, error) { + var clusters []*eks.Cluster + var names []*string + var nextToken *string + + cred := credentials.NewStaticCredentials(accessKeyId, secretAccessKey, "") + + sess, err := session.NewSession(&aws.Config{Region: aws.String(region), Credentials: cred}) + if err != nil { + return "", err + } + + eksClient := eks.New(sess) + + for { + c, err := eksClient.ListClusters(&eks.ListClustersInput{NextToken: nextToken}) + if err != nil { + return "", err + } + + names = append(names, c.Clusters...) + + if c.NextToken == nil { + break + } + + nextToken = c.NextToken + } + + for _, name := range names { + cluster, err := eksClient.DescribeCluster(&eks.DescribeClusterInput{Name: name}) + if err != nil { + return "", err + } + + if *cluster.Cluster.Status == eks.ClusterStatusActive { + clusters = append(clusters, cluster.Cluster) + } + } + + if clusters != nil { + b, err := json.Marshal(clusters) + if err != nil { + return "", err + } + + return string(b), nil + } + + return "", nil +} + +// AWSGetToken returns a bearer token for Kubernetes API requests. +// See: https://github.com/kubernetes-sigs/aws-iam-authenticator/blob/7547c74e660f8d34d9980f2c69aa008eed1f48d0/pkg/token/token.go#L310 +func AWSGetToken(accessKeyId, secretAccessKey, region, clusterID string) (string, error) { + cred := credentials.NewStaticCredentials(accessKeyId, secretAccessKey, "") + + sess, err := session.NewSession(&aws.Config{Region: aws.String(region), Credentials: cred}) + if err != nil { + return "", err + } + + stsClient := sts.New(sess) + + request, _ := stsClient.GetCallerIdentityRequest(&sts.GetCallerIdentityInput{}) + request.HTTPRequest.Header.Add("x-k8s-aws-id", clusterID) + presignedURLString, err := request.Presign(60) + if err != nil { + return "", err + } + + return fmt.Sprintf(`{"token": "k8s-aws-v1.%s"}`, base64.RawURLEncoding.EncodeToString([]byte(presignedURLString))), nil +} diff --git a/request/request_test.go b/request/request_test.go index 53d3a33..4093713 100644 --- a/request/request_test.go +++ b/request/request_test.go @@ -50,3 +50,30 @@ func TestDoNonexistingResource(t *testing.T) { t.Logf(err.Error()) } + +func TestAWSGetClusters(t *testing.T) { + accessKeyId := os.Getenv("AWS_ACCESS_KEY_ID") + secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY") + region := os.Getenv("AWS_REGION") + + data, err := AWSGetClusters(accessKeyId, secretAccessKey, region) + if err != nil { + t.Errorf("Could not get clusters: %s", err.Error()) + } + + t.Logf(data) +} + +func TestAWSGetToken(t *testing.T) { + accessKeyId := os.Getenv("AWS_ACCESS_KEY_ID") + secretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY") + region := os.Getenv("AWS_REGION") + clusterID := os.Getenv("AWS_CLUSTER_ID") + + data, err := AWSGetToken(accessKeyId, secretAccessKey, region, clusterID) + if err != nil { + t.Errorf("Could not get token: %s", err.Error()) + } + + t.Logf(data) +}