Skip to content

Commit 0cc411a

Browse files
authored
Merge pull request #6153 from Bit-Quill/muaydin/docdb-polling-bug-fix
feat(toolkit): Add DocumentDB support
2 parents 50c9121 + 874eb41 commit 0cc411a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+8116
-1
lines changed

package-lock.json

Lines changed: 2649 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/core/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,8 @@
499499
"@aws-sdk/client-cloudformation": "^3.667.0",
500500
"@aws-sdk/client-cloudwatch-logs": "^3.666.0",
501501
"@aws-sdk/client-cognito-identity": "^3.637.0",
502+
"@aws-sdk/client-docdb": "^3.699.0",
503+
"@aws-sdk/client-docdb-elastic": "^3.699.0",
502504
"@aws-sdk/client-lambda": "^3.637.0",
503505
"@aws-sdk/client-sso": "^3.342.0",
504506
"@aws-sdk/client-sso-oidc": "^3.574.0",

packages/core/package.nls.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,17 @@
233233
"AWS.command.apprunner.copyServiceUrl": "Copy Service URL",
234234
"AWS.command.apprunner.open": "Open in Browser",
235235
"AWS.command.apprunner.startDeployment": "Start Deployment",
236+
"AWS.command.docdb.createCluster": "Create DocumentDB Cluster",
237+
"AWS.command.docdb.createInstance": "Add Instance...",
238+
"AWS.command.docdb.modifyInstance": "Modify Class...",
239+
"AWS.command.docdb.rebootInstance": "Reboot Instance",
240+
"AWS.command.docdb.startCluster": "Start Cluster",
241+
"AWS.command.docdb.stopCluster": "Stop Cluster",
242+
"AWS.command.docdb.tags": "Tags...",
243+
"AWS.command.docdb.open": "Open in Browser",
244+
"AWS.command.docdb.openHelp": "Open Getting Started Guide",
245+
"AWS.command.docdb.copyEndpoint": "Copy Endpoint",
246+
"AWS.command.docdb.addRegion": "Add region...",
236247
"AWS.command.resources.copyIdentifier": "Copy Identifier",
237248
"AWS.command.resources.configure": "Show Resources...",
238249
"AWS.command.codewhisperer.introduction": "What is Amazon Q?",
@@ -272,6 +283,7 @@
272283
"AWS.generic.copyUrl": "Copy URL",
273284
"AWS.generic.promptDelete": "Delete...",
274285
"AWS.generic.promptUpdate": "Update...",
286+
"AWS.generic.promptRename": "Rename...",
275287
"AWS.generic.preview": "Preview",
276288
"AWS.generic.viewDocs": "View Documentation",
277289
"AWS.generic.moreActions": "More Actions...",

