Skip to content

Commit

Permalink
Integration Status endpoint: include pending UserTasks (gravitational…
Browse files Browse the repository at this point in the history
…#51702)

This PR changes the Integration Status endpoint to include the pending
UserTasks for the integration.
This will be used by the frontend to show the number of pending tasks.
  • Loading branch information
marcoandredinis authored and carloscastrojumo committed Feb 19, 2025
1 parent d9d1951 commit 0c2d661
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 0 deletions.
26 changes: 26 additions & 0 deletions lib/web/integrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ import (
discoveryconfigv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/discoveryconfig/v1"
integrationv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1"
pluginspb "github.com/gravitational/teleport/api/gen/proto/go/teleport/plugins/v1"
usertasksv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/usertasks/v1"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/types/discoveryconfig"
"github.com/gravitational/teleport/api/types/usertasks"
"github.com/gravitational/teleport/integrations/access/msteams"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/httplib"
Expand Down Expand Up @@ -265,6 +267,7 @@ func (h *Handler) integrationStats(w http.ResponseWriter, r *http.Request, p htt
discoveryConfigLister: clt.DiscoveryConfigClient(),
databaseGetter: clt,
awsOIDCClient: clt.IntegrationAWSOIDCClient(),
userTasksClient: clt.UserTasksServiceClient(),
}
summary, err := collectIntegrationStats(r.Context(), req)
if err != nil {
Expand All @@ -274,12 +277,17 @@ func (h *Handler) integrationStats(w http.ResponseWriter, r *http.Request, p htt
return summary, nil
}

type userTasksByIntegrationLister interface {
ListUserTasksByIntegration(ctx context.Context, pageSize int64, nextToken string, integration string) ([]*usertasksv1.UserTask, string, error)
}

type collectIntegrationStatsRequest struct {
logger *slog.Logger
integration types.Integration
discoveryConfigLister discoveryConfigLister
databaseGetter databaseGetter
awsOIDCClient deployedDatabaseServiceLister
userTasksClient userTasksByIntegrationLister
}

func collectIntegrationStats(ctx context.Context, req collectIntegrationStatsRequest) (*ui.IntegrationWithSummary, error) {
Expand All @@ -292,6 +300,24 @@ func collectIntegrationStats(ctx context.Context, req collectIntegrationStatsReq
ret.Integration = uiIg

var nextPage string
for {
userTasks, nextToken, err := req.userTasksClient.ListUserTasksByIntegration(ctx, 0, nextPage, req.integration.GetName())
if err != nil {
return nil, err
}
for _, userTask := range userTasks {
if userTask.GetSpec().GetState() == usertasks.TaskStateOpen {
ret.UnresolvedUserTasks++
}
}

if nextToken == "" {
break
}
nextPage = nextToken
}

nextPage = ""
for {
discoveryConfigs, nextToken, err := req.discoveryConfigLister.ListDiscoveryConfigs(ctx, 0, nextPage)
if err != nil {
Expand Down
85 changes: 85 additions & 0 deletions lib/web/integrations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package web
import (
"context"
"encoding/json"
"strconv"
"testing"
"time"

Expand All @@ -32,9 +33,11 @@ import (
"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"
usertasksv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/usertasks/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/api/types/usertasks"
"github.com/gravitational/teleport/lib/auth/integration/credentials"
"github.com/gravitational/teleport/lib/modules"
"github.com/gravitational/teleport/lib/services"
Expand Down Expand Up @@ -102,6 +105,44 @@ func TestIntegrationsCreateWithAudience(t *testing.T) {
}
}

type mockUserTasksLister struct {
defaultPageSize int64
userTasks []*usertasksv1.UserTask
}

func (m *mockUserTasksLister) ListUserTasksByIntegration(ctx context.Context, pageSize int64, nextToken string, integration string) ([]*usertasksv1.UserTask, string, error) {
var ret []*usertasksv1.UserTask
if pageSize == 0 {
pageSize = m.defaultPageSize
}

if len(m.userTasks) == 0 {
return ret, "", nil
}

var sliceStart int
if nextToken != "" {
nextTokenInt, err := strconv.Atoi(nextToken)
if err != nil {
return nil, "", trace.Wrap(err)
}
sliceStart = nextTokenInt
}
userTasksSlice := m.userTasks[sliceStart:]

for i, userTask := range userTasksSlice {
if userTask.GetSpec().GetState() == "OPEN" {
ret = append(ret, userTask)
if len(ret) == int(pageSize) {
nextTokenInt := sliceStart + i + 1
return ret, strconv.Itoa(nextTokenInt), nil
}
}
}

return ret, "", nil
}

func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
ctx := context.Background()
logger := utils.NewSlogLoggerForTests()
Expand Down Expand Up @@ -138,6 +179,47 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
discoveryConfigLister: clt,
databaseGetter: clt,
awsOIDCClient: deployedDatabaseServicesClient,
userTasksClient: &mockUserTasksLister{},
}
gotSummary, err := collectIntegrationStats(ctx, req)
require.NoError(t, err)
expectedSummary := &ui.IntegrationWithSummary{
Integration: &ui.Integration{
Name: integrationName,
SubKind: "aws-oidc",
AWSOIDC: &ui.IntegrationAWSOIDCSpec{RoleARN: "arn:role"},
},
}
require.Equal(t, expectedSummary, gotSummary)
})

t.Run("returns the number of unresolved user tasks", func(t *testing.T) {
clt := &mockRelevantAWSRegionsClient{
databaseServices: &proto.ListResourcesResponse{
Resources: []*proto.PaginatedResource{},
},
databases: make([]types.Database, 0),
discoveryConfigs: make([]*discoveryconfig.DiscoveryConfig, 0),
}

var userTasksList []*usertasksv1.UserTask
for range 10 {
userTasksList = append(userTasksList, &usertasksv1.UserTask{Spec: &usertasksv1.UserTaskSpec{State: usertasks.TaskStateOpen}})
userTasksList = append(userTasksList, &usertasksv1.UserTask{Spec: &usertasksv1.UserTaskSpec{State: usertasks.TaskStateResolved}})
}

userTasksClient := &mockUserTasksLister{
defaultPageSize: 3,
userTasks: userTasksList,
}

req := collectIntegrationStatsRequest{
logger: logger,
integration: integration,
discoveryConfigLister: clt,
databaseGetter: clt,
awsOIDCClient: deployedDatabaseServicesClient,
userTasksClient: userTasksClient,
}
gotSummary, err := collectIntegrationStats(ctx, req)
require.NoError(t, err)
Expand All @@ -147,6 +229,7 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
SubKind: "aws-oidc",
AWSOIDC: &ui.IntegrationAWSOIDCSpec{RoleARN: "arn:role"},
},
UnresolvedUserTasks: 10,
}
require.Equal(t, expectedSummary, gotSummary)
})
Expand Down Expand Up @@ -217,6 +300,7 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
discoveryConfigLister: clt,
databaseGetter: clt,
awsOIDCClient: deployedDatabaseServicesClient,
userTasksClient: &mockUserTasksLister{},
}
gotSummary, err := collectIntegrationStats(ctx, req)
require.NoError(t, err)
Expand Down Expand Up @@ -286,6 +370,7 @@ func TestCollectAWSOIDCAutoDiscoverStats(t *testing.T) {
discoveryConfigLister: clt,
databaseGetter: clt,
awsOIDCClient: deployedDatabaseServicesClient,
userTasksClient: &mockUserTasksLister{},
}
gotSummary, err := collectIntegrationStats(ctx, req)
require.NoError(t, err)
Expand Down
2 changes: 2 additions & 0 deletions lib/web/ui/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ func (r *IntegrationAWSOIDCSpec) CheckAndSetDefaults() error {
// IntegrationWithSummary describes Integration fields and the fields required to return the summary.
type IntegrationWithSummary struct {
*Integration
// UnresolvedUserTasks contains the count of unresolved user tasks related to this integration.
UnresolvedUserTasks int `json:"unresolvedUserTasks,omitempty"`
// AWSEC2 contains the summary for the AWS EC2 resources for this integration.
AWSEC2 ResourceTypeSummary `json:"awsec2,omitempty"`
// AWSRDS contains the summary for the AWS RDS resources and agents for this integration.
Expand Down

0 comments on commit 0c2d661

Please sign in to comment.