Skip to content

Commit

Permalink
fix(codedeploy): add name validation for Application, Deployment Grou…
Browse files Browse the repository at this point in the history
…p and Deployment Configuration (#19473)

- Naming rules from: https://docs.aws.amazon.com/codedeploy/latest/userguide/limits.html

----

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing guide?](../CONTRIBUTING.md)

### Adding new Unconventional Dependencies:

* [ ] This PR adds new unconventional dependencies following the process described [here](../CONTRIBUTING.md/#adding-new-unconventional-dependencies)

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
mina-asham committed Apr 2, 2022
1 parent 77a5fa1 commit 9185042
Show file tree
Hide file tree
Showing 14 changed files with 184 additions and 8 deletions.
6 changes: 5 additions & 1 deletion packages/@aws-cdk/aws-codedeploy/lib/ecs/application.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ArnFormat, IResource, Resource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnApplication } from '../codedeploy.generated';
import { arnForApplication } from '../utils';
import { arnForApplication, validateName } from '../utils';

/**
* Represents a reference to a CodeDeploy Application deploying to Amazon ECS.
Expand Down Expand Up @@ -77,4 +77,8 @@ export class EcsApplication extends Resource implements IEcsApplication {
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
});
}

protected validate(): string[] {
return validateName('Application', this.physicalName);
}
}
6 changes: 5 additions & 1 deletion packages/@aws-cdk/aws-codedeploy/lib/lambda/application.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ArnFormat, IResource, Resource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnApplication } from '../codedeploy.generated';
import { arnForApplication } from '../utils';
import { arnForApplication, validateName } from '../utils';

/**
* Represents a reference to a CodeDeploy Application deploying to AWS Lambda.
Expand Down Expand Up @@ -77,4 +77,8 @@ export class LambdaApplication extends Resource implements ILambdaApplication {
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
});
}

protected validate(): string[] {
return validateName('Application', this.physicalName);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Duration, Names, Resource } from '@aws-cdk/core';
import { AwsCustomResource, AwsCustomResourcePolicy, PhysicalResourceId } from '@aws-cdk/custom-resources';
import { Construct } from 'constructs';
import { arnForDeploymentConfig } from '../utils';
import { arnForDeploymentConfig, validateName } from '../utils';
import { ILambdaDeploymentConfig } from './deployment-config';

/**
Expand Down Expand Up @@ -143,6 +143,10 @@ export class CustomLambdaDeploymentConfig extends Resource implements ILambdaDep
});
}

protected validate(): string[] {
return validateName('Deployment config', this.deploymentConfigName);
}

// Validate the inputs. The percentage/interval limits come from CodeDeploy
private validateParameters(props: CustomLambdaDeploymentConfigProps): void {
if ( !(1 <= props.percentage && props.percentage <= 99) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as cdk from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnDeploymentGroup } from '../codedeploy.generated';
import { AutoRollbackConfig } from '../rollback-config';
import { arnForDeploymentGroup, renderAlarmConfiguration, renderAutoRollbackConfiguration } from '../utils';
import { arnForDeploymentGroup, renderAlarmConfiguration, renderAutoRollbackConfiguration, validateName } from '../utils';
import { ILambdaApplication, LambdaApplication } from './application';
import { ILambdaDeploymentConfig, LambdaDeploymentConfig } from './deployment-config';

Expand Down Expand Up @@ -254,6 +254,10 @@ export class LambdaDeploymentGroup extends cdk.Resource implements ILambdaDeploy
actions: ['codedeploy:PutLifecycleEventHookExecutionStatus'],
});
}

protected validate(): string[] {
return validateName('Deployment group', this.physicalName);
}
}

/**
Expand Down
6 changes: 5 additions & 1 deletion packages/@aws-cdk/aws-codedeploy/lib/server/application.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ArnFormat, IResource, Resource } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnApplication } from '../codedeploy.generated';
import { arnForApplication } from '../utils';
import { arnForApplication, validateName } from '../utils';

/**
* Represents a reference to a CodeDeploy Application deploying to EC2/on-premise instances.
Expand Down Expand Up @@ -78,4 +78,8 @@ export class ServerApplication extends Resource implements IServerApplication {
arnFormat: ArnFormat.COLON_RESOURCE_NAME,
});
}

protected validate(): string[] {
return validateName('Application', this.physicalName);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as cdk from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnDeploymentConfig } from '../codedeploy.generated';
import { arnForDeploymentConfig } from '../utils';
import { arnForDeploymentConfig, validateName } from '../utils';

/**
* The Deployment Configuration of an EC2/on-premise Deployment Group.
Expand Down Expand Up @@ -119,6 +119,10 @@ export class ServerDeploymentConfig extends cdk.Resource implements IServerDeplo
this.deploymentConfigName = resource.ref;
this.deploymentConfigArn = arnForDeploymentConfig(this.deploymentConfigName);
}

protected validate(): string[] {
return validateName('Deployment config', this.physicalName);
}
}

function deploymentConfig(name: string): IServerDeploymentConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ArnFormat } from '@aws-cdk/core';
import { Construct } from 'constructs';
import { CfnDeploymentGroup } from '../codedeploy.generated';
import { AutoRollbackConfig } from '../rollback-config';
import { arnForDeploymentGroup, renderAlarmConfiguration, renderAutoRollbackConfiguration } from '../utils';
import { arnForDeploymentGroup, renderAlarmConfiguration, renderAutoRollbackConfiguration, validateName } from '../utils';
import { IServerApplication, ServerApplication } from './application';
import { IServerDeploymentConfig, ServerDeploymentConfig } from './deployment-config';
import { LoadBalancer, LoadBalancerGeneration } from './load-balancer';
Expand Down Expand Up @@ -341,6 +341,10 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupBase {
return this._autoScalingGroups.slice();
}

protected validate(): string[] {
return validateName('Deployment group', this.physicalName);
}

private addCodeDeployAgentInstallUserData(asg: autoscaling.IAutoScalingGroup): void {
if (!this.installAgent) {
return;
Expand Down
17 changes: 16 additions & 1 deletion packages/@aws-cdk/aws-codedeploy/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as cloudwatch from '@aws-cdk/aws-cloudwatch';
import { Aws } from '@aws-cdk/core';
import { Aws, Token } from '@aws-cdk/core';
import { CfnDeploymentGroup } from './codedeploy.generated';
import { AutoRollbackConfig } from './rollback-config';

Expand Down Expand Up @@ -65,3 +65,18 @@ CfnDeploymentGroup.AutoRollbackConfigurationProperty | undefined {
}
: undefined;
}

export function validateName(type: 'Application' | 'Deployment group' | 'Deployment config', name: string): string[] {
const ret = [];

if (!Token.isUnresolved(name) && name !== undefined) {
if (name.length > 100) {
ret.push(`${type} name: "${name}" can be a max of 100 characters.`);
}
if (!/^[a-z0-9._+=,@-]+$/i.test(name)) {
ret.push(`${type} name: "${name}" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).`);
}
}

return ret;
}
20 changes: 20 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/test/ecs/application.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,24 @@ describe('CodeDeploy ECS Application', () => {
ComputePlatform: 'ECS',
});
});

test('fail with more than 100 characters in name', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app);
new codedeploy.EcsApplication(stack, 'MyApp', {
applicationName: 'a'.repeat(101),
});

expect(() => app.synth()).toThrow(`Application name: "${'a'.repeat(101)}" can be a max of 100 characters.`);
});

test('fail with unallowed characters in name', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app);
new codedeploy.EcsApplication(stack, 'MyApp', {
applicationName: 'my name',
});

expect(() => app.synth()).toThrow('Application name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).');
});
});
20 changes: 20 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/test/lambda/application.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,24 @@ describe('CodeDeploy Lambda Application', () => {
ComputePlatform: 'Lambda',
});
});

test('fail with more than 100 characters in name', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app);
new codedeploy.LambdaApplication(stack, 'MyApp', {
applicationName: 'a'.repeat(101),
});

expect(() => app.synth()).toThrow(`Application name: "${'a'.repeat(101)}" can be a max of 100 characters.`);
});

test('fail with unallowed characters in name', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app);
new codedeploy.LambdaApplication(stack, 'MyApp', {
applicationName: 'my name',
});

expect(() => app.synth()).toThrow('Application name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,32 @@ test('custom resource created with specific name', () => {
});
});

test('fail with more than 100 characters in name', () => {
const app = new cdk.App();
const stackWithApp = new cdk.Stack(app);
new codedeploy.CustomLambdaDeploymentConfig(stackWithApp, 'CustomConfig', {
type: codedeploy.CustomLambdaDeploymentConfigType.CANARY,
interval: cdk.Duration.minutes(1),
percentage: 5,
deploymentConfigName: 'a'.repeat(101),
});

expect(() => app.synth()).toThrow(`Deployment config name: "${'a'.repeat(101)}" can be a max of 100 characters.`);
});

test('fail with unallowed characters in name', () => {
const app = new cdk.App();
const stackWithApp = new cdk.Stack(app);
new codedeploy.CustomLambdaDeploymentConfig(stackWithApp, 'CustomConfig', {
type: codedeploy.CustomLambdaDeploymentConfigType.CANARY,
interval: cdk.Duration.minutes(1),
percentage: 5,
deploymentConfigName: 'my name',
});

expect(() => app.synth()).toThrow('Deployment config name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).');
});

test('can create linear custom config', () => {
// WHEN
const config = new codedeploy.CustomLambdaDeploymentConfig(stack, 'CustomConfig', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,30 @@ describe('CodeDeploy Lambda DeploymentGroup', () => {
});
});

test('fail with more than 100 characters in name', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app);
const alias = mockAlias(stack);
new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', {
alias,
deploymentGroupName: 'a'.repeat(101),
});

expect(() => app.synth()).toThrow(`Deployment group name: "${'a'.repeat(101)}" can be a max of 100 characters.`);
});

test('fail with unallowed characters in name', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app);
const alias = mockAlias(stack);
new codedeploy.LambdaDeploymentGroup(stack, 'MyDG', {
alias,
deploymentGroupName: 'my name',
});

expect(() => app.synth()).toThrow('Deployment group name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).');
});

test('can be created with explicit role', () => {
const stack = new cdk.Stack();
const application = new codedeploy.LambdaApplication(stack, 'MyApp');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,26 @@ describe('CodeDeploy DeploymentConfig', () => {

expect(deploymentConfig).not.toEqual(undefined);
});

test('fail with more than 100 characters in name', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app);
new codedeploy.ServerDeploymentConfig(stack, 'DeploymentConfig', {
minimumHealthyHosts: codedeploy.MinimumHealthyHosts.percentage(75),
deploymentConfigName: 'a'.repeat(101),
});

expect(() => app.synth()).toThrow(`Deployment config name: "${'a'.repeat(101)}" can be a max of 100 characters.`);
});

test('fail with unallowed characters in name', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app);
new codedeploy.ServerDeploymentConfig(stack, 'DeploymentConfig', {
minimumHealthyHosts: codedeploy.MinimumHealthyHosts.percentage(75),
deploymentConfigName: 'my name',
});

expect(() => app.synth()).toThrow('Deployment config name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -437,4 +437,25 @@ describe('CodeDeploy Server Deployment Group', () => {
});
});

test('fail with more than 100 characters in name', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app);
new codedeploy.ServerDeploymentGroup(stack, 'MyDG', {
deploymentGroupName: 'a'.repeat(101),
});

expect(() => app.synth()).toThrow(`Deployment group name: "${'a'.repeat(101)}" can be a max of 100 characters.`);
});

test('fail with unallowed characters in name', () => {
const app = new cdk.App();
const stack = new cdk.Stack(app);
new codedeploy.ServerDeploymentGroup(stack, 'MyDG', {

deploymentGroupName: 'my name',
});

expect(() => app.synth()).toThrow('Deployment group name: "my name" can only contain letters (a-z, A-Z), numbers (0-9), periods (.), underscores (_), + (plus signs), = (equals signs), , (commas), @ (at signs), - (minus signs).');
});

});

0 comments on commit 9185042

Please sign in to comment.