Skip to content

Commit

Permalink
split out basic discovery code (#23)
Browse files Browse the repository at this point in the history
* split out basic discovery code
* added tests
* merged with master
* changelog
* moved changelog
* code gen
* locked version of imports
  • Loading branch information
EItanya authored Apr 28, 2020
1 parent 0dbc80a commit c0bf9f2
Show file tree
Hide file tree
Showing 19 changed files with 1,001 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ install-deps: mod-download
go get -v github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc
go get -v github.com/solo-io/protoc-gen-ext
go get -v github.com/golang/mock/mockgen
go get -v golang.org/x/tools/cmd/goimports
go get -v golang.org/x/tools/cmd/goimports@v0.0.0-20200427205912-352a5409fae0
go mod tidy

# Generated Code - Required to update Codgen Templates
Expand Down
6 changes: 6 additions & 0 deletions changelog/v0.1.0/cluster-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
changelog:
- type: NEW_FEATURE
description: |
Add functions to build temporary ClientConfig from different cloud provider cluster resources
issueLink: https://github.com/solo-io/skv2/issues/25
resolvesIssue: false
10 changes: 8 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ module github.com/solo-io/skv2
go 1.13

require (
cloud.google.com/go v0.40.0
github.com/BurntSushi/toml v0.3.1
github.com/Masterminds/sprig/v3 v3.0.0
github.com/aws/aws-sdk-go v1.30.15
github.com/envoyproxy/protoc-gen-validate v0.1.0
github.com/gertd/go-pluralize v0.1.1
github.com/go-logr/logr v0.1.0
Expand All @@ -20,7 +22,7 @@ require (
github.com/onsi/ginkgo v1.12.0
github.com/onsi/gomega v1.8.1
github.com/pborman/uuid v1.2.0
github.com/pkg/errors v0.8.1
github.com/pkg/errors v0.9.1
github.com/rogpeppe/go-internal v1.5.2
github.com/rotisserie/eris v0.1.1
github.com/sirupsen/logrus v1.4.2
Expand All @@ -30,13 +32,17 @@ require (
github.com/solo-io/solo-kit v0.12.2
go.uber.org/zap v1.13.0
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad // indirect
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c // indirect
golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b
golang.org/x/tools v0.0.0-20200427205912-352a5409fae0
google.golang.org/api v0.6.0
google.golang.org/genproto v0.0.0-20191028173616-919d9bdd9fe6
k8s.io/api v0.17.2
k8s.io/apiextensions-apiserver v0.17.2
k8s.io/apimachinery v0.17.2
k8s.io/client-go v8.0.0+incompatible
k8s.io/code-generator v0.17.2
sigs.k8s.io/aws-iam-authenticator v0.5.0
sigs.k8s.io/controller-runtime v0.5.1
sigs.k8s.io/yaml v1.1.0
)
Expand Down
72 changes: 70 additions & 2 deletions go.sum

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions pkg/multicluster/discovery/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package discovery

import (
"context"
"encoding/base64"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/eks"
"github.com/solo-io/skv2/pkg/multicluster/discovery/cloud"
"github.com/solo-io/skv2/pkg/multicluster/kubeconfig"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

func NewEksConfigBuilder(eksClient cloud.EksClient) EksConfigBuilder {
return &awsClusterConfigBuilder{
eksClient: eksClient,
}
}

type awsClusterConfigBuilder struct {
eksClient cloud.EksClient
}

func (a *awsClusterConfigBuilder) ConfigForCluster(ctx context.Context, cluster *eks.Cluster) (clientcmd.ClientConfig, error) {
tok, err := a.eksClient.Token(ctx, aws.StringValue(cluster.Name))
if err != nil {
return nil, err
}
ca, err := base64.StdEncoding.DecodeString(aws.StringValue(cluster.CertificateAuthority.Data))
if err != nil {
return nil, err
}

cfg := kubeconfig.BuildRemoteCfg(
&clientcmdapi.Cluster{
Server: aws.StringValue(cluster.Endpoint),
CertificateAuthorityData: ca,
},
&clientcmdapi.Context{
Cluster: aws.StringValue(cluster.Name),
},
aws.StringValue(cluster.Name),
tok.Token,
)

return clientcmd.NewDefaultClientConfig(cfg, &clientcmd.ConfigOverrides{}), nil
}
90 changes: 90 additions & 0 deletions pkg/multicluster/discovery/aws_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package discovery_test

import (
"context"
"encoding/base64"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/eks"
"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/rotisserie/eris"
"github.com/solo-io/go-utils/testutils"
"github.com/solo-io/skv2/pkg/multicluster/discovery"
mock_cloud "github.com/solo-io/skv2/pkg/multicluster/discovery/cloud/mocks"
"github.com/solo-io/skv2/pkg/multicluster/kubeconfig"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"
)

var _ = Describe("Aws", func() {
var (
ctx context.Context
ctrl *gomock.Controller

eksClient *mock_cloud.MockEksClient

testErr = eris.New("hello")
)

BeforeEach(func() {
ctrl, ctx = gomock.WithContext(context.TODO(), GinkgoT())

eksClient = mock_cloud.NewMockEksClient(ctrl)
})

It("will fail if token cannot be found", func() {
configBuilder := discovery.NewEksConfigBuilder(eksClient)

cluster := &eks.Cluster{
Name: aws.String("cluster-name"),
}
eksClient.EXPECT().
Token(ctx, aws.StringValue(cluster.Name)).
Return(token.Token{}, testErr)

_, err := configBuilder.ConfigForCluster(ctx, cluster)
Expect(err).To(HaveOccurred())
Expect(err).To(testutils.HaveInErrorChain(testErr))
})

It("will Create ClientConfig if token can be found", func() {
configBuilder := discovery.NewEksConfigBuilder(eksClient)
caData := []byte("fake-ca-data")
tok := token.Token{Token: "new-token-who-dis"}
cluster := &eks.Cluster{
Name: aws.String("cluster-name"),
CertificateAuthority: &eks.Certificate{
Data: aws.String(base64.StdEncoding.EncodeToString(caData)),
},
Endpoint: aws.String("fake-endpoint"),
}
eksClient.EXPECT().
Token(ctx, aws.StringValue(cluster.Name)).
Return(tok, nil)

cfg, err := configBuilder.ConfigForCluster(ctx, cluster)
Expect(err).NotTo(HaveOccurred())

ca, err := base64.StdEncoding.DecodeString(aws.StringValue(cluster.CertificateAuthority.Data))
Expect(err).NotTo(HaveOccurred())
newCfg := kubeconfig.BuildRemoteCfg(
&clientcmdapi.Cluster{
Server: aws.StringValue(cluster.Endpoint),
CertificateAuthorityData: ca,
},
&clientcmdapi.Context{
Cluster: aws.StringValue(cluster.Name),
},
aws.StringValue(cluster.Name),
tok.Token,
)

rawCfg, err := cfg.RawConfig()
Expect(err).NotTo(HaveOccurred())
Expect(rawCfg).To(Equal(newCfg))

})

})
65 changes: 65 additions & 0 deletions pkg/multicluster/discovery/cloud/aws.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package cloud

import (
"context"

"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"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"
)

func NewEksClient(region string, creds *credentials.Credentials) (EksClient, error) {
sess, err := session.NewSession(&aws.Config{
Region: aws.String(region),
Credentials: creds,
})
if err != nil {
return nil, err
}
return &awsClient{
sess: sess,
}, nil
}

type awsClient struct {
sess *session.Session
}

func (a *awsClient) Session() *session.Session {
return a.sess
}

func (a *awsClient) DescribeCluster(ctx context.Context, name string) (*eks.Cluster, error) {
eksSvc := eks.New(a.sess)
input := &eks.DescribeClusterInput{
Name: aws.String(name),
}
resp, err := eksSvc.DescribeClusterWithContext(ctx, input)
if err != nil {
return nil, err
}
return resp.Cluster, nil
}

func (a *awsClient) ListClusters(ctx context.Context, input *eks.ListClustersInput) (*eks.ListClustersOutput, error) {
eksSvc := eks.New(a.sess)
return eksSvc.ListClustersWithContext(ctx, input)
}

func (a *awsClient) Token(ctx context.Context, name string) (token.Token, error) {
gen, err := token.NewGenerator(true, false)
if err != nil {
return token.Token{}, err
}
opts := &token.GetTokenOptions{
ClusterID: name,
Session: a.sess,
}
tok, err := gen.GetWithOptions(opts)
if err != nil {
return token.Token{}, err
}
return tok, nil
}
46 changes: 46 additions & 0 deletions pkg/multicluster/discovery/cloud/gke.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package cloud

import (
"context"
"fmt"

container "cloud.google.com/go/container/apiv1"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/option"
containerpb "google.golang.org/genproto/googleapis/container/v1"
)

func NewGkeClient(ctx context.Context, projectId string) (GkeClient, error) {
creds, err := google.FindDefaultCredentials(ctx, container.DefaultAuthScopes()...)
if err != nil {
return nil, err
}
return &gkeClient{
creds: creds,
projectId: projectId,
}, nil
}

type gkeClient struct {
projectId string
creds *google.Credentials
}

func (g *gkeClient) Token(ctx context.Context) (*oauth2.Token, error) {
return g.creds.TokenSource.Token()
}

func (g *gkeClient) ListClusters(ctx context.Context) ([]*containerpb.Cluster, error) {
client, err := container.NewClusterManagerClient(ctx, option.WithCredentials(g.creds))
if err != nil {
return nil, err
}
resp, err := client.ListClusters(ctx, &containerpb.ListClustersRequest{
Parent: fmt.Sprintf("projects/%s/locations/-", g.projectId),
})
if err != nil {
return nil, err
}
return resp.GetClusters(), nil
}
23 changes: 23 additions & 0 deletions pkg/multicluster/discovery/cloud/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package cloud

import (
"context"

"github.com/aws/aws-sdk-go/service/eks"
"golang.org/x/oauth2"
containerpb "google.golang.org/genproto/googleapis/container/v1"
"sigs.k8s.io/aws-iam-authenticator/pkg/token"
)

//go:generate mockgen -source ./interfaces.go -destination ./mocks/mock_interfaces.go

type EksClient interface {
DescribeCluster(ctx context.Context, name string) (*eks.Cluster, error)
ListClusters(ctx context.Context, input *eks.ListClustersInput) (*eks.ListClustersOutput, error)
Token(ctx context.Context, name string) (token.Token, error)
}

type GkeClient interface {
Token(ctx context.Context) (*oauth2.Token, error)
ListClusters(ctx context.Context) ([]*containerpb.Cluster, error)
}
Loading

0 comments on commit c0bf9f2

Please sign in to comment.