From 898d8acc896941be1f7c28e425367e912a31236d Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Fri, 21 Apr 2023 08:25:37 -0700 Subject: [PATCH 01/13] Clean up aws oidc integration instructions --- .../instructions/FifthStageInstructions.tsx | 5 ++++- .../instructions/Instructions.story.tsx | 16 +++++++--------- .../instructions/SecondStageInstructions.tsx | 1 + .../instructions/SeventhStageInstructions.tsx | 18 +++++++----------- .../instructions/SixthStageInstructions.tsx | 7 ++++--- .../instructions/ThirdStageInstructions.tsx | 3 ++- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/FifthStageInstructions.tsx b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/FifthStageInstructions.tsx index 35966bb6ba8b4..87b598a2270e0 100644 --- a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/FifthStageInstructions.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/FifthStageInstructions.tsx @@ -32,7 +32,10 @@ export function FifthStageInstructions(props: CommonInstructionsProps) { "Statement": [ { "Effect": "Allow", - "Action": "rds:DescribeDBInstances", + "Action": [ + "rds:DescribeDBInstances", + "rds:DescribeDBClusters" + ] "Resource": "*" } ] diff --git a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/Instructions.story.tsx b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/Instructions.story.tsx index 4125dae4d58c1..45be4f2fa87d8 100644 --- a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/Instructions.story.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/Instructions.story.tsx @@ -28,6 +28,8 @@ import { SuccessfullyAddedIntegrationDialog, } from './SeventhStageInstructions'; +import type { DiscoverUrlLocState } from 'teleport/Discover/useDiscover'; + export default { title: 'Teleport/Integrations/Enroll/AwsOidc/Instructions', }; @@ -46,19 +48,15 @@ export const Step7 = () => ( export const ConfirmDialog = () => ( - + ); export const ConfirmDialogFromDiscover = () => ( - - + + ); diff --git a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SecondStageInstructions.tsx b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SecondStageInstructions.tsx index 324fdfdac500f..1e0b4b08dba22 100644 --- a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SecondStageInstructions.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SecondStageInstructions.tsx @@ -92,6 +92,7 @@ export function SecondStageInstructions(props: CommonInstructionsProps) { <> setThumbprint(e.target.value)} value={thumbprint} diff --git a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SeventhStageInstructions.tsx b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SeventhStageInstructions.tsx index db9c2ec456f0a..9ee4b6b354c33 100644 --- a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SeventhStageInstructions.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SeventhStageInstructions.tsx @@ -37,12 +37,11 @@ import { integrationService, } from 'teleport/services/integrations'; import cfg from 'teleport/config'; +import { DiscoverUrlLocState } from 'teleport/Discover/useDiscover'; import { InstructionsContainer } from './common'; export function SeventhStageInstructions() { - const location = useLocation<{ discoverEventId: string }>(); - const { attempt, setAttempt } = useAttempt(''); const [showConfirmBox, setShowConfirmBox] = useState(false); const [roleArn, setRoleArn] = useState(''); @@ -79,6 +78,7 @@ export function SeventhStageInstructions() { Copy the role ARN and paste it below setRoleArn(e.target.value)} value={roleArn} @@ -108,22 +108,19 @@ export function SeventhStageInstructions() { )} {showConfirmBox && ( - + )} ); } export function SuccessfullyAddedIntegrationDialog({ - discoverEventId, integrationName, }: { - discoverEventId: string; integrationName: string; }) { + const location = useLocation(); + return ( ({ maxWidth: '500px', width: '100%' })} @@ -140,16 +137,15 @@ export function SuccessfullyAddedIntegrationDialog({ - {discoverEventId ? ( + {location.state?.discover ? ( diff --git a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SixthStageInstructions.tsx b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SixthStageInstructions.tsx index 240dca368be3a..1b81159d2102b 100644 --- a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SixthStageInstructions.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SixthStageInstructions.tsx @@ -28,14 +28,15 @@ import type { CommonInstructionsProps } from './common'; export function SixthStageInstructions(props: CommonInstructionsProps) { return ( - Close the "Create policy tab" + + Close the tab for "Create Policy" and go back to the tab for{' '} + Create Role + Refresh the list of policies and select the policy you just created - Search for the policy you just created and select it - Click Next: Tags and then Next: Review diff --git a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/ThirdStageInstructions.tsx b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/ThirdStageInstructions.tsx index d96c76576907c..2d3b3033c209d 100644 --- a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/ThirdStageInstructions.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/ThirdStageInstructions.tsx @@ -38,7 +38,8 @@ export function ThirdStageInstructions(props: CommonInstructionsProps) { - Select Assign role and create a new role + Click Assign role and select{' '} + Create a new role From 85c10c3ab12ce8c7e0b85c555e8e1f98afec38c1 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Fri, 21 Apr 2023 08:26:55 -0700 Subject: [PATCH 02/13] Change ResourceSpec icon type from ReactElement to string When storing state into location URL, it doesn't allow storing ReactElement, so I changed the icon element into string that refers to the correct icon. Also adds rds aurora tiles to Select Resources screen. --- .../src/Discover/Navigation/StepItem.tsx | 5 +- .../SelectResource/SelectResource.tsx | 4 +- .../SelectResource.story.test.tsx.snap | 250 ++++++++++++++++++ .../src/Discover/SelectResource/databases.tsx | 77 ++++-- .../src/Discover/SelectResource/icons.tsx | 2 + .../src/Discover/SelectResource/resources.tsx | 17 +- .../src/Discover/SelectResource/types.ts | 3 +- 7 files changed, 318 insertions(+), 40 deletions(-) diff --git a/web/packages/teleport/src/Discover/Navigation/StepItem.tsx b/web/packages/teleport/src/Discover/Navigation/StepItem.tsx index 7b041b0088cf3..c29520b685296 100644 --- a/web/packages/teleport/src/Discover/Navigation/StepItem.tsx +++ b/web/packages/teleport/src/Discover/Navigation/StepItem.tsx @@ -18,6 +18,8 @@ import React from 'react'; import styled from 'styled-components'; import { Flex } from 'design'; +import { icons } from 'teleport/Discover/SelectResource/icons'; + import { StepList } from './StepList'; import type { View } from 'teleport/Discover/flow'; @@ -51,7 +53,7 @@ export function StepItem(props: StepItemProps) { {getBulletIcon({ - Icon: props.selectedResource.Icon, + Icon: icons[props.selectedResource.icon], })} {props.selectedResource.name} @@ -155,6 +157,7 @@ const CheckedBullet = styled(Bullet)` :before { content: '✓'; + color: ${props => props.theme.colors.levels.popout}; } `; diff --git a/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx b/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx index d3ff189ee3397..0021af82aac04 100644 --- a/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx +++ b/web/packages/teleport/src/Discover/SelectResource/SelectResource.tsx @@ -35,6 +35,8 @@ import { RESOURCES, } from 'teleport/Discover/SelectResource/resources'; +import { icons } from './icons'; + import type { ResourceSpec } from './types'; import type { AddButtonResourceKind } from 'teleport/components/AgentButtonAdd/AgentButtonAdd'; @@ -160,7 +162,7 @@ export function SelectResource(props: SelectResourceProps) { )} - {r.Icon} + {icons[r.icon]} {pretitle && ( diff --git a/web/packages/teleport/src/Discover/SelectResource/__snapshots__/SelectResource.story.test.tsx.snap b/web/packages/teleport/src/Discover/SelectResource/__snapshots__/SelectResource.story.test.tsx.snap index 443e82a88fe4d..6f2a7c0466130 100644 --- a/web/packages/teleport/src/Discover/SelectResource/__snapshots__/SelectResource.story.test.tsx.snap +++ b/web/packages/teleport/src/Discover/SelectResource/__snapshots__/SelectResource.story.test.tsx.snap @@ -1192,6 +1192,47 @@ exports[`render with all access 1`] = ` +
+
+ Guided +
+
+
+ +
+
+
+ Amazon Web Services (AWS) +
+
+ Aurora PostgreSQL +
+
+
+
+
+
+ Guided +
+
+
+ +
+
+
+ Amazon Web Services (AWS) +
+
+ Aurora MySQL/MariaDB +
+
+
+
+
+
+ Lacking Permissions +
+
+
+ +
+
+
+ Amazon Web Services (AWS) +
+
+ Aurora PostgreSQL +
+
+
+
+
+
+ Lacking Permissions +
+
+
+ +
+
+
+ Amazon Web Services (AWS) +
+
+ Aurora MySQL/MariaDB +
+
+
+
+
+
+ Lacking Permissions +
+
+
+ +
+
+
+ Amazon Web Services (AWS) +
+
+ Aurora PostgreSQL +
+
+
+
+
+
+ Lacking Permissions +
+
+
+ +
+
+
+ Amazon Web Services (AWS) +
+
+ Aurora MySQL/MariaDB +
+
+
+
, Unknown: , }; + +export type ResourceIconName = keyof typeof icons; diff --git a/web/packages/teleport/src/Discover/SelectResource/resources.tsx b/web/packages/teleport/src/Discover/SelectResource/resources.tsx index 4236897602cdc..1e5b0a5d91f23 100644 --- a/web/packages/teleport/src/Discover/SelectResource/resources.tsx +++ b/web/packages/teleport/src/Discover/SelectResource/resources.tsx @@ -24,7 +24,6 @@ import { DATABASES_UNGUIDED_DOC, } from './databases'; import { ResourceSpec, DatabaseLocation, DatabaseEngine } from './types'; -import { icons } from './icons'; const baseServerKeywords = 'server node'; export const SERVERS: ResourceSpec[] = [ @@ -32,35 +31,35 @@ export const SERVERS: ResourceSpec[] = [ name: 'Ubuntu 14.04+', kind: ResourceKind.Server, keywords: baseServerKeywords + 'ubuntu', - Icon: icons.Linux, + icon: 'Linux', event: DiscoverEventResource.Server, }, { name: 'Debian 8+', kind: ResourceKind.Server, keywords: baseServerKeywords + 'debian', - Icon: icons.Linux, + icon: 'Linux', event: DiscoverEventResource.Server, }, { name: 'RHEL/CentOS 7+', kind: ResourceKind.Server, keywords: baseServerKeywords + 'rhel centos', - Icon: icons.Linux, + icon: 'Linux', event: DiscoverEventResource.Server, }, { name: 'Amazon Linux 2', kind: ResourceKind.Server, keywords: baseServerKeywords + 'amazon linux', - Icon: icons.Aws, + icon: 'Aws', event: DiscoverEventResource.Server, }, { name: 'macOS (Intel)', kind: ResourceKind.Server, keywords: baseServerKeywords + 'mac macos intel', - Icon: icons.Apple, + icon: 'Apple', event: DiscoverEventResource.Server, }, ]; @@ -70,7 +69,7 @@ export const APPLICATIONS: ResourceSpec[] = [ name: 'Application', kind: ResourceKind.Application, keywords: 'application', - Icon: icons.Application, + icon: 'Application', unguidedLink: 'https://goteleport.com/docs/application-access/getting-started/', event: DiscoverEventResource.ApplicationHttp, @@ -82,7 +81,7 @@ export const WINDOWS_DESKTOPS: ResourceSpec[] = [ name: 'Active Directory', kind: ResourceKind.Desktop, keywords: 'windows desktop active directory ad', - Icon: icons.Windows, + icon: 'Windows', event: DiscoverEventResource.WindowsDesktop, }, // { @@ -99,7 +98,7 @@ export const KUBERNETES: ResourceSpec[] = [ name: 'Kubernetes', kind: ResourceKind.Kubernetes, keywords: 'kubernetes cluster kubes', - Icon: icons.Kube, + icon: 'Kube', event: DiscoverEventResource.Kubernetes, }, ]; diff --git a/web/packages/teleport/src/Discover/SelectResource/types.ts b/web/packages/teleport/src/Discover/SelectResource/types.ts index ba1f284ff7f62..286f7de3921b6 100644 --- a/web/packages/teleport/src/Discover/SelectResource/types.ts +++ b/web/packages/teleport/src/Discover/SelectResource/types.ts @@ -17,6 +17,7 @@ import { ResourceKind } from '../Shared/ResourceKind'; import type { DiscoverEventResource } from 'teleport/services/userEvent'; +import type { ResourceIconName } from './icons'; export enum DatabaseLocation { Aws, @@ -52,7 +53,7 @@ export interface ResourceSpec { name: string; popular?: boolean; kind: ResourceKind; - Icon: React.ReactElement; + icon: ResourceIconName; // keywords are filter words that user may use to search for // this resource. keywords: string; From 89e0d20ba3fb0eecda1fa4bb31e215effcc2c5e7 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Fri, 21 Apr 2023 08:28:23 -0700 Subject: [PATCH 03/13] Fix the expected backend aws status value for RDS list --- .../Database/EnrollRdsDatabase/RdsDatabaseList.tsx | 6 +++--- .../src/services/integrations/integrations.test.ts | 8 +++----- .../teleport/src/services/integrations/integrations.ts | 4 ++-- web/packages/teleport/src/services/integrations/types.ts | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/RdsDatabaseList.tsx b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/RdsDatabaseList.tsx index ada2c8aa2a556..40a8666f3ef02 100644 --- a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/RdsDatabaseList.tsx +++ b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/RdsDatabaseList.tsx @@ -142,11 +142,11 @@ enum Status { function getStatus(item: AwsRdsDatabase) { switch (item.status) { - case 'Available': + case 'available': return Status.Success; - case 'Failed': - case 'Deleting': + case 'failed': + case 'deleting': return Status.Error; } } diff --git a/web/packages/teleport/src/services/integrations/integrations.test.ts b/web/packages/teleport/src/services/integrations/integrations.test.ts index 0c665e745f6d1..f889056065685 100644 --- a/web/packages/teleport/src/services/integrations/integrations.test.ts +++ b/web/packages/teleport/src/services/integrations/integrations.test.ts @@ -123,8 +123,8 @@ test('fetchAwsDatabases response', async () => { engine: 'mysql', name: 'rds-2', uri: 'endpoint-2', - status: 'Available', labels: [], + status: undefined, accountId: undefined, resourceId: undefined, }, @@ -132,8 +132,8 @@ test('fetchAwsDatabases response', async () => { engine: 'mysql', name: 'rds-3', uri: 'endpoint-3', - status: 'Available', labels: [], + status: undefined, accountId: undefined, resourceId: undefined, }, @@ -200,9 +200,9 @@ const mockAwsDbs = [ protocol: 'postgres', name: 'rds-1', uri: 'endpoint-1', - status: 'Available', labels: [{ name: 'env', value: 'prod' }], aws: { + status: 'Available', account_id: 'account-id-1', rds: { resource_id: 'resource-id-1', @@ -214,7 +214,6 @@ const mockAwsDbs = [ protocol: 'mysql', name: 'rds-2', uri: 'endpoint-2', - status: 'Available', aws: {}, }, // Test without aws field. @@ -222,6 +221,5 @@ const mockAwsDbs = [ protocol: 'mysql', name: 'rds-3', uri: 'endpoint-3', - status: 'Available', }, ]; diff --git a/web/packages/teleport/src/services/integrations/integrations.ts b/web/packages/teleport/src/services/integrations/integrations.ts index 5f3e4bb29b643..98af8fab4b009 100644 --- a/web/packages/teleport/src/services/integrations/integrations.ts +++ b/web/packages/teleport/src/services/integrations/integrations.ts @@ -133,13 +133,13 @@ function makeIntegration(json: any): Integration { export function makeAwsDatabase(json: any): AwsRdsDatabase { json = json ?? {}; - const { aws, name, uri, status, labels, protocol } = json; + const { aws, name, uri, labels, protocol } = json; return { engine: protocol, name, uri, - status, + status: aws?.status, labels: labels ?? [], resourceId: aws?.rds?.resource_id, accountId: aws?.account_id, diff --git a/web/packages/teleport/src/services/integrations/types.ts b/web/packages/teleport/src/services/integrations/types.ts index 2657af5b98ea0..5a5b70502f238 100644 --- a/web/packages/teleport/src/services/integrations/types.ts +++ b/web/packages/teleport/src/services/integrations/types.ts @@ -192,7 +192,7 @@ export type AwsRdsDatabase = { // There is a lot of status states available so only a select few were // hard defined to use to determine the status color. // https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/accessing-monitoring.html - status: 'Available' | 'Failed' | 'Deleting'; + status: 'available' | 'failed' | 'deleting'; }; export type ListAwsRdsDatabaseResponse = { From eeb3dc3614ded82d02ab2e2dd8b8b31245c0e785 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Fri, 21 Apr 2023 08:28:58 -0700 Subject: [PATCH 04/13] For RDS list, allow refreshing the table Helpful when user makes changes to the RDS instance (eg. tags) and needs to get the most up to date listing --- .../EnrollRdsDatabase/AwsRegionSelector.tsx | 50 +++++++++++++------ .../EnrollRdsDatabase.story.tsx | 26 +++++++--- .../EnrollRdsDatabase/EnrollRdsDatabase.tsx | 13 +++-- 3 files changed, 63 insertions(+), 26 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/AwsRegionSelector.tsx b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/AwsRegionSelector.tsx index 0965d0245330c..9754fb1567806 100644 --- a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/AwsRegionSelector.tsx +++ b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/AwsRegionSelector.tsx @@ -15,22 +15,25 @@ */ import React, { useState } from 'react'; -import { Box, ButtonPrimary, Text, Flex } from 'design'; +import { Box, ButtonPrimary, Text, Flex, ButtonSecondary } from 'design'; import FieldSelect from 'shared/components/FieldSelect'; import { Option } from 'shared/components/Select'; import { requiredField } from 'shared/components/Validation/rules'; import Validation, { Validator } from 'shared/components/Validation'; +import { Refresh as RefreshIcon } from 'design/Icon'; import { awsRegionMap, Regions } from 'teleport/services/integrations'; export function AwsRegionSelector({ onFetch, - disableBtn, + onRefresh, + disableFetch, disableSelector, clear, }: { onFetch(region: Regions): void; - disableBtn: boolean; + onRefresh(): void; + disableFetch: boolean; disableSelector: boolean; clear(): void; }) { @@ -51,7 +54,7 @@ export function AwsRegionSelector({ return ( {({ validator }) => ( - <> + Select the AWS Region you would like to see databases for: @@ -69,17 +72,36 @@ export function AwsRegionSelector({ isDisabled={disableSelector} /> - handleFetch(validator)} - width="160px" - height="40px" - mt={1} - > - Fetch Databases - + + handleFetch(validator)} + width="160px" + height="40px" + mt={1} + > + Fetch Databases + + + + + - + )} ); diff --git a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.story.tsx b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.story.tsx index 18e745e236cfe..92cc7a7d4599e 100644 --- a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.story.tsx +++ b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.story.tsx @@ -28,7 +28,8 @@ export default { export const AwsRegionsSelectorDisabled = () => ( null} - disableBtn={true} + disableFetch={true} + onRefresh={() => null} disableSelector={true} clear={() => null} /> @@ -37,7 +38,18 @@ export const AwsRegionsSelectorDisabled = () => ( export const AwsRegionsSelectorEnabled = () => ( null} - disableBtn={false} + disableFetch={false} + onRefresh={() => null} + disableSelector={false} + clear={() => null} + /> +); + +export const AwsRegionsSelectorRefreshEnabled = () => ( + null} + disableFetch={true} + onRefresh={() => null} disableSelector={false} clear={() => null} /> @@ -79,7 +91,7 @@ const fixtures: AwsRdsDatabase[] = [ engine: 'postgres', uri: '', labels: [], - status: 'Available', + status: 'available', accountId: '', resourceId: '', }, @@ -88,7 +100,7 @@ const fixtures: AwsRdsDatabase[] = [ engine: 'mysql', uri: '', labels: [], - status: 'Available', + status: 'available', accountId: '', resourceId: '', }, @@ -100,7 +112,7 @@ const fixtures: AwsRdsDatabase[] = [ { name: 'env', value: 'prod' }, { name: 'os', value: 'windows' }, ], - status: 'Deleting', + status: 'deleting', accountId: '', resourceId: '', }, @@ -109,7 +121,7 @@ const fixtures: AwsRdsDatabase[] = [ engine: 'postgres', uri: '', labels: [], - status: 'Failed', + status: 'failed', accountId: '', resourceId: '', }, @@ -131,7 +143,7 @@ const fixtures: AwsRdsDatabase[] = [ engine: 'postgres', uri: '', labels: [{ name: 'testing-name', value: 'testing-value' }], - status: 'Available', + status: 'available', accountId: '', resourceId: '', }, diff --git a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx index a33bfbbde038d..eee0f73d4e3b2 100644 --- a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx +++ b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx @@ -82,6 +82,11 @@ export function EnrollRdsDatabase() { fetchDatabases({ ...tableData }); } + function refreshDatabaseList() { + // When refreshing, start the table back at page 1. + fetchDatabases({ ...tableData, startKey: '', items: [] }); + } + function fetchDatabases(data: TableData) { const integrationName = (agentMeta as DbMeta).integrationName; @@ -128,9 +133,6 @@ export function EnrollRdsDatabase() { } function handleOnProceed() { - // Append `teleport_` to the RDS db name to - // lower the chance of duplicate db name. - registerDatabase( { name: selectedDb.name, @@ -157,9 +159,10 @@ export function EnrollRdsDatabase() { )} 0 } /> @@ -177,7 +180,7 @@ export function EnrollRdsDatabase() { {registerAttempt.status !== '' && ( Date: Fri, 21 Apr 2023 08:59:22 -0700 Subject: [PATCH 05/13] Update rds db setup access text info --- .../Database/SetupAccess/SetupAccess.tsx | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx b/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx index cd1887a32c439..18665dc9cab8c 100644 --- a/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx +++ b/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx @@ -207,15 +207,7 @@ function DbEngineInstructions({ return ( - Database users must allow{' '} - - IAM authentication - {' '} - in order to be used with Database Access for RDS. To enable, users - must have a rds_iam role: + Users must have a rds_iam role: - Database users must allow{' '} - - IAM authentication - {' '} - in order to be used with Database Access for RDS. Users must - have the RDS authentication plugin enabled: + Users must have the RDS authentication plugin enabled: Date: Fri, 21 Apr 2023 09:01:08 -0700 Subject: [PATCH 06/13] Make create database dialog more consistent btwn states --- .../CreateDatabase/CreateDatabaseDialog.tsx | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabaseDialog.tsx b/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabaseDialog.tsx index 93db631ca4904..547adb2a5c858 100644 --- a/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabaseDialog.tsx +++ b/web/packages/teleport/src/Discover/Database/CreateDatabase/CreateDatabaseDialog.tsx @@ -50,12 +50,9 @@ export function CreateDatabaseDialog({ if (attempt.status === 'failed') { content = ( <> - - Database Register Failed - - Error: {attempt.statusText} + Register Failed: {attempt.statusText} @@ -70,11 +67,9 @@ export function CreateDatabaseDialog({ } else if (attempt.status === 'processing') { content = ( <> - - Registering Database - - + + + Next + ); } else { // success content = ( <> - - Successfully Registered Database - Database "{dbName}" successfully registered - + Next @@ -114,6 +109,9 @@ export function CreateDatabaseDialog({ mb={0} textAlign="center" > + + Database Register + {content}
From 432b02c8b1fd9c9bfd37e351c9282cd4517f0779 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Fri, 21 Apr 2023 09:04:17 -0700 Subject: [PATCH 07/13] Fix label matching Previously we required the agent matcher labels be an exact match of registered db labels otherwise we prevented the user from deploying an agent (which was wrong). Now the only requirement is that the matcher labels are all able to match against registered db labels. --- .../CreateDatabase/useCreateDatabase.test.tsx | 40 +++--- .../CreateDatabase/useCreateDatabase.ts | 15 +-- .../DownloadScript/DownloadScript.story.tsx | 5 +- .../DownloadScript/DownloadScript.tsx | 78 ++++++----- .../teleport/src/Discover/Database/util.ts | 121 +++++++----------- .../src/Discover/Database/utils.test.ts | 113 ++++++++++++++++ 6 files changed, 230 insertions(+), 142 deletions(-) create mode 100644 web/packages/teleport/src/Discover/Database/utils.test.ts diff --git a/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.test.tsx b/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.test.tsx index 080a3fabe0917..c4ea988b83282 100644 --- a/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.test.tsx +++ b/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.test.tsx @@ -87,7 +87,19 @@ const services = [ const testCases = [ { - name: 'match in multiple services', + name: 'match with a service', + newLabels: dbLabels, + services: [ + { + name: 'svc4', + matcherLabels: { env: ['prod'] }, + awsIdentity: emptyAwsIdentity, + }, + ], + expectedMatch: 'svc4', + }, + { + name: 'match among multple service', newLabels: dbLabels, services, expectedMatch: 'svc2', @@ -96,26 +108,14 @@ const testCases = [ name: 'no match despite matching all labels when a svc has a non-matching label', newLabels: dbLabels, services: [ - { - name: 'svc1', - matcherLabels: { os: ['windows', 'mac'], env: ['staging'] }, - awsIdentity: emptyAwsIdentity, - }, { name: 'svc2', matcherLabels: { os: ['windows', 'mac', 'linux'], - tag: ['v11.0.0'], - env: ['staging', 'prod'], fruit: ['apple', '*'], // the non-matching label }, awsIdentity: emptyAwsIdentity, }, - { - name: 'svc3', - matcherLabels: { env: ['prod'], fruit: ['orange'] }, - awsIdentity: emptyAwsIdentity, - }, ], expectedMatch: undefined, }, @@ -161,7 +161,7 @@ const testCases = [ name: 'svc2', matcherLabels: { os: ['linux', 'mac'], - '*': ['prod', 'apple', 'v11.0.0'], + '*': ['prod', 'apple'], }, awsIdentity: emptyAwsIdentity, }, @@ -174,7 +174,7 @@ const testCases = [ services: [ { name: 'svc1', - matcherLabels: { '*': ['windows', 'mac'] }, + matcherLabels: { '*': ['windows'] }, awsIdentity: emptyAwsIdentity, }, ], @@ -204,7 +204,7 @@ const testCases = [ name: 'svc1', matcherLabels: { fruit: ['*'], - os: ['mac'], + os: ['windows'], }, awsIdentity: emptyAwsIdentity, }, @@ -239,9 +239,11 @@ const testCases = [ }, ]; -test.each(testCases)('$name', ({ newLabels, services, expectedMatch }) => { - const foundSvc = findActiveDatabaseSvc(newLabels, services); - expect(foundSvc?.name).toEqual(expectedMatch); +describe('findActiveDatabaseSvc()', () => { + test.each(testCases)('$name', ({ newLabels, services, expectedMatch }) => { + const foundSvc = findActiveDatabaseSvc(newLabels, services); + expect(foundSvc?.name).toEqual(expectedMatch); + }); }); const newDatabaseReq: CreateDatabaseRequest = { diff --git a/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts b/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts index f71be652de845..11b5da7adf9aa 100644 --- a/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts +++ b/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts @@ -22,7 +22,7 @@ import { useDiscover } from 'teleport/Discover/useDiscover'; import { usePoll } from 'teleport/Discover/Shared/usePoll'; import { compareByString } from 'teleport/lib/util'; -import { matchLabels, makeLabelMaps } from '../util'; +import { matchLabels } from '../util'; import type { CreateDatabaseRequest, @@ -257,21 +257,10 @@ export function findActiveDatabaseSvc( return null; } - // Create maps for easy lookup and matching. - const { labelKeysToMatchMap, labelValsToMatchMap, labelToMatchSeenMap } = - makeLabelMaps(newDbLabels); - - const hasLabelsToMatch = newDbLabels.length > 0; for (let i = 0; i < dbServices.length; i++) { // Loop through the current service label keys and its value set. const currService = dbServices[i]; - const match = matchLabels({ - hasLabelsToMatch, - labelKeysToMatchMap, - labelValsToMatchMap, - labelToMatchSeenMap, - matcherLabels: currService.matcherLabels, - }); + const match = matchLabels(newDbLabels, currService.matcherLabels); if (match) { return currService; diff --git a/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.story.tsx b/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.story.tsx index 8758cb2cf20cd..62dc97e359394 100644 --- a/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.story.tsx +++ b/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.story.tsx @@ -55,7 +55,10 @@ export const InitWithLabels = () => { {...props} agentMeta={{ ...props.agentMeta, - agentMatcherLabels: [{ name: 'env', value: 'prod' }], + agentMatcherLabels: [ + { name: 'env', value: 'staging' }, + { name: 'os', value: 'windows' }, + ], }} /> diff --git a/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx b/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx index 56880b337eb4d..0c1a512fa877b 100644 --- a/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx +++ b/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx @@ -15,7 +15,7 @@ */ import React, { Suspense, useState } from 'react'; -import { Box, ButtonSecondary, Text } from 'design'; +import { Box, ButtonSecondary, Label as Pill, Text } from 'design'; import * as Icons from 'design/Icon'; import Validation, { useRule, Validator } from 'shared/components/Validation'; @@ -50,7 +50,7 @@ import { TextIcon, useShowHint, } from '../../Shared'; -import { makeLabelMaps, matchLabels } from '../util'; +import { matchLabels } from '../util'; import type { AgentStepProps } from '../../types'; @@ -58,12 +58,11 @@ export default function Container(props: AgentStepProps) { const hasDbLabels = props.agentMeta?.agentMatcherLabels?.length; const dbLabels = hasDbLabels ? props.agentMeta.agentMatcherLabels : []; const [labels, setLabels] = useState( - hasDbLabels - ? dbLabels - : // If user did not define any labels from previous step (create db) - // then the only way the agent will know how to pick up the - // new db is through asteriks. - [{ name: '*', value: '*', isFixed: true }] + // TODO(lisa): we will always be defaulting to asterisks, so + // instead of defining the default here, define it inside + // the LabelsCreator (component responsible for rendering + // label input boxes) + [{ name: '*', value: '*', isFixed: dbLabels.length === 0 }] ); const [showScript, setShowScript] = useState(false); @@ -230,11 +229,8 @@ export function DownloadScript( const Heading = () => { return ( <> -
Optionally Deploy a Database Service
+
Deploy a Database Service
- This step is optional if you already have a Teleport Database Service - running. -
On the host where you will run the Teleport Database Service, execute the generated command that will install and start Teleport with the appropriate configuration. @@ -256,18 +252,48 @@ export const Labels = ({ }) => { const { valid, message } = useRule(requireMatchingLabels(dbLabels, labels)); const hasError = !valid; - + const hasDbLabels = dbLabels.length > 0; return ( - Labels + Optionally Define Matcher Labels - At least one label is required to help this service identify your - database. + {!hasDbLabels && ( + <> + Since no labels were defined for the registered database from the + previous step, the matcher labels are defaulted to wildcard which + will allow this database service to match any database. + + )} + {hasDbLabels && ( + <> + Wildcard labels allows this database service to match any database. +
+ You can define your own labels that this database service will use + to identify your database. + + )}
+ {hasDbLabels && ( + + The registered database labels from previous step: + {dbLabels.map((label, index) => { + const labelText = `${label.name}: ${label.value}`; + return ( + + {labelText} + + ); + })} + + )} {hasError && ( @@ -289,9 +315,9 @@ const requireMatchingLabels = (dbLabels: AgentLabel[], agentLabels: AgentLabel[]) => () => { if (!hasMatchingLabels(dbLabels, agentLabels)) { return { - valid: false, - message: `Labels must match with the labels defined for the database resource. \ - To match any key, and/or any value, asteriks can be used.`, + valid: true, + message: `The matcher labels must be able to match with the labels defined for the registered database. \ + Use wildcards to match by any labels.`, }; } return { valid: true }; @@ -322,15 +348,5 @@ export function hasMatchingLabels( matcherLabels[l.name] = [...matcherLabels[l.name], l.value]; }); - // Create maps for easy lookup and matching. - const { labelKeysToMatchMap, labelValsToMatchMap, labelToMatchSeenMap } = - makeLabelMaps(dbLabels); - - return matchLabels({ - hasLabelsToMatch: dbLabels.length > 0, - labelKeysToMatchMap, - labelValsToMatchMap, - labelToMatchSeenMap, - matcherLabels, - }); + return matchLabels(dbLabels, matcherLabels); } diff --git a/web/packages/teleport/src/Discover/Database/util.ts b/web/packages/teleport/src/Discover/Database/util.ts index 6d9d8e3419035..5e71d3f9805b0 100644 --- a/web/packages/teleport/src/Discover/Database/util.ts +++ b/web/packages/teleport/src/Discover/Database/util.ts @@ -16,103 +16,68 @@ import type { AgentLabel } from 'teleport/services/agents'; -// makeLabelMaps makes a few lookup tables out of the label prop -// for easy lookup: -// - lookup table with label.name as key, and label.value as value -// - lookup table with label.value as key, and label.key as value -// - lookup table of flags with label.name as key, and booleans as value -// which serves to record seen labels. -export function makeLabelMaps(labels: AgentLabel[]) { - let labelKeysToMatchMap: Record = {}; - let labelValsToMatchMap: Record = {}; - let labelToMatchSeenMap: Record = {}; +export function matchLabels( + newDbLabels: AgentLabel[], + matcherLabels: Record +) { + // Sorting to match by asteriks sooner. + const entries = Object.entries({ ...matcherLabels }).sort(); - labels.forEach(label => { - labelKeysToMatchMap[label.name] = label.value; - labelToMatchSeenMap[label.name] = false; - labelValsToMatchMap[label.value] = label.name; - }); + if (!entries.length) { + return false; + } - return { labelKeysToMatchMap, labelValsToMatchMap, labelToMatchSeenMap }; -} + // Create a map for db labels for easy lookup. + let dbKeyMap = {}; + let dbValMap = {}; -// matchLabels will go through each `matcherlabels` and record matched labels. -// If all labels are matched (or all asteriks exists in `matcherLabels`), -// returns true. -// -// It will return false when: -// - there were no labels to match -// - `matcherLabel` contains a label that isn't seen in the `xxxToMatchMap` -export function matchLabels({ - hasLabelsToMatch, - matcherLabels, - labelKeysToMatchMap, - labelValsToMatchMap, - labelToMatchSeenMap, -}: { - hasLabelsToMatch: boolean; - matcherLabels: Record; - labelKeysToMatchMap: Record; - labelValsToMatchMap: Record; - labelToMatchSeenMap: Record; -}) { - const matchedLabelMap = { ...labelToMatchSeenMap }; - // Sorted to have asteriks be the first label key to test. - const entries = Object.entries({ ...matcherLabels }).sort(); - for (const [key, vals] of entries) { - // Check if the label contains asteriks, which means match all eg: - // a service with match all can pick up any database regardless of other labels + newDbLabels.forEach(label => { + dbKeyMap[label.name] = label.value; + dbValMap[label.value] = label.name; + }); + + // All matching labels must make a match with the new database labels. + let matched = true; + for (let i = 0; i < entries.length; i++) { + const [key, vals] = entries[i]; + // Check if this label set contains asteriks, which means match all. + // A service with match all can pick up any database regardless of other labels // or no labels. const foundAsterikAsValue = vals.includes('*'); if (key === '*' && foundAsterikAsValue) { return true; } - if (!hasLabelsToMatch) { - return false; + // If no newDbLabels labels were defined, there are no matches to make, + // which makes this service not a match. + if (!newDbLabels.length) { + matched = false; + break; } - // Start matching by value. + // Start matching by values. - // This means any key is fine, as long as value matches. - if (key === '*') { - let found = false; - vals.forEach(val => { - const key = labelValsToMatchMap[val]; - if (key) { - matchedLabelMap[key] = true; - found = true; - } - }); - if (found) { - continue; - } + // This means any key is fine, as long as a value matches. + if (key === '*' && vals.find(val => dbValMap[val])) { + continue; } // This means any value is fine, as long as a key matches. - // Note that db resource labels can't have duplicate keys - // (but db service can). - else if (foundAsterikAsValue && labelKeysToMatchMap[key]) { - matchedLabelMap[key] = true; + if (foundAsterikAsValue && dbKeyMap[key]) { continue; } - // Match against actual values of key and its value. - else { - const dbVal = labelKeysToMatchMap[key]; - if (dbVal && vals.find(val => val === dbVal)) { - matchedLabelMap[key] = true; - continue; - } + // Match against key and value. + const dbVal = dbKeyMap[key]; + if (dbVal && vals.find(val => val === dbVal)) { + continue; } - // At this point, the current label did not match any criteria, - // we can abort, since it takes only one mismatch to fail. - return false; - } + // No matches were found for this label set which + // means this service not a match. + matched = false; + break; + } // label set loop - return ( - hasLabelsToMatch && - Object.keys(matchedLabelMap).every(key => matchedLabelMap[key]) - ); + return matched; } diff --git a/web/packages/teleport/src/Discover/Database/utils.test.ts b/web/packages/teleport/src/Discover/Database/utils.test.ts new file mode 100644 index 0000000000000..a606758ecb811 --- /dev/null +++ b/web/packages/teleport/src/Discover/Database/utils.test.ts @@ -0,0 +1,113 @@ +/** + * Copyright 2023 Gravitational, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { matchLabels } from './util'; + +const newDbLabels = [ + { name: 'env', value: 'prod' }, + { name: 'os', value: 'mac' }, + { name: 'tag', value: 'v11.0.0' }, +]; + +const testCases = [ + { + name: 'match multiple by exact keys and values', + match: true, + matcherLabels: { + env: ['prod'], + os: ['mac'], + tag: ['v11.0.0'], + }, + }, + { + name: 'match by multivalues', + match: true, + matcherLabels: { + env: ['prod', 'staging'], + os: ['windows', 'mac', 'linux'], + tag: ['1', '2', '3', 'v11.0.0'], + }, + }, + { + name: 'match with one label set', + match: true, + matcherLabels: { + tag: ['v1', 'v11.0.0', 'v2'], + }, + }, + { + name: 'match by asteriks', + match: true, + matcherLabels: { + env: ['na'], + os: ['na'], + '*': ['*'], + }, + }, + { + name: 'match with a key and value asterik', + match: true, + matcherLabels: { + '*': ['prod', 'staging'], + os: ['*'], + }, + }, + { + name: 'match by asteriks with no db labels defined', + noDbLabels: true, + match: true, + matcherLabels: { '*': ['*'] }, + }, + { + name: 'no match with no db labels defined and no matcher labels', + noDbLabels: true, + match: false, + matcherLabels: {}, + }, + { + name: 'no match with no db labels with matcher labels', + noDbLabels: true, + match: false, + matcherLabels: { os: ['mac'] }, + }, + { + name: 'no match despite other matching labels', + match: false, + matcherLabels: { + '*': ['no-match'], // no match + env: ['prod'], + os: ['mac'], + tag: ['v11.0.0'], + }, + }, + { + name: 'no match with empty labels', + match: false, + matcherLabels: {}, + }, + { + name: 'no match with empty label values', + match: false, + matcherLabels: { os: [] }, + }, +]; + +describe('matchLabels()', () => { + test.each(testCases)('$name', ({ matcherLabels, match, noDbLabels }) => { + const isMatched = matchLabels(noDbLabels ? [] : newDbLabels, matcherLabels); + expect(isMatched).toEqual(match); + }); +}); From 6b2b993312e1822e194842f70189da541a9d6f14 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Fri, 21 Apr 2023 09:07:32 -0700 Subject: [PATCH 08/13] Implement resuming discover flow from where user left of --- .../ConnectAwsAccount/ConnectAwsAccount.tsx | 33 +++-- .../teleport/src/Discover/Discover.tsx | 2 +- web/packages/teleport/src/Discover/flow.tsx | 4 +- .../teleport/src/Discover/useDiscover.tsx | 125 +++++++++++++++--- 4 files changed, 132 insertions(+), 32 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/ConnectAwsAccount/ConnectAwsAccount.tsx b/web/packages/teleport/src/Discover/Database/ConnectAwsAccount/ConnectAwsAccount.tsx index a5de9dd826ee0..abb2d98062402 100644 --- a/web/packages/teleport/src/Discover/Database/ConnectAwsAccount/ConnectAwsAccount.tsx +++ b/web/packages/teleport/src/Discover/Database/ConnectAwsAccount/ConnectAwsAccount.tsx @@ -18,7 +18,7 @@ import React, { useEffect, useState } from 'react'; import { Link } from 'react-router-dom'; import { Box, - ButtonLink, + ButtonText, Text, ButtonPrimary, Indicator, @@ -42,12 +42,19 @@ import useTeleport from 'teleport/useTeleport'; import { ActionButtons, HeaderSubtitle, HeaderWithBackBtn } from '../../Shared'; -import { DbMeta, useDiscover } from '../../useDiscover'; +import { DbMeta, DiscoverUrlLocState, useDiscover } from '../../useDiscover'; export function ConnectAwsAccount() { const { storeUser } = useTeleport(); - const { prevStep, nextStep, agentMeta, updateAgentMeta, eventState } = - useDiscover(); + const { + prevStep, + nextStep, + agentMeta, + updateAgentMeta, + eventState, + resourceSpec, + currentStep, + } = useDiscover(); const integrationAccess = storeUser.getIntegrationsAccess(); const databaseAccess = storeUser.getDatabaseAccess(); @@ -140,14 +147,24 @@ export function ConnectAwsAccount() { integrationName: selectedAwsIntegration.value, }); - // TODO(lisa): Need to add a new event to emit for this screen. nextStep(); } const hasAwsIntegrations = awsIntegrations.length > 0; + + // When a user clicks to create a new AWS integration, we + // define location state to preserve all the states required + // to resume from this step when the user comes back to discover route + // after successfully finishing enrolling integration. const locationState = { pathname: cfg.getIntegrationEnrollRoute(IntegrationKind.AwsOidc), - state: { discoverEventId: eventState?.id }, + state: { + discover: { + eventState, + resourceSpec, + currentStep, + }, + } as DiscoverUrlLocState, }; return ( @@ -174,9 +191,9 @@ export function ConnectAwsAccount() { options={awsIntegrations} /> - + Or click here to set up a different AWS account - + ) : ( diff --git a/web/packages/teleport/src/Discover/flow.tsx b/web/packages/teleport/src/Discover/flow.tsx index edf5ba96d7cc0..06e1ef953b0fa 100644 --- a/web/packages/teleport/src/Discover/flow.tsx +++ b/web/packages/teleport/src/Discover/flow.tsx @@ -20,6 +20,8 @@ import { ResourceKind } from 'teleport/Discover/Shared'; import { AgentStepComponent } from 'teleport/Discover/types'; import { DiscoverEvent } from 'teleport/services/userEvent'; +import { ResourceSpec } from './SelectResource'; + type ViewFunction = (t: T) => View[]; export interface ResourceViewConfig { @@ -41,7 +43,7 @@ export interface ResourceViewConfig { // by "currentStep" param). // Not supplying a function is equivalent to always prompting // on exit or changing route. - shouldPrompt?: (currentStep: number) => boolean; + shouldPrompt?: (currentStep: number, resourceSpec: ResourceSpec) => boolean; } export interface View { diff --git a/web/packages/teleport/src/Discover/useDiscover.tsx b/web/packages/teleport/src/Discover/useDiscover.tsx index 875560ddf724a..b2ea5365e87e0 100644 --- a/web/packages/teleport/src/Discover/useDiscover.tsx +++ b/web/packages/teleport/src/Discover/useDiscover.tsx @@ -15,7 +15,7 @@ */ import React, { useContext, useState, useEffect, useCallback } from 'react'; -import { useHistory } from 'react-router'; +import { useHistory, useLocation } from 'react-router'; import { DiscoverEventStatus, @@ -66,6 +66,7 @@ type CustomEventInput = { eventName?: DiscoverEvent; eventResourceName?: DiscoverEventResource; autoDiscoverResourcesCount?: number; + selectedResourcesCount?: number; }; type DiscoverProviderProps = { @@ -73,18 +74,36 @@ type DiscoverProviderProps = { mockCtx?: DiscoverContextState; }; +// DiscoverUrlLocState define fields to preserve state between +// react routes (eg. in RDS database flow, it is required of user +// to create a AWS OIDC integration which requires changing route +// and then coming back to resume the flow.) +export type DiscoverUrlLocState = { + // discover contains the fields necessary to be able to resume + // the flow from where user left of. + discover: { + eventState: EventState; + resourceSpec: ResourceSpec; + currentStep: number; + }; + // integrationName is the name of the created integration + // resource name (eg: integration subkind "aws-oidc") + integrationName: string; +}; + const discoverContext = React.createContext(null); export function DiscoverProvider( props: React.PropsWithChildren ) { const history = useHistory(); + const location = useLocation(); const [currentStep, setCurrentStep] = useState(0); const [agentMeta, setAgentMeta] = useState(); const [resourceSpec, setResourceSpec] = useState(); const [viewConfig, setViewConfig] = useState(); - const [eventState, setEventState] = useState(); + const [eventState, setEventState] = useState({} as any); // indexedViews contains views of the selected resource where // each view has been assigned an index value. @@ -135,12 +154,14 @@ export function DiscoverProvider( }, [eventState, history.location.pathname, emitEvent]); useEffect(() => { - initEventState(); + if (location.state?.discover) { + resumeDiscoverFlow(); + } else { + initEventState(); + } }, []); function initEventState() { - // Generates a v4 UUID using a cryptographically secure - // random number. const id = crypto.randomUUID(); setEventState({ @@ -160,11 +181,46 @@ export function DiscoverProvider( }); } - // onSelectResources initializes all the required - // variables needed to start a guided flow. + // If a location.state.discover was provided, that means the user is + // coming back from another location to resume the flow. + // Users will resume at the step that is +1 from the step they left from. + // + // Example (only applies to AWS RDS & Aurora resources): + // A user can leave from route `web/discover/` + // to `web/integrations/enroll/` then + // come back to resume flow at `web/discover/` + // + // Resuming flow at `Enroll RDS Database` means the user has + // successfully finished the prior `Connect AWS Account` step, + // so we emit a success event for that step. + // + // The location.state.discover should contain all the state that allows + // the user to resume from where they left of. + function resumeDiscoverFlow() { + const { discover, integrationName } = location.state; + + updateAgentMeta({ integrationName } as DbMeta); + + startDiscoverFlow( + discover.resourceSpec, + discover.eventState, + discover.currentStep + 1 + ); + + emitEvent( + { stepStatus: DiscoverEventStatus.Success }, + { + eventName: discover.eventState.currEventName, + eventResourceName: discover.resourceSpec.event, + } + ); + } + + // onSelectResources inits states, starts flow, and + // emits events. function onSelectResource(resource: ResourceSpec) { - // We still want to emit an event if user clicked on - // unguided links to gather data on which unguided resource + // We still want to emit an event if user clicked on an + // unguided link to gather data on which unguided resource // is most popular. if (resource.unguidedLink) { emitEvent( @@ -177,6 +233,27 @@ export function DiscoverProvider( return; } + startDiscoverFlow(resource, eventState); + + // At this point it's considered the user has + // successfully selected a resource, so we send an event + // for it. + emitEvent( + { stepStatus: DiscoverEventStatus.Success }, + { + eventName: DiscoverEvent.ResourceSelection, + eventResourceName: resource.event, + } + ); + } + + // startDiscoverFlow sets all the required states + // that will begin the flow. + function startDiscoverFlow( + resource: ResourceSpec, + initEventState: EventState, + targetViewIndex = 0 + ) { // Process each view and assign each with an index number. const currCfg = viewConfigs.find(r => r.kind === resource.kind); let indexedViews = []; @@ -186,30 +263,22 @@ export function DiscoverProvider( indexedViews = addIndexToViews(currCfg.views); } - // Find the first view to update the event state. + // Find the target view to update the event state. const { eventName, manuallyEmitSuccessEvent } = findViewAtIndex( indexedViews, - currentStep - ); - // At this point it's considered the user has - // successfully selected a resource, so we send an event. - emitEvent( - { stepStatus: DiscoverEventStatus.Success }, - { - eventName: DiscoverEvent.ResourceSelection, - eventResourceName: resource.event, - } + targetViewIndex ); // Init all required states to start the flow. setEventState({ - ...eventState, + ...initEventState, currEventName: eventName, manuallyEmitSuccessEvent, }); setViewConfig(currCfg); setIndexedViews(indexedViews); setResourceSpec(resource); + setCurrentStep(targetViewIndex); } // nextStep takes the user to next screen and sends reporting events. @@ -247,7 +316,17 @@ export function DiscoverProvider( if (!numToIncrement) { emitEvent({ stepStatus: DiscoverEventStatus.Skipped }); } else if (!eventState.manuallyEmitSuccessEvent) { - emitEvent({ stepStatus: DiscoverEventStatus.Success }); + // TODO(lisa): Currently the RDS enroll screen only allows + // user to select one RDS database to enroll so we hard code + // it for now. + if (eventState.currEventName === DiscoverEvent.DatabaseRDSEnrollEvent) { + emitEvent( + { stepStatus: DiscoverEventStatus.Success }, + { selectedResourcesCount: 1 } + ); + } else { + emitEvent({ stepStatus: DiscoverEventStatus.Success }); + } } // Whenever a numToIncrement is > 1, it means some steps (after the current view) @@ -342,6 +421,8 @@ export type NodeMeta = BaseMeta & { // DbMeta describes the fields for a db resource // that needs to be preserved throughout the flow. export type DbMeta = BaseMeta & { + // TODO(lisa): when we can enroll multiple RDS's, turn this into an array? + // The enroll event expects num count of enrolled RDS's, update accordingly. db: Database; integrationName?: string; }; From 503ae8951ead3a12dc09aa2e0a9ae5a0aa0280f7 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Fri, 21 Apr 2023 09:08:04 -0700 Subject: [PATCH 09/13] Enable integration access and rds flow --- .../teleport/src/Discover/Database/index.tsx | 30 +++++++++++++------ .../teleport/src/services/user/makeAcl.ts | 5 +--- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/index.tsx b/web/packages/teleport/src/Discover/Database/index.tsx index b811f6754afb8..fc8b494ddf463 100644 --- a/web/packages/teleport/src/Discover/Database/index.tsx +++ b/web/packages/teleport/src/Discover/Database/index.tsx @@ -29,14 +29,26 @@ import { SetupAccess } from 'teleport/Discover/Database/SetupAccess'; import { DownloadScript } from 'teleport/Discover/Database/DownloadScript'; import { MutualTls } from 'teleport/Discover/Database/MutualTls'; import { TestConnection } from 'teleport/Discover/Database/TestConnection'; -import { IamPolicy } from 'teleport/Discover/Database/IamPolicy'; import { DiscoverEvent } from 'teleport/services/userEvent'; +import { ConnectAwsAccount } from 'teleport/Discover/Database/ConnectAwsAccount'; +import { EnrollRdsDatabase } from 'teleport/Discover/Database/EnrollRdsDatabase'; export const DatabaseResource: ResourceViewConfig = { kind: ResourceKind.Database, wrapper(component: React.ReactNode) { return {component}; }, + shouldPrompt(currentStep, resourceSpec) { + if (resourceSpec.dbMeta?.location === DatabaseLocation.Aws) { + // Allow user to bypass prompting on this step (Connect AWS Connect) + // on exit because users might need to change route to setup an + // integration. + if (currentStep === 0) { + return false; + } + } + return true; + }, views(resource) { let configureResourceViews; if (resource && resource.dbMeta) { @@ -44,20 +56,20 @@ export const DatabaseResource: ResourceViewConfig = { case DatabaseLocation.Aws: configureResourceViews = [ { - title: 'Register a Database', - component: CreateDatabase, - eventName: DiscoverEvent.DatabaseRegister, + title: 'Connect AWS Account', + component: ConnectAwsAccount, + eventName: DiscoverEvent.IntegrationAWSOIDCConnectEvent, + }, + { + title: 'Enroll RDS Database', + component: EnrollRdsDatabase, + eventName: DiscoverEvent.DatabaseRDSEnrollEvent, }, { title: 'Deploy Database Service', component: DownloadScript, eventName: DiscoverEvent.DeployService, }, - { - title: 'Configure IAM Policy', - component: IamPolicy, - eventName: DiscoverEvent.DatabaseConfigureIAMPolicy, - }, ]; break; diff --git a/web/packages/teleport/src/services/user/makeAcl.ts b/web/packages/teleport/src/services/user/makeAcl.ts index d860e976eb2b8..9f15b8a91692d 100644 --- a/web/packages/teleport/src/services/user/makeAcl.ts +++ b/web/packages/teleport/src/services/user/makeAcl.ts @@ -31,10 +31,7 @@ export function makeAcl(json): Acl { const accessRequests = json.accessRequests || defaultAccess; const billing = json.billing || defaultAccess; const plugins = json.plugins || defaultAccess; - // TODO(lisa): requires backend changes to user context. - // Feature is off until all TODO related to integrations is done. - // const integrations = json.integrations || defaultAccessWithUse; - const integrations = defaultAccessWithUse; + const integrations = json.integrations || defaultAccessWithUse; const dbServers = json.dbServers || defaultAccess; const db = json.db || defaultAccess; const desktops = json.desktops || defaultAccess; From 8f34ecd466b550ed69676f2662cf8ee9bef8c7ea Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Fri, 21 Apr 2023 09:16:48 -0700 Subject: [PATCH 10/13] Strip 443 ports from cluster uri --- .../instructions/SecondStageInstructions.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SecondStageInstructions.tsx b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SecondStageInstructions.tsx index 1e0b4b08dba22..df09c181d307a 100644 --- a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SecondStageInstructions.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SecondStageInstructions.tsx @@ -60,7 +60,7 @@ export function SecondStageInstructions(props: CommonInstructionsProps) { bash={false} lines={[ { - text: `https://${props.clusterPublicUri}`, + text: `https://${getClusterPublicUri(props.clusterPublicUri)}`, }, ]} /> @@ -111,3 +111,15 @@ export function SecondStageInstructions(props: CommonInstructionsProps) { ); } + +function getClusterPublicUri(uri: string) { + const uriParts = uri.split(':'); + const port = uriParts.length > 1 ? uriParts[1] : ''; + + // Strip 443 ports from uri. + if (port === '443') { + return uriParts[0]; + } + + return uri; +} From 523dd91e554a0371f08af0e4bf705b549bcafa7d Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Fri, 21 Apr 2023 10:45:03 -0700 Subject: [PATCH 11/13] Use the labels returned from polling db instaed --- .../Database/CreateDatabase/useCreateDatabase.test.tsx | 8 ++++---- .../Discover/Database/CreateDatabase/useCreateDatabase.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.test.tsx b/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.test.tsx index c4ea988b83282..97e1b1c05ef31 100644 --- a/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.test.tsx +++ b/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.test.tsx @@ -286,9 +286,9 @@ describe('registering new databases, mainly error checking', () => { jest .spyOn(userEventService, 'captureDiscoverEvent') .mockResolvedValue(null as never); // return value does not matter but required by ts - jest - .spyOn(teleCtx.databaseService, 'fetchDatabases') - .mockResolvedValue({ agents: [{ name: 'new-db' } as any] }); + jest.spyOn(teleCtx.databaseService, 'fetchDatabases').mockResolvedValue({ + agents: [{ name: 'new-db', labels: dbLabels } as any], + }); jest .spyOn(teleCtx.databaseService, 'createDatabase') .mockResolvedValue(null); // ret val not used @@ -341,7 +341,7 @@ describe('registering new databases, mainly error checking', () => { expect(discoverCtx.updateAgentMeta).toHaveBeenCalledWith({ resourceName: 'db-name', agentMatcherLabels: dbLabels, - db: { name: 'new-db' }, + db: { name: 'new-db', labels: dbLabels }, }); // Test the dynamic definition of nextStep is called with a number diff --git a/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts b/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts index 11b5da7adf9aa..afbcc75d995d8 100644 --- a/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts +++ b/web/packages/teleport/src/Discover/Database/CreateDatabase/useCreateDatabase.ts @@ -111,7 +111,7 @@ export function useCreateDatabase() { updateAgentMeta({ ...(agentMeta as DbMeta), resourceName: createdDb.name, - agentMatcherLabels: createdDb.labels, + agentMatcherLabels: dbPollingResult.labels, db: dbPollingResult, }); From b1217f8a16515128cc7b2bba1eadc63299b33488 Mon Sep 17 00:00:00 2001 From: Lisa Kim Date: Mon, 24 Apr 2023 12:57:12 -0700 Subject: [PATCH 12/13] Various touch ups - Make label matching error less confusing by showing error upon user trying to generate command - Make label messaging clearer - Emit errors when failing to fetch rds dbs --- .../DownloadScript/DownloadScript.tsx | 92 +++++++++++-------- .../EnrollRdsDatabase/EnrollRdsDatabase.tsx | 3 +- .../Server/SetupAccess/SetupAccess.tsx | 2 +- .../teleport/src/Discover/useDiscover.tsx | 12 ++- 4 files changed, 66 insertions(+), 43 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx b/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx index 0c1a512fa877b..a6d20e8ba3b3b 100644 --- a/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx +++ b/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx @@ -14,10 +14,10 @@ * limitations under the License. */ -import React, { Suspense, useState } from 'react'; +import React, { Suspense, useState, useEffect } from 'react'; import { Box, ButtonSecondary, Label as Pill, Text } from 'design'; import * as Icons from 'design/Icon'; -import Validation, { useRule, Validator } from 'shared/components/Validation'; +import Validation, { Validator } from 'shared/components/Validation'; import { CatchError } from 'teleport/components/CatchError'; import { @@ -98,17 +98,7 @@ export default function Container(props: AgentStepProps) { } > {!showScript && ( - - - - setShowScript(true)} - > - Generate Command - - null} disableProceed={true} /> - + )} {showScript && ( { - const { valid, message } = useRule(requireMatchingLabels(dbLabels, labels)); - const hasError = !valid; const hasDbLabels = dbLabels.length > 0; return ( Optionally Define Matcher Labels - + {!hasDbLabels && ( <> Since no labels were defined for the registered database from the - previous step, the matcher labels are defaulted to wildcard which + previous step, the matcher labels are defaulted to wildcards which will allow this database service to match any database. )} {hasDbLabels && ( <> - Wildcard labels allows this database service to match any database. + Default wildcards label allows this database service to match any + database.
You can define your own labels that this database service will use - to identify your database. + to identify your registered database. The labels you define must + match with the labels that was defined for the registered database + (from previous step): )}
{hasDbLabels && ( - - The registered database labels from previous step: + {dbLabels.map((label, index) => { const labelText = `${label.name}: ${label.value}`; return ( @@ -295,11 +287,12 @@ export const Labels = ({ setLabels={setLabels} disableBtns={disableBtns || dbLabels.length === 0} /> - - {hasError && ( - + + {showLabelMatchErr && ( + - {message} + The matcher labels must be able to match with the labels defined for + the registered database. Use wildcards to match by any labels. )} @@ -311,18 +304,6 @@ function createBashCommand(tokenId: string) { return `sudo bash -c "$(curl -fsSL ${cfg.getDbScriptUrl(tokenId)})"`; } -const requireMatchingLabels = - (dbLabels: AgentLabel[], agentLabels: AgentLabel[]) => () => { - if (!hasMatchingLabels(dbLabels, agentLabels)) { - return { - valid: true, - message: `The matcher labels must be able to match with the labels defined for the registered database. \ - Use wildcards to match by any labels.`, - }; - } - return { valid: true }; - }; - // hasMatchingLabels will go through each 'agentLabels' and find matches from // 'dbLabels'. The 'agentLabels' must have same amount of matching labels // with 'dbLabels' either with asteriks (match all) or by exact match. @@ -350,3 +331,40 @@ export function hasMatchingLabels( return matchLabels(dbLabels, matcherLabels); } + +function LoadedView({ labels, setLabels, dbLabels, setShowScript }) { + const [showLabelMatchErr, setShowLabelMatchErr] = useState(true); + + useEffect(() => { + // Turn off error once user changes labels. + if (showLabelMatchErr) { + setShowLabelMatchErr(false); + } + }, [labels]); + + function handleGenerateCommand() { + if (!hasMatchingLabels(dbLabels, labels)) { + setShowLabelMatchErr(true); + return; + } + + setShowLabelMatchErr(false); + setShowScript(true); + } + + return ( + + + + + Generate Command + + null} disableProceed={true} /> + + ); +} diff --git a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx index eee0f73d4e3b2..b7ed9612bc2dd 100644 --- a/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx +++ b/web/packages/teleport/src/Discover/Database/EnrollRdsDatabase/EnrollRdsDatabase.tsx @@ -62,7 +62,7 @@ export function EnrollRdsDatabase() { nextStep, } = useCreateDatabase(); - const { agentMeta, resourceSpec } = useDiscover(); + const { agentMeta, resourceSpec, emitErrorEvent } = useDiscover(); const { attempt: fetchDbAttempt, setAttempt: setFetchDbAttempt } = useAttempt(''); @@ -115,6 +115,7 @@ export function EnrollRdsDatabase() { .catch((err: Error) => { setFetchDbAttempt({ status: 'failed', statusText: err.message }); setTableData(data); // fallback to previous data + emitErrorEvent(`failed to fetch aws rds list: ${err.message}`); }); } diff --git a/web/packages/teleport/src/Discover/Server/SetupAccess/SetupAccess.tsx b/web/packages/teleport/src/Discover/Server/SetupAccess/SetupAccess.tsx index 8a802c1a191ec..06c617c07b275 100644 --- a/web/packages/teleport/src/Discover/Server/SetupAccess/SetupAccess.tsx +++ b/web/packages/teleport/src/Discover/Server/SetupAccess/SetupAccess.tsx @@ -72,7 +72,7 @@ export function SetupAccess(props: State) { const hasTraits = selectedLogins.length > 0; const canAddTraits = !props.isSsoUser && props.canEditUser; const headerSubtitle = - 'Select the OS users you will use to connect to server.'; + 'Select or create the OS users you will use to connect to server.'; return ( Date: Tue, 25 Apr 2023 12:04:33 -0700 Subject: [PATCH 13/13] Address CR and update test --- .../Database/ConnectAwsAccount/ConnectAwsAccount.tsx | 8 ++++++-- .../Discover/Database/DownloadScript/DownloadScript.tsx | 6 +++--- .../src/Discover/Database/SetupAccess/SetupAccess.tsx | 2 +- web/packages/teleport/src/Discover/useDiscover.test.tsx | 2 ++ web/packages/teleport/src/Discover/useDiscover.tsx | 8 ++++---- .../AwsOidc/instructions/FifthStageInstructions.tsx | 2 +- .../Enroll/AwsOidc/instructions/Instructions.story.tsx | 4 ++-- .../AwsOidc/instructions/SeventhStageInstructions.tsx | 4 ++-- 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/web/packages/teleport/src/Discover/Database/ConnectAwsAccount/ConnectAwsAccount.tsx b/web/packages/teleport/src/Discover/Database/ConnectAwsAccount/ConnectAwsAccount.tsx index abb2d98062402..21873c62500fb 100644 --- a/web/packages/teleport/src/Discover/Database/ConnectAwsAccount/ConnectAwsAccount.tsx +++ b/web/packages/teleport/src/Discover/Database/ConnectAwsAccount/ConnectAwsAccount.tsx @@ -42,7 +42,11 @@ import useTeleport from 'teleport/useTeleport'; import { ActionButtons, HeaderSubtitle, HeaderWithBackBtn } from '../../Shared'; -import { DbMeta, DiscoverUrlLocState, useDiscover } from '../../useDiscover'; +import { + DbMeta, + DiscoverUrlLocationState, + useDiscover, +} from '../../useDiscover'; export function ConnectAwsAccount() { const { storeUser } = useTeleport(); @@ -164,7 +168,7 @@ export function ConnectAwsAccount() { resourceSpec, currentStep, }, - } as DiscoverUrlLocState, + } as DiscoverUrlLocationState, }; return ( diff --git a/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx b/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx index a6d20e8ba3b3b..b45aa97e68c20 100644 --- a/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx +++ b/web/packages/teleport/src/Discover/Database/DownloadScript/DownloadScript.tsx @@ -261,8 +261,8 @@ export const Labels = ({
You can define your own labels that this database service will use to identify your registered database. The labels you define must - match with the labels that was defined for the registered database - (from previous step): + match the labels that were defined for the registered database (from + previous step): )}
@@ -292,7 +292,7 @@ export const Labels = ({ The matcher labels must be able to match with the labels defined for - the registered database. Use wildcards to match by any labels. + the registered database. Use wildcards to match with any labels. )}
diff --git a/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx b/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx index 18665dc9cab8c..595f05612dc8f 100644 --- a/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx +++ b/web/packages/teleport/src/Discover/Database/SetupAccess/SetupAccess.tsx @@ -207,7 +207,7 @@ function DbEngineInstructions({ return ( - Users must have a rds_iam role: + Users must have an rds_iam role: { resource: DiscoverEventResource.Server, stepStatus: DiscoverEventStatus.Error, stepStatusError: 'some error message', + selectedResourcesCount: 0, + autoDiscoverResourcesCount: 0, }, }) ); diff --git a/web/packages/teleport/src/Discover/useDiscover.tsx b/web/packages/teleport/src/Discover/useDiscover.tsx index 46041942a30f6..ec7db069ef4e6 100644 --- a/web/packages/teleport/src/Discover/useDiscover.tsx +++ b/web/packages/teleport/src/Discover/useDiscover.tsx @@ -74,13 +74,13 @@ type DiscoverProviderProps = { mockCtx?: DiscoverContextState; }; -// DiscoverUrlLocState define fields to preserve state between +// DiscoverUrlLocationState define fields to preserve state between // react routes (eg. in RDS database flow, it is required of user // to create a AWS OIDC integration which requires changing route // and then coming back to resume the flow.) -export type DiscoverUrlLocState = { +export type DiscoverUrlLocationState = { // discover contains the fields necessary to be able to resume - // the flow from where user left of. + // the flow from where user left off. discover: { eventState: EventState; resourceSpec: ResourceSpec; @@ -97,7 +97,7 @@ export function DiscoverProvider( props: React.PropsWithChildren ) { const history = useHistory(); - const location = useLocation(); + const location = useLocation(); const [currentStep, setCurrentStep] = useState(0); const [agentMeta, setAgentMeta] = useState(); diff --git a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/FifthStageInstructions.tsx b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/FifthStageInstructions.tsx index 87b598a2270e0..23f13b8cfd161 100644 --- a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/FifthStageInstructions.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/FifthStageInstructions.tsx @@ -35,7 +35,7 @@ export function FifthStageInstructions(props: CommonInstructionsProps) { "Action": [ "rds:DescribeDBInstances", "rds:DescribeDBClusters" - ] + ], "Resource": "*" } ] diff --git a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/Instructions.story.tsx b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/Instructions.story.tsx index 45be4f2fa87d8..2aab67528b761 100644 --- a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/Instructions.story.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/Instructions.story.tsx @@ -28,7 +28,7 @@ import { SuccessfullyAddedIntegrationDialog, } from './SeventhStageInstructions'; -import type { DiscoverUrlLocState } from 'teleport/Discover/useDiscover'; +import type { DiscoverUrlLocationState } from 'teleport/Discover/useDiscover'; export default { title: 'Teleport/Integrations/Enroll/AwsOidc/Instructions', @@ -54,7 +54,7 @@ export const ConfirmDialog = () => ( export const ConfirmDialogFromDiscover = () => ( diff --git a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SeventhStageInstructions.tsx b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SeventhStageInstructions.tsx index 9ee4b6b354c33..d3cc7a3fbd1b5 100644 --- a/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SeventhStageInstructions.tsx +++ b/web/packages/teleport/src/Integrations/Enroll/AwsOidc/instructions/SeventhStageInstructions.tsx @@ -37,7 +37,7 @@ import { integrationService, } from 'teleport/services/integrations'; import cfg from 'teleport/config'; -import { DiscoverUrlLocState } from 'teleport/Discover/useDiscover'; +import { DiscoverUrlLocationState } from 'teleport/Discover/useDiscover'; import { InstructionsContainer } from './common'; @@ -119,7 +119,7 @@ export function SuccessfullyAddedIntegrationDialog({ }: { integrationName: string; }) { - const location = useLocation(); + const location = useLocation(); return (