Skip to content

Commit

Permalink
[v17] Integration Dashboard: include number of deployed agents (#50760)
Browse files Browse the repository at this point in the history
* Integration Dashboard: include number of deployed agents

* rename collectIntegrationStats and use request type
  • Loading branch information
marcoandredinis authored Jan 6, 2025
1 parent eeb8c7e commit 9619d4f
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 37 deletions.
57 changes: 38 additions & 19 deletions lib/web/integrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package web

import (
"context"
"log/slog"
"net/http"
"net/url"
"slices"
Expand Down Expand Up @@ -220,52 +221,61 @@ func (h *Handler) integrationStats(w http.ResponseWriter, r *http.Request, p htt
return nil, trace.Wrap(err)
}

summary, err := collectAWSOIDCAutoDiscoverStats(r.Context(), ig, clt.DiscoveryConfigClient())
req := collectIntegrationStatsRequest{
logger: h.logger,
integration: ig,
discoveryConfigLister: clt.DiscoveryConfigClient(),
databaseGetter: clt,
awsOIDCClient: clt.IntegrationAWSOIDCClient(),
}
summary, err := collectIntegrationStats(r.Context(), req)
if err != nil {
return nil, trace.Wrap(err)
}

return summary, nil
}

func collectAWSOIDCAutoDiscoverStats(
ctx context.Context,
integration types.Integration,
clt interface {
ListDiscoveryConfigs(ctx context.Context, pageSize int, nextToken string) ([]*discoveryconfig.DiscoveryConfig, string, error)
},
) (ui.IntegrationWithSummary, error) {
var ret ui.IntegrationWithSummary
type collectIntegrationStatsRequest struct {
logger *slog.Logger
integration types.Integration
discoveryConfigLister discoveryConfigLister
databaseGetter databaseGetter
awsOIDCClient deployedDatabaseServiceLister
}

uiIg, err := ui.MakeIntegration(integration)
func collectIntegrationStats(ctx context.Context, req collectIntegrationStatsRequest) (*ui.IntegrationWithSummary, error) {
ret := &ui.IntegrationWithSummary{}

uiIg, err := ui.MakeIntegration(req.integration)
if err != nil {
return ret, err
return nil, err
}
ret.Integration = uiIg

var nextPage string
for {
discoveryConfigs, nextToken, err := clt.ListDiscoveryConfigs(ctx, 0, nextPage)
discoveryConfigs, nextToken, err := req.discoveryConfigLister.ListDiscoveryConfigs(ctx, 0, nextPage)
if err != nil {
return ret, trace.Wrap(err)
return nil, trace.Wrap(err)
}
for _, dc := range discoveryConfigs {
discoveredResources, ok := dc.Status.IntegrationDiscoveredResources[integration.GetName()]
discoveredResources, ok := dc.Status.IntegrationDiscoveredResources[req.integration.GetName()]
if !ok {
continue
}

if matchers := rulesWithIntegration(dc, types.AWSMatcherEC2, integration.GetName()); matchers != 0 {
if matchers := rulesWithIntegration(dc, types.AWSMatcherEC2, req.integration.GetName()); matchers != 0 {
ret.AWSEC2.RulesCount += matchers
mergeResourceTypeSummary(&ret.AWSEC2, dc.Status.LastSyncTime, discoveredResources.AwsEc2)
}

if matchers := rulesWithIntegration(dc, types.AWSMatcherRDS, integration.GetName()); matchers != 0 {
if matchers := rulesWithIntegration(dc, types.AWSMatcherRDS, req.integration.GetName()); matchers != 0 {
ret.AWSRDS.RulesCount += matchers
mergeResourceTypeSummary(&ret.AWSRDS, dc.Status.LastSyncTime, discoveredResources.AwsRds)
}

if matchers := rulesWithIntegration(dc, types.AWSMatcherEKS, integration.GetName()); matchers != 0 {
if matchers := rulesWithIntegration(dc, types.AWSMatcherEKS, req.integration.GetName()); matchers != 0 {
ret.AWSEKS.RulesCount += matchers
mergeResourceTypeSummary(&ret.AWSEKS, dc.Status.LastSyncTime, discoveredResources.AwsEks)
}
Expand All @@ -277,8 +287,17 @@ func collectAWSOIDCAutoDiscoverStats(
nextPage = nextToken
}

// TODO(marco): add total number of ECS Database Services.
ret.AWSRDS.ECSDatabaseServiceCount = 0
regions, err := fetchRelevantAWSRegions(ctx, req.databaseGetter, req.discoveryConfigLister)
if err != nil {
return nil, trace.Wrap(err)
}

services, err := listDeployedDatabaseServices(ctx, req.logger, req.integration.GetName(), regions, req.awsOIDCClient)
if err != nil {
return nil, trace.Wrap(err)
}

ret.AWSRDS.ECSDatabaseServiceCount = len(services)

return ret, nil
}
Expand Down
62 changes: 44 additions & 18 deletions lib/web/integrations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,15 @@ import (
"github.com/google/uuid"
"github.com/stretchr/testify/require"

"github.com/gravitational/teleport/api/client/proto"
discoveryconfigv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/discoveryconfig/v1"
integrationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/discoveryconfig"
"github.com/gravitational/teleport/api/types/header"
"github.com/gravitational/teleport/lib/services"
libui "github.com/gravitational/teleport/lib/ui"
"github.com/gravitational/teleport/lib/utils"
"github.com/gravitational/teleport/lib/web/ui"
)

Expand Down Expand Up @@ -97,6 +100,8 @@ func TestIntegrationsCreateWithAudience(t *testing.T) {

func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
ctx := context.Background()
logger := utils.NewSlogLoggerForTests()

integrationName := "my-integration"
integration, err := types.NewIntegrationAWSOIDC(
types.Metadata{Name: integrationName},
Expand All @@ -106,14 +111,33 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
)
require.NoError(t, err)

deployedServiceCommand := buildCommandDeployedDatabaseService(t, true, types.Labels{"vpc": []string{"vpc1", "vpc2"}})
deployedDatabaseServicesClient := &mockDeployedDatabaseServices{
integration: "my-integration",
servicesPerRegion: map[string][]*integrationv1.DeployedDatabaseService{
"us-west-2": dummyDeployedDatabaseServices(1, deployedServiceCommand),
},
}

t.Run("without discovery configs, returns just the integration", func(t *testing.T) {
clt := &mockDiscoveryConfigsGetter{
clt := &mockRelevantAWSRegionsClient{
databaseServices: &proto.ListResourcesResponse{
Resources: []*proto.PaginatedResource{},
},
databases: make([]types.Database, 0),
discoveryConfigs: make([]*discoveryconfig.DiscoveryConfig, 0),
}

gotSummary, err := collectAWSOIDCAutoDiscoverStats(ctx, integration, clt)
req := collectIntegrationStatsRequest{
logger: logger,
integration: integration,
discoveryConfigLister: clt,
databaseGetter: clt,
awsOIDCClient: deployedDatabaseServicesClient,
}
gotSummary, err := collectIntegrationStats(ctx, req)
require.NoError(t, err)
expectedSummary := ui.IntegrationWithSummary{
expectedSummary := &ui.IntegrationWithSummary{
Integration: &ui.Integration{
Name: integrationName,
SubKind: "aws-oidc",
Expand Down Expand Up @@ -145,7 +169,7 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
Spec: discoveryconfig.Spec{AWS: []types.AWSMatcher{{
Integration: integrationName,
Types: []string{"rds"},
Regions: []string{"us-east-1", "us-east-2"},
Regions: []string{"us-east-1", "us-east-2", "us-west-2"},
}}},
Status: discoveryconfig.Status{
LastSyncTime: syncTime,
Expand Down Expand Up @@ -173,17 +197,26 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
},
},
}
clt := &mockDiscoveryConfigsGetter{
clt := &mockRelevantAWSRegionsClient{
discoveryConfigs: []*discoveryconfig.DiscoveryConfig{
dcForEC2,
dcForRDS,
dcForEKS,
},
databaseServices: &proto.ListResourcesResponse{},
databases: make([]types.Database, 0),
}

gotSummary, err := collectAWSOIDCAutoDiscoverStats(ctx, integration, clt)
req := collectIntegrationStatsRequest{
logger: logger,
integration: integration,
discoveryConfigLister: clt,
databaseGetter: clt,
awsOIDCClient: deployedDatabaseServicesClient,
}
gotSummary, err := collectIntegrationStats(ctx, req)
require.NoError(t, err)
expectedSummary := ui.IntegrationWithSummary{
expectedSummary := &ui.IntegrationWithSummary{
Integration: &ui.Integration{
Name: integrationName,
SubKind: "aws-oidc",
Expand All @@ -197,10 +230,11 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
DiscoverLastSync: &syncTime,
},
AWSRDS: ui.ResourceTypeSummary{
RulesCount: 2,
RulesCount: 3,
ResourcesFound: 2,
ResourcesEnrollmentFailed: 1,
ResourcesEnrollmentSuccess: 1,
ECSDatabaseServiceCount: 1,
DiscoverLastSync: &syncTime,
},
AWSEKS: ui.ResourceTypeSummary{
Expand All @@ -220,7 +254,7 @@ func TestCollectAutoDiscoveryRules(t *testing.T) {
integrationName := "my-integration"

t.Run("without discovery configs, returns no rules", func(t *testing.T) {
clt := &mockDiscoveryConfigsGetter{
clt := &mockRelevantAWSRegionsClient{
discoveryConfigs: make([]*discoveryconfig.DiscoveryConfig, 0),
}

Expand Down Expand Up @@ -287,7 +321,7 @@ func TestCollectAutoDiscoveryRules(t *testing.T) {
Tags: types.Labels{"*": []string{"*"}},
}}},
}
clt := &mockDiscoveryConfigsGetter{
clt := &mockRelevantAWSRegionsClient{
discoveryConfigs: []*discoveryconfig.DiscoveryConfig{
dcForEC2,
dcForRDS,
Expand Down Expand Up @@ -350,11 +384,3 @@ func TestCollectAutoDiscoveryRules(t *testing.T) {
require.ElementsMatch(t, expectedRules, got.Rules)
})
}

type mockDiscoveryConfigsGetter struct {
discoveryConfigs []*discoveryconfig.DiscoveryConfig
}

func (m *mockDiscoveryConfigsGetter) ListDiscoveryConfigs(ctx context.Context, pageSize int, nextToken string) ([]*discoveryconfig.DiscoveryConfig, string, error) {
return m.discoveryConfigs, "", nil
}

0 comments on commit 9619d4f

Please sign in to comment.