Skip to content

Commit

Permalink
Lazily initialize EKS discovery fetcher client.
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonAM committed Nov 30, 2023
1 parent f286327 commit 579d000
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 33 deletions.
27 changes: 8 additions & 19 deletions lib/srv/discovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,9 @@ func (s *Server) initAWSWatchers(matchers []types.AWSMatcher) error {

// Add kube fetchers.
for _, matcher := range otherMatchers {
matcherAssumeRole := &types.AssumeRole{}
matcherAssumeRole := types.AssumeRole{}
if matcher.AssumeRole != nil {
matcherAssumeRole = matcher.AssumeRole
matcherAssumeRole = *matcher.AssumeRole
}

for _, t := range matcher.Types {
Expand All @@ -409,25 +409,14 @@ func (s *Server) initAWSWatchers(matchers []types.AWSMatcher) error {
return nil
}

func (s *Server) getEKSFetcher(region string, assumeRole *types.AssumeRole, tags types.Labels) (common.Fetcher, error) {
client, err := s.CloudClients.GetAWSEKSClient(
s.ctx,
region,
cloud.WithAssumeRole(
assumeRole.RoleARN,
assumeRole.ExternalID,
),
cloud.WithAmbientCredentials(),
)
if err != nil {
return nil, trace.Wrap(err)
}
func (s *Server) getEKSFetcher(region string, assumeRole types.AssumeRole, tags types.Labels) (common.Fetcher, error) {
fetcher, err := fetchers.NewEKSFetcher(
fetchers.EKSFetcherConfig{
Client: client,
Region: region,
FilterLabels: tags,
Log: s.Log,
EKSClientGetter: s.CloudClients,
AssumeRole: assumeRole,
Region: region,
FilterLabels: tags,
Log: s.Log,
},
)
return fetcher, trace.Wrap(err)
Expand Down
6 changes: 3 additions & 3 deletions lib/srv/discovery/discovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1099,7 +1099,7 @@ func TestDiscoveryServer_New(t *testing.T) {
},
},
{
desc: "EKS fetcher is skipped on initialization error",
desc: "EKS fetcher is skipped on initialization error (missing region)",
cloudClients: &cloud.TestCloudClients{
STS: &mocks.STSMock{AssumeRoleErrors: map[string]error{"arn:aws:iam::123456789012:role/teleport-role": trace.AccessDenied("unauthorized")}},
EKS: &mocks.EKSMock{},
Expand All @@ -1108,7 +1108,7 @@ func TestDiscoveryServer_New(t *testing.T) {
AWS: []types.AWSMatcher{
{
Types: []string{"eks"},
Regions: []string{"eu-west-1"},
Regions: []string{},
Tags: map[string]utils.Strings{"env": {"prod"}},
AssumeRole: &types.AssumeRole{
RoleARN: "arn:aws:iam::123456789012:role/teleport-role",
Expand Down Expand Up @@ -1144,7 +1144,7 @@ func TestDiscoveryServer_New(t *testing.T) {
discServer, err := New(
ctx,
&Config{
CloudClients: tt.cloudClients,
CloudClients: nil,
AccessPoint: newFakeAccessPoint(),
Matchers: tt.matchers,
Emitter: &mockEmitter{},
Expand Down
62 changes: 55 additions & 7 deletions lib/srv/discovery/fetchers/eks.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"golang.org/x/sync/errgroup"

"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/cloud"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/srv/discovery/common"
)
Expand All @@ -39,12 +40,24 @@ const (

type eksFetcher struct {
EKSFetcherConfig

mu sync.Mutex
client eksiface.EKSAPI
}

// EKSClientGetter is an interface for getting an EKS client.
type EKSClientGetter interface {
// GetAWSEKSClient returns AWS EKS client for the specified region.
GetAWSEKSClient(ctx context.Context, region string, opts ...cloud.AWSAssumeRoleOptionFn) (eksiface.EKSAPI, error)
}

// EKSFetcherConfig configures the EKS fetcher.
type EKSFetcherConfig struct {
// Client is the AWS eKS client.
Client eksiface.EKSAPI
// EKSClientGetter retrieves an EKS client.
EKSClientGetter EKSClientGetter
// AssumeRole provides a role ARN and ExternalID to assume an AWS role
// when fetching clusters.
AssumeRole types.AssumeRole
// Region is the region where the clusters should be located.
Region string
// FilterLabels are the filter criteria.
Expand All @@ -55,8 +68,8 @@ type EKSFetcherConfig struct {

// CheckAndSetDefaults validates and sets the defaults values.
func (c *EKSFetcherConfig) CheckAndSetDefaults() error {
if c.Client == nil {
return trace.BadParameter("missing Client field")
if c.EKSClientGetter == nil {
return trace.BadParameter("missing EKSClientGetter field")
}
if len(c.Region) == 0 {
return trace.BadParameter("missing Region field")
Expand All @@ -78,7 +91,32 @@ func NewEKSFetcher(cfg EKSFetcherConfig) (common.Fetcher, error) {
return nil, trace.Wrap(err)
}

return &eksFetcher{cfg}, nil
return &eksFetcher{EKSFetcherConfig: cfg}, nil
}

func (a *eksFetcher) getClient(ctx context.Context) (eksiface.EKSAPI, error) {
a.mu.Lock()
defer a.mu.Unlock()

if a.client != nil {
return a.client, nil
}

client, err := a.EKSClientGetter.GetAWSEKSClient(
ctx,
a.Region,
cloud.WithAssumeRole(
a.AssumeRole.RoleARN,
a.AssumeRole.ExternalID,
),
cloud.WithAmbientCredentials(),
)
if err != nil {
return nil, trace.Wrap(err)
}
a.client = client

return a.client, nil
}

func (a *eksFetcher) Get(ctx context.Context) (types.ResourcesWithLabels, error) {
Expand Down Expand Up @@ -106,7 +144,12 @@ func (a *eksFetcher) getEKSClusters(ctx context.Context) (types.KubeClusters, er
)
group.SetLimit(concurrencyLimit)

err := a.Client.ListClustersPagesWithContext(ctx,
client, err := a.getClient(ctx)
if err != nil {
return nil, trace.Wrap(err, "failed getting AWS EKS client")
}

err = client.ListClustersPagesWithContext(ctx,
&eks.ListClustersInput{
Include: nil, // For now we should only list EKS clusters
},
Expand Down Expand Up @@ -161,7 +204,12 @@ func (a *eksFetcher) String() string {
// If any cluster does not match the filtering criteria, this function returns a “trace.CompareFailed“ error
// to distinguish filtering and operational errors.
func (a *eksFetcher) getMatchingKubeCluster(ctx context.Context, clusterName string) (types.KubeCluster, error) {
rsp, err := a.Client.DescribeClusterWithContext(
client, err := a.getClient(ctx)
if err != nil {
return nil, trace.Wrap(err, "failed getting AWS EKS client")
}

rsp, err := client.DescribeClusterWithContext(
ctx,
&eks.DescribeClusterInput{
Name: aws.String(clusterName),
Expand Down
15 changes: 11 additions & 4 deletions lib/srv/discovery/fetchers/eks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/cloud"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/srv/discovery/common"
)
Expand Down Expand Up @@ -99,10 +100,10 @@ func TestEKSFetcher(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := EKSFetcherConfig{
Client: newPopulatedEKSMock(),
FilterLabels: tt.args.filterLabels,
Region: tt.args.region,
Log: logrus.New(),
EKSClientGetter: &mockEKSClientGetter{},
FilterLabels: tt.args.filterLabels,
Region: tt.args.region,
Log: logrus.New(),
}
fetcher, err := NewEKSFetcher(cfg)
require.NoError(t, err)
Expand All @@ -114,6 +115,12 @@ func TestEKSFetcher(t *testing.T) {
}
}

type mockEKSClientGetter struct{}

func (e *mockEKSClientGetter) GetAWSEKSClient(ctx context.Context, region string, opts ...cloud.AWSAssumeRoleOptionFn) (eksiface.EKSAPI, error) {
return newPopulatedEKSMock(), nil
}

type mockEKSAPI struct {
eksiface.EKSAPI
clusters []*eks.Cluster
Expand Down

0 comments on commit 579d000

Please sign in to comment.