Skip to content

Commit

Permalink
Merge branch 'master' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] committed Aug 27, 2020
2 parents b4adae7 + 07acac2 commit cc91082
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 135 deletions.
169 changes: 100 additions & 69 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"tools/*"
],
"rejectCycles": "true",
"version": "1.60.0"
"version": "1.61.0"
}
28 changes: 20 additions & 8 deletions packages/@aws-cdk/aws-redshift/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,11 @@ export interface ClusterProps {
readonly parameterGroup?: IClusterParameterGroup;

/**
* Number of compute nodes in the cluster
* Number of compute nodes in the cluster. Only specify this property for multi-node clusters.
*
* Value must be at least 1 and no more than 100.
* Value must be at least 2 and no more than 100.
*
* @default 1
* @default - 2 if `clusterType` is ClusterType.MULTI_NODE, undefined otherwise
*/
readonly numberOfNodes?: number;

Expand Down Expand Up @@ -425,11 +425,7 @@ export class Cluster extends ClusterBase {
}

const clusterType = props.clusterType || ClusterType.MULTI_NODE;
const nodeCount = props.numberOfNodes !== undefined ? props.numberOfNodes : (clusterType === ClusterType.MULTI_NODE ? 2 : 1);

if (clusterType === ClusterType.MULTI_NODE && nodeCount < 2) {
throw new Error('Number of nodes for cluster type multi-node must be at least 2');
}
const nodeCount = this.validateNodeCount(clusterType, props.numberOfNodes);

if (props.encrypted === false && props.encryptionKey !== undefined) {
throw new Error('Cannot set property encryptionKey without enabling encryption!');
Expand Down Expand Up @@ -537,4 +533,20 @@ export class Cluster extends ClusterBase {
target: this,
});
}

private validateNodeCount(clusterType: ClusterType, numberOfNodes?: number): number | undefined {
if (clusterType === ClusterType.SINGLE_NODE) {
// This property must not be set for single-node clusters; be generous and treat a value of 1 node as undefined.
if (numberOfNodes !== undefined && numberOfNodes !== 1) {
throw new Error('Number of nodes must be not be supplied or be 1 for cluster type single-node');
}
return undefined;
} else {
const nodeCount = numberOfNodes ?? 2;
if (nodeCount < 2 || nodeCount > 100) {
throw new Error('Number of nodes for cluster type multi-node must be at least 2 and no more than 100');
}
return nodeCount;
}
}
}
144 changes: 90 additions & 54 deletions packages/@aws-cdk/aws-redshift/test/cluster.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { expect as cdkExpect, haveResource, ResourcePart } from '@aws-cdk/assert';
import { ABSENT, expect as cdkExpect, haveResource, ResourcePart } from '@aws-cdk/assert';
import '@aws-cdk/assert/jest';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as kms from '@aws-cdk/aws-kms';
import * as s3 from '@aws-cdk/aws-s3';
import * as cdk from '@aws-cdk/core';
import { Cluster, ClusterParameterGroup, ClusterType } from '../lib';

import { Cluster, ClusterParameterGroup, ClusterType, NodeType } from '../lib';
let stack: cdk.Stack;
let vpc: ec2.IVpc;