packages/core/src/awsexplorer/regionNode.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { StepFunctionsNode } from '../stepFunctions/explorer/stepFunctionsNodes'
2020
import { SsmDocumentNode } from '../ssmDocument/explorer/ssmDocumentNode'
2121
import { ResourcesNode } from '../dynamicResources/explorer/nodes/resourcesNode'
2222
import { AppRunnerNode } from '../awsService/apprunner/explorer/apprunnerNode'
23+
import { DocumentDBNode } from '../docdb/explorer/docdbNode'
24+
import { DefaultDocumentDBClient } from '../shared/clients/docdbClient'
2325
import { DefaultAppRunnerClient } from '../shared/clients/apprunnerClient'
2426
import { DefaultEcrClient } from '../shared/clients/ecrClient'
2527
import { DefaultRedshiftClient } from '../shared/clients/redshiftClient'
@@ -57,6 +59,10 @@ const serviceCandidates: ServiceNode[] = [
5759
serviceId: 'cloudformation',
5860
createFn: (regionCode: string) => new CloudFormationNode(regionCode),
5961
},
62+
{
63+
serviceId: 'docdb',
64+
createFn: (regionCode: string) => new DocumentDBNode(DefaultDocumentDBClient.create(regionCode)),
65+
},
6066
{
6167
serviceId: 'logs',
6268
createFn: (regionCode: string) => new CloudWatchLogsNode(regionCode),
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { Commands } from '../shared'
7+
import { ExtContext } from '../shared/extensions'
8+
import { DBResourceNode } from './explorer/dbResourceNode'
9+
import { DocumentDBNode } from './explorer/docdbNode'
10+
import { DBClusterNode } from './explorer/dbClusterNode'
11+
import { DBInstanceNode } from './explorer/dbInstanceNode'
12+
import { addRegion } from './commands/addRegion'
13+
import { createCluster } from './commands/createCluster'
14+
import { deleteCluster } from './commands/deleteCluster'
15+
import { renameCluster } from './commands/renameCluster'
16+
import { startCluster } from './commands/startCluster'
17+
import { stopCluster } from './commands/stopCluster'
18+
import { createInstance } from './commands/createInstance'
19+
import { deleteInstance } from './commands/deleteInstance'
20+
import { modifyInstance } from './commands/modifyInstance'
21+
import { rebootInstance } from './commands/rebootInstance'
22+
import { renameInstance } from './commands/renameInstance'
23+
import { addTag, listTags, removeTag } from './commands/tagCommands'
24+
import { Uri } from 'vscode'
25+
import { openUrl } from '../shared/utilities/vsCodeUtils'
26+
import { getLogger } from '../shared/logger'
27+
28+
/**
29+
* A utility function to automatically invoke trackChanges after a command.
30+
*/
31+
32+
function withTrackChanges<T extends DBResourceNode>(
33+
command: (node: T) => Promise<void>,
34+
commandName: string = 'UnnamedCommand'
35+
): (node: T) => Promise<void> {
36+
return async (node: T) => {
37+
const arn = node.arn || 'UnknownARN'
38+
const startTime = new Date().toISOString()
39+
40+
getLogger().info(
41+
`[${startTime}] Executing command "${commandName}" for resource with ARN: ${arn}. Tracking changes will be invoked post-execution.`
42+
)
43+
44+
await command(node)
45+
46+
const endTime = new Date().toISOString()
47+
getLogger().info(
48+
`[${endTime}] Successfully executed command "${commandName}" for resource with ARN: ${arn}. Invoking trackChanges now.`
49+
)
50+
51+
await node.trackChangesWithWaitProcessingStatus()
52+
}
53+
}
54+
55+
/**
56+
* Activates DocumentDB components.
57+
*/
58+
export async function activate(ctx: ExtContext): Promise<void> {
59+
ctx.extensionContext.subscriptions.push(
60+
Commands.register('aws.docdb.createCluster', async (node?: DocumentDBNode) => {
61+
await createCluster(node)
62+
}),
63+
64+
Commands.register('aws.docdb.deleteCluster', withTrackChanges<DBClusterNode>(deleteCluster, 'deleteCluster')),
65+
66+
Commands.register('aws.docdb.renameCluster', withTrackChanges<DBClusterNode>(renameCluster, 'renameCluster')),
67+
68+
Commands.register('aws.docdb.startCluster', withTrackChanges<DBClusterNode>(startCluster, 'startCluster')),
69+
70+
Commands.register('aws.docdb.stopCluster', withTrackChanges<DBClusterNode>(stopCluster, 'stopCluster')),
71+
72+
Commands.register('aws.docdb.addRegion', withTrackChanges<DBClusterNode>(addRegion, 'addRegion')),
73+
74+
Commands.register(
75+
'aws.docdb.createInstance',
76+
withTrackChanges<DBClusterNode>(createInstance, 'createInstance')
77+
),
78+
79+
Commands.register(
80+
'aws.docdb.deleteInstance',
81+
withTrackChanges<DBInstanceNode>(deleteInstance, 'deleteInstance')
82+
),
83+
84+
Commands.register(
85+
'aws.docdb.modifyInstance',
86+
withTrackChanges<DBInstanceNode>(modifyInstance, 'modifyInstance')
87+
),
88+
89+
Commands.register(
90+
'aws.docdb.rebootInstance',
91+
withTrackChanges<DBInstanceNode>(rebootInstance, 'rebootInstance')
92+
),
93+
94+
Commands.register(
95+
'aws.docdb.renameInstance',
96+
withTrackChanges<DBInstanceNode>(renameInstance, 'renameInstance')
97+
),
98+
99+
Commands.register('aws.docdb.listTags', async (node: DBResourceNode) => {
100+
await listTags(node)
101+
}),
102+
103+
Commands.register('aws.docdb.addTag', async (node: DBResourceNode) => {
104+
await addTag(node)
105+
}),
106+
107+
Commands.register('aws.docdb.removeTag', async (node: DBResourceNode) => {
108+
await removeTag(node)
109+
}),
110+
111+
Commands.register('aws.docdb.viewConsole', async (node?: DBResourceNode) => {
112+
await node?.openInBrowser()
113+
}),
114+
115+
Commands.register('aws.docdb.viewDocs', async () => {
116+
await openUrl(
117+
Uri.parse('https://docs.aws.amazon.com/documentdb/latest/developerguide/get-started-guide.html')
118+
)
119+
}),
120+
121+
Commands.register('aws.docdb.copyEndpoint', async (node?: DBResourceNode) => {
122+
await node?.copyEndpoint()
123+
})
124+
)
125+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import * as vscode from 'vscode'
7+
import { getLogger } from '../../shared/logger'
8+
import { telemetry } from '../../shared/telemetry'
9+
import { localize } from '../../shared/utilities/vsCodeUtils'
10+
import { DBClusterNode } from '../explorer/dbClusterNode'
11+
import { DBGlobalClusterNode } from '../explorer/dbGlobalClusterNode'
12+
import { DefaultDocumentDBClient } from '../../shared/clients/docdbClient'
13+
import { ToolkitError } from '../../shared'
14+
import { showViewLogsMessage } from '../../shared/utilities/messages'
15+
import { isValidResponse } from '../../shared/wizards/wizard'
16+
import { CancellationError } from '../../shared/utilities/timeoutUtils'
17+
import { CreateGlobalClusterWizard } from '../wizards/createGlobalClusterWizard'
18+
import { CreateDBClusterMessage } from '@aws-sdk/client-docdb'
19+
import { createInstancesForCluster } from './createCluster'
20+
import { isSupportedGlobalInstanceClass } from '../utils'
21+
22+
export async function addRegion(node: DBClusterNode | DBGlobalClusterNode): Promise<void> {
23+
if (!node) {
24+
throw new ToolkitError('No node specified for AddRegion')
25+
}
26+
27+
return telemetry.docdb_addRegion.run(async () => {
28+
let globalClusterName = undefined
29+
30+
if (node.cluster.StorageEncrypted) {
31+
void vscode.window.showErrorMessage('Encrypted clusters are not supported')
32+
return
33+
}
34+
35+
if (node instanceof DBClusterNode) {
36+
if (node.clusterRole !== 'regional') {
37+
void vscode.window.showErrorMessage('Only regional clusters are supported')
38+
return
39+
}
40+
41+
if (node.cluster.DBClusterMembers?.length === 0) {
42+
void vscode.window.showErrorMessage(
43+
localize(
44+
'AWS.docdb.addRegion.noInstances',
45+
'Cluster must have at least one instance to add a region'
46+
)
47+
)
48+
throw new ToolkitError('Cluster must have at least one instance to add a region', { cancelled: true })
49+
}
50+
51+
const unsupportedInstanceFound = node.instances.find(
52+
(instance) => !isSupportedGlobalInstanceClass(instance.DBInstanceClass!)
53+
)
54+
55+
if (unsupportedInstanceFound) {
56+
void vscode.window.showErrorMessage(
57+
localize(
58+
'AWS.docdb.addRegion.unsupportedInstanceClass',
59+
'Instance class {0} not supported for global cluster. Upgrade the instances then try again.',
60+
unsupportedInstanceFound.DBInstanceClass
61+
)
62+
)
63+
throw new ToolkitError('Instance class not supported for global cluster', {
64+
cancelled: true,
65+
code: 'docdbInstanceClassNotSupported',
66+
})
67+
}
68+
} else {
69+
globalClusterName = node.cluster.GlobalClusterIdentifier
70+
71+
if (node.cluster.GlobalClusterMembers!.length > 4) {
72+
void vscode.window.showErrorMessage(
73+
localize('AWS.docdb.addRegion.maxRegions', 'Global clusters can have a maximum of 5 regions')
74+
)
75+
throw new ToolkitError('Global clusters can have a maximum of 5 regions', {
76+
cancelled: true,
77+
code: 'docdbMaxRegionsInUse',
78+
})
79+
}
80+
}
81+
82+
if (!node.isAvailable) {
83+
void vscode.window.showErrorMessage(localize('AWS.docdb.clusterStopped', 'Cluster must be running'))
84+
throw new ToolkitError('Cluster not available', { cancelled: true, code: 'docdbClusterStopped' })
85+
}
86+
87+
const wizard = new CreateGlobalClusterWizard(node.regionCode, node.cluster.EngineVersion, node.client, {
88+
initState: { GlobalClusterName: globalClusterName },
89+
})
90+
const response = await wizard.run()
91+
92+
if (!isValidResponse(response)) {
93+
throw new CancellationError('user')
94+
}
95+
96+
const regionCode = response.RegionCode
97+
let input: CreateDBClusterMessage
98+
let clusterName = response.GlobalClusterName
99+
100+
try {
101+
if (node instanceof DBClusterNode) {
102+
// Create new global cluster from regional cluster
103+
const primaryCluster = node.cluster
104+
105+
getLogger().info(`docdb: Creating global cluster: ${clusterName}`)
106+
const globalCluster = await node.client.createGlobalCluster({
107+
GlobalClusterIdentifier: response.GlobalClusterName,
108+
SourceDBClusterIdentifier: primaryCluster.DBClusterArn,
109+
})
110+
111+
input = {
112+
GlobalClusterIdentifier: globalCluster?.GlobalClusterIdentifier,
113+
DBClusterIdentifier: response.Cluster.DBClusterIdentifier,
114+
DeletionProtection: primaryCluster.DeletionProtection,
115+
Engine: primaryCluster.Engine,
116+
EngineVersion: primaryCluster.EngineVersion,
117+
StorageType: primaryCluster.StorageType,
118+
StorageEncrypted: globalCluster?.StorageEncrypted,
119+
}
120+
} else {
121+
// Add secondary cluster to global cluster
122+
const globalCluster = node.cluster
123+
124+
input = {
125+
GlobalClusterIdentifier: globalClusterName,
126+
DBClusterIdentifier: response.Cluster.DBClusterIdentifier,
127+
DeletionProtection: globalCluster.DeletionProtection,
128+
Engine: globalCluster.Engine,
129+
EngineVersion: globalCluster.EngineVersion,
130+
StorageEncrypted: globalCluster.StorageEncrypted,
131+
}
132+
}
133+
134+
clusterName = response.Cluster.DBClusterIdentifier
135+
getLogger().info(`docdb: Creating secondary cluster: ${clusterName} in region ${regionCode}`)
136+
137+
const client = DefaultDocumentDBClient.create(regionCode)
138+
const newCluster = await client.createCluster(input)
139+
140+
if (response.Cluster.DBInstanceCount) {
141+
await createInstancesForCluster(
142+
client,
143+
clusterName,
144+
response.Cluster.DBInstanceClass,
145+
response.Cluster.DBInstanceCount
146+
)
147+
}
148+
149+
getLogger().info('docdb: Created cluster: %O', newCluster)
150+
void vscode.window.showInformationMessage(localize('AWS.docdb.addRegion.success', 'Region added'))
151+
152+
if (node instanceof DBClusterNode) {
153+
node?.parent.refresh()
154+
} else {
155+
node?.refresh()
156+
}
157+
} catch (e) {
158+
getLogger().error(`docdb: Failed to create cluster ${clusterName}: %s`, e)
159+
void showViewLogsMessage(
160+
localize('AWS.docdb.createCluster.error', 'Failed to create cluster: {0}', clusterName)
161+
)
162+
throw ToolkitError.chain(e, `Failed to create cluster ${clusterName}`)
163+
}
164+
})
165+
}

0 commit comments

Comments
 (0)