Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
IntegrationKind,
integrationService,
} from 'teleport/services/integrations';
import { integrationRWE } from 'teleport/Discover/yamlTemplates';
import { integrationRWEAndDbCU } from 'teleport/Discover/yamlTemplates';
import useTeleport from 'teleport/useTeleport';

import { ActionButtons, HeaderSubtitle, HeaderWithBackBtn } from '../../Shared';
Expand All @@ -49,10 +49,14 @@ export function ConnectAwsAccount() {
const { prevStep, nextStep, agentMeta, updateAgentMeta, eventState } =
useDiscover();

// TODO(lisa): also need to check for verb `use` which is pending
// work.
const access = storeUser.getIntegrationsAccess();
const hasAccess = access.create && access.list;
const integrationAccess = storeUser.getIntegrationsAccess();
const databaseAccess = storeUser.getDatabaseAccess();
const hasAccess =
integrationAccess.create &&
integrationAccess.list &&
// Required access after integrating:
integrationAccess.use && // required to list AWS RDS db's
databaseAccess.create; // required to enroll AWS RDS db
const { attempt, run } = useAttempt(hasAccess ? 'processing' : '');

const [awsIntegrations, setAwsIntegrations] = useState<Option[]>([]);
Expand Down Expand Up @@ -95,7 +99,7 @@ export function ConnectAwsAccount() {
<Flex minHeight="215px" mt={3}>
<TextEditor
readOnly={true}
data={[{ content: integrationRWE, type: 'yaml' }]}
data={[{ content: integrationRWEAndDbCU, type: 'yaml' }]}
/>
</Flex>
</Box>
Expand Down Expand Up @@ -133,7 +137,7 @@ export function ConnectAwsAccount() {

updateAgentMeta({
...(agentMeta as DbMeta),
awsIntegrationName: selectedAwsIntegration.value,
integrationName: selectedAwsIntegration.value,
});

// TODO(lisa): Need to add a new event to emit for this screen.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,6 @@ export const InitSelfHostedMySql = () => (
</MemoryRouter>
);

export const InitAws = () => (
<MemoryRouter>
<CreateDatabaseView {...props} dbLocation={DatabaseLocation.Aws} />
</MemoryRouter>
);

export const NoPerm = () => (
<MemoryRouter>
<CreateDatabaseView {...props} canCreateDatabase={false} />
Expand Down Expand Up @@ -80,4 +74,6 @@ const props: State = {
dbLocation: DatabaseLocation.SelfHosted,
isDbCreateErr: false,
prevStep: () => null,
nextStep: () => null,
createdDb: {} as any,
};
Original file line number Diff line number Diff line change
Expand Up @@ -15,48 +15,33 @@
*/

import React, { useState, useEffect } from 'react';
import {
Text,
Box,
Flex,
AnimatedProgressBar,
ButtonPrimary,
ButtonSecondary,
} from 'design';
import { Text, Box, Flex } from 'design';

import Dialog, { DialogContent } from 'design/DialogConfirmation';
import * as Icons from 'design/Icon';
import Validation, { Validator } from 'shared/components/Validation';
import FieldInput from 'shared/components/FieldInput';
import { requiredField } from 'shared/components/Validation/rules';
import TextEditor from 'shared/components/TextEditor';

import { Timeout } from 'teleport/Discover/Shared/Timeout';

import {
ActionButtons,
HeaderSubtitle,
LabelsCreater,
Mark,
TextIcon,
HeaderWithBackBtn,
} from '../../Shared';
import { dbCU } from '../../yamlTemplates';
import {
DatabaseLocation,
getDatabaseProtocol,
getDefaultDatabasePort,
} from '../../SelectResource';

import { useCreateDatabase, State } from './useCreateDatabase';
import { CreateDatabaseDialog } from './CreateDatabaseDialog';

import type { AgentStepProps } from '../../types';
import type { AgentLabel } from 'teleport/services/agents';
import type { Attempt } from 'shared/hooks/useAttemptNext';
import type { AwsRds } from 'teleport/services/databases';

export function CreateDatabase(props: AgentStepProps) {
const state = useCreateDatabase(props);
export function CreateDatabase() {
const state = useCreateDatabase();
return <CreateDatabaseView {...state} />;
}

Expand All @@ -67,19 +52,15 @@ export function CreateDatabaseView({
canCreateDatabase,
pollTimeout,
dbEngine,
dbLocation,
isDbCreateErr,
prevStep,
nextStep,
}: State) {
const [dbName, setDbName] = useState('');
const [dbUri, setDbUri] = useState('');
const [labels, setLabels] = useState<AgentLabel[]>([]);
const [dbPort, setDbPort] = useState(getDefaultDatabasePort(dbEngine));

// TODO(lisa): refactor using ryan's example (reusable).
const [awsAccountId, setAwsAccountId] = useState('');
const [awsResourceId, setAwsResourceId] = useState('');

const [finishedFirstStep, setFinishedFirstStep] = useState(false);

useEffect(() => {
Expand All @@ -102,24 +83,14 @@ export function CreateDatabaseView({
return;
}

let awsRds: AwsRds;
if (dbLocation === DatabaseLocation.Aws) {
awsRds = {
accountId: awsAccountId,
resourceId: awsResourceId,
};
}

registerDatabase({
labels,
name: dbName,
uri: `${dbUri}:${dbPort}`,
protocol: getDatabaseProtocol(dbEngine),
awsRds,
});
}

const isAws = dbLocation === DatabaseLocation.Aws;
return (
<Validation>
{({ validator }) => (
Expand Down Expand Up @@ -167,33 +138,13 @@ export function CreateDatabaseView({
<FieldInput
autoFocus
label="Database Connection Endpoint"
rule={
isAws
? requireAwsEndpoint
: requiredField('connection endpoint is required')
}
rule={requiredField('connection endpoint is required')}
value={dbUri}
placeholder={
isAws
? 'db.example.us-west-1.rds.amazonaws.com'
: 'db.example.com'
}
placeholder="db.example.com"
onChange={e => setDbUri(e.target.value)}
width="70%"
mr={2}
toolTipContent={
isAws ? (
<Text>
Database location and connection information.
Typically in the format:{' '}
<Mark
light
>{`<your-db-identifier>.<random-id>.<your-region>.rds.amazonaws.com`}</Mark>
</Text>
) : (
'Database location and connection information.'
)
}
toolTipContent="Database location and connection information."
/>
<FieldInput
label="Endpoint Port"
Expand All @@ -204,38 +155,6 @@ export function CreateDatabaseView({
width="30%"
/>
</Flex>
{dbLocation === DatabaseLocation.Aws && (
<>
<Box width="500px">
<FieldInput
label="AWS Account ID"
rule={requiredAwsAccountId}
value={awsAccountId}
placeholder="123456789012"
onChange={e => setAwsAccountId(e.target.value)}
toolTipContent="A 12-digit number that uniquely identifies your AWS account."
/>
</Box>
<Box width="500px" mb={6}>
<FieldInput
label="Resource ID"
value={awsResourceId}
rule={requiredField(
'database resource ID is required'
)}
placeholder="db-ABCDE1234567..."
onChange={e => setAwsResourceId(e.target.value)}
toolTipContent={
<Text>
The unique identifier for your resource. May have
the prefix <Mark light>db-</Mark> then follow with
alphanumerics.
</Text>
}
/>
</Box>
</>
)}
<Box mt={3}>
<Text bold>Labels (optional)</Text>
<Text mb={2}>
Expand Down Expand Up @@ -263,12 +182,14 @@ export function CreateDatabaseView({
attempt.status === 'processing' || !canCreateDatabase
}
/>
{(attempt.status === 'processing' || attempt.status === 'failed') && (
{attempt.status !== '' && (
<CreateDatabaseDialog
pollTimeout={pollTimeout}
attempt={attempt}
retry={() => handleOnProceed(validator, true /* retry */)}
close={clearAttempt}
dbName={dbName}
next={nextStep}
/>
)}
</Box>
Expand All @@ -277,69 +198,6 @@ export function CreateDatabaseView({
);
}

const CreateDatabaseDialog = ({
pollTimeout,
attempt,
retry,
close,
}: {
pollTimeout: number;
attempt: Attempt;
retry(): void;
close(): void;
}) => {
return (
<Dialog disableEscapeKeyDown={false} open={true}>
<DialogContent
width="400px"
alignItems="center"
mb={0}
textAlign="center"
>
{attempt.status !== 'failed' ? (
<>
{' '}
<Text bold caps mb={4}>
Registering Database
</Text>
<AnimatedProgressBar />
<TextIcon
css={`
white-space: pre;
`}
>
<Icons.Restore fontSize={4} />
<Timeout
timeout={pollTimeout}
message=""
tailMessage={' seconds left'}
/>
</TextIcon>
</>
) : (
<Box width="100%">
<Text bold caps mb={3}>
Database Register Failed
</Text>
<Text mb={5}>
<Icons.Warning ml={1} mr={2} color="error.main" />
Error: {attempt.statusText}
</Text>
<Flex>
<ButtonPrimary mr={2} width="50%" onClick={retry}>
Retry
</ButtonPrimary>
<ButtonSecondary width="50%" onClick={close}>
Close
</ButtonSecondary>
</Flex>
</Box>
)}
</DialogContent>
</Dialog>
);
};

// Only allows digits with valid port range 1-65535.
const requirePort = (value: string) => () => {
const numberValue = parseInt(value);
Expand All @@ -355,59 +213,3 @@ const requirePort = (value: string) => () => {
valid: true,
};
};

// AWS_ACC_ID_REGEX only allows digits with length 12.
export const AWS_ACC_ID_REGEX = /^\d{12}$/;
const requiredAwsAccountId = value => () => {
const isValidId = value.match(AWS_ACC_ID_REGEX);
if (!isValidId) {
return {
valid: false,
message: 'aws account id must be 12 digits',
};
}
return {
valid: true,
};
};

const requireAwsEndpoint = value => () => {
const parts = value.split('.');
// Following possible format (bare mininum len has to be 6):
// (len 6) test.abcd.us-west-2.rds.amazonaws.com
// (len 7) test.abcd.suffix.us-west-2.rds.amazonaws.com
// (len 8) test.abcd.suffix.us-west-2.rds.amazonaws.com.cn
const hasCorrectLen = parts.length >= 6; // loosely match
if (!hasCorrectLen || !value.includes('.rds.amazonaws.com')) {
return {
valid: false,
message: 'invalid connection endpoint format',
};
}

return {
valid: true,
};
};

// TODO(lisa): this check and the backend check does not match
// re-visit and let backend do the checking for now.
//
// // AWS_POLICY_NAME_REGEX only allows alphanumeric including the
// // following common characters: plus (+), equal (=), comma (,),
// // period (.), at (@), underscore (_), and hyphen (-).
// // As defined in: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html
// export const AWS_POLICY_NAME_REGEX = /^[\w@+=,.:/-]+$/;
// const conformNameWithAWSPolicyNameReq = value => () => {
// const isValid = value.match(AWS_POLICY_NAME_REGEX);
// if (!isValid) {
// return {
// valid: false,
// message:
// 'name must be alphanumerics, including characters such as _ @ = , . + -',
// };
// }
// return {
// valid: true,
// };
// };
Loading