test('check that instantiation works', () => {
// GIVEN
const stack = testStack();
const vpc = new ec2.Vpc(stack, 'VPC');
beforeEach(() => {
stack = testStack();
vpc = new ec2.Vpc(stack, 'VPC');
});

test('check that instantiation works', () => {
// WHEN
new Cluster(stack, 'Redshift', {
masterUser: {
Expand Down Expand Up @@ -57,8 +60,7 @@ test('check that instantiation works', () => {

test('can create a cluster with imported vpc and security group', () => {
// GIVEN
const stack = testStack();
const vpc = ec2.Vpc.fromLookup(stack, 'VPC', {
vpc = ec2.Vpc.fromLookup(stack, 'ImportedVPC', {
vpcId: 'VPC12345',
});
const sg = ec2.SecurityGroup.fromSecurityGroupId(stack, 'SG', 'SecurityGroupId12345');
Expand All @@ -83,10 +85,6 @@ test('can create a cluster with imported vpc and security group', () => {
});

test('creates a secret when master credentials are not specified', () => {
// GIVEN
const stack = testStack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
new Cluster(stack, 'Redshift', {
masterUser: {
Expand Down Expand Up @@ -133,34 +131,88 @@ test('creates a secret when master credentials are not specified', () => {
}));
});

test('SIngle Node CLusters spawn only single node', () => {
// GIVEN
const stack = testStack();
const vpc = new ec2.Vpc(stack, 'VPC');
describe('node count', () => {

test('Single Node Clusters do not define node count', () => {
// WHEN
new Cluster(stack, 'Redshift', {
masterUser: {
masterUsername: 'admin',
},
vpc,
clusterType: ClusterType.SINGLE_NODE,
});

// THEN
cdkExpect(stack).to(haveResource('AWS::Redshift::Cluster', {
ClusterType: 'single-node',
NumberOfNodes: ABSENT,
}));
});

// WHEN
new Cluster(stack, 'Redshift', {
masterUser: {
masterUsername: 'admin',
},
vpc,
nodeType: NodeType.DC1_8XLARGE,
clusterType: ClusterType.SINGLE_NODE,
test('Single Node Clusters treat 1 node as undefined', () => {
// WHEN
new Cluster(stack, 'Redshift', {
masterUser: {
masterUsername: 'admin',
},
vpc,
clusterType: ClusterType.SINGLE_NODE,
numberOfNodes: 1,
});

// THEN
cdkExpect(stack).to(haveResource('AWS::Redshift::Cluster', {
ClusterType: 'single-node',
NumberOfNodes: ABSENT,
}));
});

// THEN
cdkExpect(stack).to(haveResource('AWS::Redshift::Cluster', {
ClusterType: 'single-node',
NodeType: 'dc1.8xlarge',
NumberOfNodes: 1,
}));
test('Single Node Clusters throw if any other node count is specified', () => {
expect(() => {
new Cluster(stack, 'Redshift', {
masterUser: {
masterUsername: 'admin',
},
vpc,
clusterType: ClusterType.SINGLE_NODE,
numberOfNodes: 2,
});
}).toThrow(/Number of nodes must be not be supplied or be 1 for cluster type single-node/);
});

test('Multi-Node Clusters default to 2 nodes', () => {
// WHEN
new Cluster(stack, 'Redshift', {
masterUser: {
masterUsername: 'admin',
},
vpc,
clusterType: ClusterType.MULTI_NODE,
});

// THEN
cdkExpect(stack).to(haveResource('AWS::Redshift::Cluster', {
ClusterType: 'multi-node',
NumberOfNodes: 2,
}));
});

test.each([0, 1, -1, 101])('Multi-Node Clusters throw with %s nodes', (numberOfNodes: number) => {
expect(() => {
new Cluster(stack, 'Redshift', {
masterUser: {
masterUsername: 'admin',
},
vpc,
clusterType: ClusterType.MULTI_NODE,
numberOfNodes,
});
}).toThrow(/Number of nodes for cluster type multi-node must be at least 2 and no more than 100/);
});
});

test('create an encrypted cluster with custom KMS key', () => {
// GIVEN
const stack = testStack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
new Cluster(stack, 'Redshift', {
masterUser: {
Expand All @@ -182,10 +234,6 @@ test('create an encrypted cluster with custom KMS key', () => {
});

test('cluster with parameter group', () => {
// GIVEN
const stack = testStack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
const group = new ClusterParameterGroup(stack, 'Params', {
description: 'bye',
Expand All @@ -211,8 +259,6 @@ test('cluster with parameter group', () => {

test('imported cluster with imported security group honors allowAllOutbound', () => {
// GIVEN
const stack = testStack();

const cluster = Cluster.fromClusterAttributes(stack, 'Database', {
clusterEndpointAddress: 'addr',
clusterName: 'identifier',
Expand All @@ -235,8 +281,6 @@ test('imported cluster with imported security group honors allowAllOutbound', ()

test('can create a cluster with logging enabled', () => {
// GIVEN
const stack = testStack();
const vpc = new ec2.Vpc(stack, 'VPC');
const bucket = s3.Bucket.fromBucketName(stack, 'bucket', 'logging-bucket');

// WHEN
Expand All @@ -259,10 +303,6 @@ test('can create a cluster with logging enabled', () => {
});

test('throws when trying to add rotation to a cluster without secret', () => {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
const cluster = new Cluster(stack, 'Redshift', {
masterUser: {
Expand All @@ -281,8 +321,6 @@ test('throws when trying to add rotation to a cluster without secret', () => {

test('throws validation error when trying to set encryptionKey without enabling encryption', () => {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC');
const key = new kms.Key(stack, 'kms-key');

// WHEN
Expand All @@ -304,8 +342,6 @@ test('throws validation error when trying to set encryptionKey without enabling

test('throws when trying to add single user rotation multiple times', () => {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC');
const cluster = new Cluster(stack, 'Redshift', {
masterUser: {
masterUsername: 'admin',
Expand All @@ -323,7 +359,7 @@ test('throws when trying to add single user rotation multiple times', () => {
});

function testStack() {
const stack = new cdk.Stack(undefined, undefined, { env: { account: '12345', region: 'us-test-1' } });
stack.node.setContext('availability-zones:12345:us-test-1', ['us-test-1a', 'us-test-1b']);
return stack;
}
const newTestStack = new cdk.Stack(undefined, undefined, { env: { account: '12345', region: 'us-test-1' } });
newTestStack.node.setContext('availability-zones:12345:us-test-1', ['us-test-1a', 'us-test-1b']);
return newTestStack;
}
27 changes: 27 additions & 0 deletions packages/aws-cdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,33 @@ Example `outputs.json` after deployment of multiple stacks
}
```

##### Deployment Progress

By default, stack deployment events are displayed as a progress bar with the events for the resource
currently being deployed.

Set the `--progress` flag to request the complete history which includes all CloudFormation events
```console
$ cdk deploy --progress events
```

Alternatively, the `progress` key can be specified in the project config (`cdk.json`).

The following shows a sample `cdk.json` where the `progress` key is set to *events*.
When `cdk deploy` is executed, deployment events will include the complete history.
```
{
"app": "npx ts-node bin/myproject.ts",
"context": {
"@aws-cdk/core:enableStackNameDuplicates": "true",
"aws-cdk:enableDiffNoFail": "true",
"@aws-cdk/core:stackRelativeExports": "true"
},
"progress": "events"
}
```
The `progress` key can also be specified as a user setting (`~/.cdk.json`)

#### `cdk destroy`
Deletes a stack from it's environment. This will cause the resources in the stack to be destroyed (unless they were
configured with a `DeletionPolicy` of `Retain`). During the stack destruction, the command will output progress
Expand Down
5 changes: 4 additions & 1 deletion packages/aws-cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { SdkProvider } from '../lib/api/aws-auth';
import { CloudFormationDeployments } from '../lib/api/cloudformation-deployments';
import { CloudExecutable } from '../lib/api/cxapp/cloud-executable';
import { execProgram } from '../lib/api/cxapp/exec';
import { StackActivityProgress } from '../lib/api/util/cloudformation/stack-activity-monitor';
import { CdkToolkit } from '../lib/cdk-toolkit';
import { RequireApproval } from '../lib/diff';
import { availableInitLanguages, cliInit, printAvailableTemplates } from '../lib/init';
Expand Down Expand Up @@ -89,7 +90,8 @@ async function parseCommandLineArguments() {
.option('force', { alias: 'f', type: 'boolean', desc: 'Always deploy stack even if templates are identical', default: false })
.option('parameters', { type: 'array', desc: 'Additional parameters passed to CloudFormation at deploy time (STACK:KEY=VALUE)', nargs: 1, requiresArg: true, default: {} })
.option('outputs-file', { type: 'string', alias: 'O', desc: 'Path to file where stack outputs will be written as JSON', requiresArg: true })
.option('previous-parameters', { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' }),
.option('previous-parameters', { type: 'boolean', default: true, desc: 'Use previous values for existing parameters (you must specify all parameters on every deployment if this is disabled)' })
.option('progress', { type: 'string', choices: [StackActivityProgress.BAR, StackActivityProgress.EVENTS], desc: 'Display mode for stack activity events.' }),
)
.command('destroy [STACKS..]', 'Destroy the stack(s) named STACKS', yargs => yargs
.option('exclusively', { type: 'boolean', alias: 'e', desc: 'Only destroy requested stacks, don\'t include dependees' })
Expand Down Expand Up @@ -279,6 +281,7 @@ async function initCommandLine() {
parameters: parameterMap,
usePreviousParameters: args['previous-parameters'],
outputsFile: args.outputsFile,
progress: configuration.settings.get(['progress']),
ci: args.ci,
});

Expand Down
10 changes: 10 additions & 0 deletions packages/aws-cdk/lib/api/cloudformation-deployments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Mode, SdkProvider } from './aws-auth';
import { deployStack, DeployStackResult, destroyStack } from './deploy-stack';
import { ToolkitInfo } from './toolkit-info';
import { CloudFormationStack, Template } from './util/cloudformation';
import { StackActivityProgress } from './util/cloudformation/stack-activity-monitor';

export interface DeployStackOptions {
/**
Expand Down Expand Up @@ -89,6 +90,14 @@ export interface DeployStackOptions {
*/
usePreviousParameters?: boolean;

/**
* Display mode for stack deployment progress.
*
* @default - StackActivityProgress.Bar - stack events will be displayed for
* the resource currently being deployed.
*/
progress?: StackActivityProgress;

/**
* Whether we are on a CI system
*
Expand Down Expand Up @@ -163,6 +172,7 @@ export class CloudFormationDeployments {
force: options.force,
parameters: options.parameters,
usePreviousParameters: options.usePreviousParameters,
progress: options.progress,
ci: options.ci,
});
}
Expand Down
Loading

0 comments on commit cc91082

Please sign in to comment.