Skip to content

Commit

Permalink
Show aws summary stats on dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
michellescripts committed Dec 5, 2024
1 parent 6ad18e3 commit 609be0c
Show file tree
Hide file tree
Showing 9 changed files with 454 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,184 @@

import React from 'react';

import { addHours } from 'date-fns';

import { AwsOidcDashboard } from 'teleport/Integrations/status/AwsOidc/AwsOidcDashboard';
import { MockAwsOidcStatusProvider } from 'teleport/Integrations/status/AwsOidc/testHelpers/mockAwsOidcStatusProvider';
import { IntegrationKind } from 'teleport/services/integrations';
import {
IntegrationKind,
ResourceTypeSummary,
} from 'teleport/services/integrations';
import { AwsOidcStatusContextState } from 'teleport/Integrations/status/AwsOidc/useAwsOidcStatus';

export default {
title: 'Teleport/Integrations/AwsOidc',
};

// Loaded dashboard with data for each aws resource and a navigation header
export function Dashboard() {
return (
<MockAwsOidcStatusProvider
value={{
attempt: {
status: 'success',
data: {
resourceType: 'integration',
name: 'integration-one',
kind: IntegrationKind.AwsOidc,
spec: {
roleArn: 'arn:aws:iam::111456789011:role/bar',
},
statusCode: 1,
},
statusText: '',
},
}}
>
<MockAwsOidcStatusProvider value={makeAwsOidcStatusContextState()}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// Loaded dashboard with missing data for each aws resource and a navigation header
export function DashboardMissingData() {
const state = makeAwsOidcStatusContextState();
state.statsAttempt.data.awseks = undefined;
state.statsAttempt.data.awsrds = undefined;
state.statsAttempt.data.awsec2 = undefined;
return (
<MockAwsOidcStatusProvider value={state}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// Loading screen
export function StatsProcessing() {
const props = makeAwsOidcStatusContextState({
statsAttempt: { status: 'processing', data: null, statusText: '' },
});
return (
<MockAwsOidcStatusProvider value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// No header, no loading indicator
export function IntegrationProcessing() {
const props = makeAwsOidcStatusContextState({
integrationAttempt: {
status: 'processing',
data: null,
statusText: '',
},
});
return (
<MockAwsOidcStatusProvider value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// Loaded error message
export function StatsFailed() {
const props = makeAwsOidcStatusContextState({
statsAttempt: {
status: 'error',
data: null,
statusText: 'failed to get stats',
error: {},
},
});
return (
<MockAwsOidcStatusProvider value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// Loaded dashboard with data for each aws resource but no navigation header
export function IntegrationFailed() {
const props = makeAwsOidcStatusContextState({
integrationAttempt: {
status: 'error',
data: null,
statusText: 'failed to get integration',
error: {},
},
});
return (
<MockAwsOidcStatusProvider value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// Blank screen
export function StatsNoData() {
const props = makeAwsOidcStatusContextState({
statsAttempt: { status: 'success', data: null, statusText: '' },
});
return (
<MockAwsOidcStatusProvider value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

// No header, no loading indicator
export function IntegrationNoData() {
const props = makeAwsOidcStatusContextState({
integrationAttempt: {
status: 'success',
data: null,
statusText: '',
},
});
return (
<MockAwsOidcStatusProvider value={props}>
<AwsOidcDashboard />
</MockAwsOidcStatusProvider>
);
}

function makeAwsOidcStatusContextState(
overrides: Partial<AwsOidcStatusContextState> = {}
): AwsOidcStatusContextState {
return Object.assign(
{
integrationAttempt: {
status: 'success',
statusText: '',
data: {
resourceType: 'integration',
name: 'integration-one',
kind: IntegrationKind.AwsOidc,
spec: {
roleArn: 'arn:aws:iam::111456789011:role/bar',
},
statusCode: 1,
},
},
statsAttempt: {
status: 'success',
statusText: '',
data: {
name: 'integration-one',
subKind: IntegrationKind.AwsOidc,
awsoidc: {
roleArn: 'arn:aws:iam::111456789011:role/bar',
},
awsec2: makeResourceTypeSummary(),
awsrds: makeResourceTypeSummary(),
awseks: makeResourceTypeSummary(),
},
},
},
overrides
);
}

function makeResourceTypeSummary(
overrides: Partial<ResourceTypeSummary> = {}
): ResourceTypeSummary {
return Object.assign(
{
rulesCount: Math.floor(Math.random() * 100),
resourcesFound: Math.floor(Math.random() * 100),
resourcesEnrollmentFailed: Math.floor(Math.random() * 100),
resourcesEnrollmentSuccess: Math.floor(Math.random() * 100),
discoverLastSync: addHours(
new Date().getTime(),
-Math.floor(Math.random() * 100)
),
ecsDatabaseServiceCount: Math.floor(Math.random() * 100),
},
overrides
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,20 @@
import React from 'react';
import { render, screen } from 'design/utils/testing';

import { within } from '@testing-library/react';

import { AwsOidcDashboard } from 'teleport/Integrations/status/AwsOidc/AwsOidcDashboard';
import { MockAwsOidcStatusProvider } from 'teleport/Integrations/status/AwsOidc/testHelpers/mockAwsOidcStatusProvider';
import { IntegrationKind } from 'teleport/services/integrations';
import { addHours } from 'teleport/components/BannerList/useAlerts';

test('renders header', () => {
render(
<MockAwsOidcStatusProvider
value={{
attempt: {
integrationAttempt: {
status: 'success',
statusText: '',
data: {
resourceType: 'integration',
name: 'integration-one',
Expand All @@ -38,7 +42,41 @@ test('renders header', () => {
},
statusCode: 1,
},
},
statsAttempt: {
status: 'success',
statusText: '',
data: {
name: 'integration-one',
subKind: IntegrationKind.AwsOidc,
awsoidc: {
roleArn: 'arn:aws:iam::111456789011:role/bar',
},
awsec2: {
rulesCount: 24,
resourcesFound: 12,
resourcesEnrollmentFailed: 3,
resourcesEnrollmentSuccess: 9,
discoverLastSync: new Date().getTime(),
ecsDatabaseServiceCount: 0, // irrelevant
},
awsrds: {
rulesCount: 14,
resourcesFound: 5,
resourcesEnrollmentFailed: 5,
resourcesEnrollmentSuccess: 0,
discoverLastSync: addHours(new Date().getTime(), -4),
ecsDatabaseServiceCount: 8, // relevant
},
awseks: {
rulesCount: 33,
resourcesFound: 3,
resourcesEnrollmentFailed: 0,
resourcesEnrollmentSuccess: 3,
discoverLastSync: addHours(new Date().getTime(), -48),
ecsDatabaseServiceCount: 0, // irrelevant
},
},
},
}}
>
Expand All @@ -53,4 +91,49 @@ test('renders header', () => {
expect(screen.getByText('integration-one')).toBeInTheDocument();
expect(screen.getByLabelText('status')).toHaveAttribute('kind', 'success');
expect(screen.getByLabelText('status')).toHaveTextContent('Running');

const ec2 = screen.getByTestId('ec2-stats');
expect(within(ec2).getByTestId('sync')).toHaveTextContent(
'Last Sync: 0 seconds ago'
);
expect(within(ec2).getByTestId('rules')).toHaveTextContent(
'Enrollment Rules 24'
);
expect(within(ec2).queryByTestId('rds-agents')).not.toBeInTheDocument();
expect(within(ec2).getByTestId('enrolled')).toHaveTextContent(
'Enrolled Instances 9'
);
expect(within(ec2).getByTestId('failed')).toHaveTextContent(
'Failed Instances 3'
);

const rds = screen.getByTestId('rds-stats');
expect(within(rds).getByTestId('sync')).toHaveTextContent(
'Last Sync: 4 hours ago'
);
expect(within(rds).getByTestId('rules')).toHaveTextContent(
'Enrollment Rules 14'
);
expect(within(rds).getByTestId('rds-agents')).toHaveTextContent('Agents 8');
expect(within(rds).getByTestId('enrolled')).toHaveTextContent(
'Enrolled Databases 0'
);
expect(within(rds).getByTestId('failed')).toHaveTextContent(
'Failed Databases 5'
);

const eks = screen.getByTestId('eks-stats');
expect(within(eks).getByTestId('sync')).toHaveTextContent(
'Last Sync: 2 days ago'
);
expect(within(eks).getByTestId('rules')).toHaveTextContent(
'Enrollment Rules 33'
);
expect(within(eks).queryByTestId('rds-agents')).not.toBeInTheDocument();
expect(within(eks).getByTestId('enrolled')).toHaveTextContent(
'Enrolled Clusters 3'
);
expect(within(eks).getByTestId('failed')).toHaveTextContent(
'Failed Clusters 0'
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,43 @@

import React from 'react';

import { Flex, H2, Indicator } from 'design';

import { Danger } from 'design/Alert';

import { AwsOidcHeader } from 'teleport/Integrations/status/AwsOidc/AwsOidcHeader';
import { useAwsOidcStatus } from 'teleport/Integrations/status/AwsOidc/useAwsOidcStatus';
import { FeatureBox } from 'teleport/components/Layout';
import {
AwsResource,
StatCard,
} from 'teleport/Integrations/status/AwsOidc/StatCard';

// todo (michellescripts) after routing, ensure this view can be sticky
export function AwsOidcDashboard() {
const { attempt } = useAwsOidcStatus();
const { statsAttempt, integrationAttempt } = useAwsOidcStatus();

if (statsAttempt.status == 'processing') {
return <Indicator />;
}
if (statsAttempt.status == 'error') {
return <Danger>{statsAttempt.statusText}</Danger>;
}
if (!statsAttempt.data) {
return;
}

// todo (michellescripts) after routing, ensure this view can be sticky
const { awsec2, awseks, awsrds } = statsAttempt.data;
const { data: integration } = integrationAttempt;
return (
<FeatureBox css={{ maxWidth: '1400px', paddingTop: '16px' }}>
{attempt.data && <AwsOidcHeader integration={attempt.data} />}
Status for integration type aws-oidc is not supported
{integration && <AwsOidcHeader integration={integration} />}
<H2 my={3}>Auto-Enrollment</H2>
<Flex gap={3}>
{<StatCard resource={AwsResource.ec2} summary={awsec2} />}
{<StatCard resource={AwsResource.rds} summary={awsrds} />}
{<StatCard resource={AwsResource.eks} summary={awseks} />}
</Flex>
</FeatureBox>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export function AwsOidcHeader({ integration }: { integration: Integration }) {
<ArrowLeft size="medium" />
</ButtonIcon>
</HoverTooltip>
<Text bold fontSize={6} mr={2}>
<Text bold fontSize={6} mx={2}>
{integration.name}
</Text>
<Label kind={labelKind} aria-label="status" px={3}>
Expand Down
Loading

0 comments on commit 609be0c

Please sign in to comment.