|
| 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