Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deployment Configuration Construct for CodeDeploy L2 #653

Merged
merged 1 commit into from
Sep 20, 2018
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
35 changes: 33 additions & 2 deletions packages/@aws-cdk/aws-codedeploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ To import an already existing Application:

```ts
const application = codedeploy.ServerApplicationRef.import(this, 'ExistingCodeDeployApplication', {
applicationName: new codedeploy.ApplicationName('MyExistingApplication'),
applicationName: 'MyExistingApplication',
});
```

Expand All @@ -39,7 +39,38 @@ To import an already existing Deployment Group:
```ts
const deploymentGroup = codedeploy.ServerDeploymentGroupRef.import(this, 'ExistingCodeDeployDeploymentGroup', {
application,
deploymentGroupName: new codedeploy.DeploymentGroupName('MyExistingDeploymentGroup'),
deploymentGroupName: 'MyExistingDeploymentGroup',
});
```

### Deployment Configurations

You can also pass a Deployment Configuration when creating the Deployment Group:

```ts
const deploymentGroup = new codedeploy.ServerDeploymentGroup(this, 'CodeDeployDeploymentGroup', {
deploymentConfig: codedeploy.ServerDeploymentConfig.AllAtOnce,
});
```

The default Deployment Configuration is `ServerDeploymentConfig.OneAtATime`.

You can also create a custom Deployment Configuration:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe in a subsequent commit, but can we provide a sugar that allows me to do this:

deploymentConfig: ServerDeploymentConfig.custom({ minHealthyCount: 5 })

It will be the natural thing for people to look for when they are “browsing” the options.

The challenge would be how to propagate the parent construct. I think that the construct id can be just DeploymentConfig if you attach this to the DeploymentGroup because a deployment group can only have one deployment config.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I don't see an easy way of allowing both that form, and deploymentConfig: new ServerDeploymentConfig(this, 'id', { minHealthyHostCount: 5 })...


```ts
const deploymentConfig = new codedeploy.ServerDeploymentConfig(this, 'DeploymentConfiguration', {
deploymentConfigName: 'MyDeploymentConfiguration', // optional property
// one of these is required, but both cannot be specified at the same time
minHealthyHostCount: 2,
minHealthyHostPercentage: 75,
});
```

Or import an existing one:

```ts
const deploymentConfig = codedeploy.ServerDeploymentConfigRef.import(this, 'ExistingDeploymentConfiguration', {
deploymentConfigName: 'MyExistingDeploymentConfiguration',
});
```

Expand Down
158 changes: 158 additions & 0 deletions packages/@aws-cdk/aws-codedeploy/lib/deployment-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import cdk = require('@aws-cdk/cdk');
import { cloudformation } from './codedeploy.generated';

/**
* The Deployment Configuration of an EC2/on-premise Deployment Group.
* The default, pre-defined Configurations are available as constants on the {@link ServerDeploymentConfig} class
* (`ServerDeploymentConfig.HalfAtATime`, `ServerDeploymentConfig.AllAtOnce`, etc.).
* To create a custom Deployment Configuration,
* instantiate the {@link ServerDeploymentConfig} Construct.
*/
export interface IServerDeploymentConfig {
readonly deploymentConfigName: string;
readonly deploymentConfigArn: string;
}

/**
* Properties of a reference to a CodeDeploy EC2/on-premise Deployment Configuration.
*
* @see ServerDeploymentConfigRef#import
* @see ServerDeploymentConfigRef#export
*/
export interface ServerDeploymentConfigRefProps {
/**
* The physical, human-readable name of the custom CodeDeploy EC2/on-premise Deployment Configuration
* that we are referencing.
*/
deploymentConfigName: string;
}

/**
* Reference to a custom Deployment Configuration for an EC2/on-premise Deployment Group.
*/
export abstract class ServerDeploymentConfigRef extends cdk.Construct implements IServerDeploymentConfig {
/**
* Import a custom Deployment Configuration for an EC2/on-premise Deployment Group defined either outside the CDK,
* or in a different CDK Stack and exported using the {@link #export} method.
*
* @param parent the parent Construct for this new Construct
* @param id the logical ID of this new Construct
* @param props the properties of the referenced custom Deployment Configuration
* @returns a Construct representing a reference to an existing custom Deployment Configuration
*/
public static import(parent: cdk.Construct, id: string, props: ServerDeploymentConfigRefProps):
ServerDeploymentConfigRef {
return new ImportedServerDeploymentConfigRef(parent, id, props);
}

public abstract readonly deploymentConfigName: string;
public abstract readonly deploymentConfigArn: string;

public export(): ServerDeploymentConfigRefProps {
return {
deploymentConfigName: new cdk.Output(this, 'DeploymentConfigName', {
value: this.deploymentConfigName,
}).makeImportValue().toString(),
};
}
}

class ImportedServerDeploymentConfigRef extends ServerDeploymentConfigRef {
public readonly deploymentConfigName: string;
public readonly deploymentConfigArn: string;

constructor(parent: cdk.Construct, id: string, props: ServerDeploymentConfigRefProps) {
super(parent, id);

this.deploymentConfigName = props.deploymentConfigName;
this.deploymentConfigArn = arnForDeploymentConfigName(this.deploymentConfigName);
}
}

class DefaultServerDeploymentConfig implements IServerDeploymentConfig {
public readonly deploymentConfigName: string;
public readonly deploymentConfigArn: string;

constructor(deploymentConfigName: string) {
this.deploymentConfigName = deploymentConfigName;
this.deploymentConfigArn = arnForDeploymentConfigName(this.deploymentConfigName);
}
}

/**
* Construction properties of {@link ServerDeploymentConfig}.
*/
export interface ServerDeploymentConfigProps {
/**
* The physical, human-readable name of the Deployment Configuration.
*
* @default a name will be auto-generated
*/
deploymentConfigName?: string;

/**
* The minimum healhty hosts threshold expressed as an absolute number.
* If you've specified this value,
* you can't specify {@link #minHealthyHostPercentage},
* however one of this or {@link #minHealthyHostPercentage} is required.
*/
minHealthyHostCount?: number;

/**
* The minmum healhty hosts threshold expressed as a percentage of the fleet.
* If you've specified this value,
* you can't specify {@link #minHealthyHostCount},
* however one of this or {@link #minHealthyHostCount} is required.
*/
minHealthyHostPercentage?: number;
}

/**
* A custom Deployment Configuration for an EC2/on-premise Deployment Group.
*/
export class ServerDeploymentConfig extends ServerDeploymentConfigRef {
public static readonly OneAtATime: IServerDeploymentConfig = new DefaultServerDeploymentConfig('CodeDeployDefault.OneAtATime');
public static readonly HalfAtATime: IServerDeploymentConfig = new DefaultServerDeploymentConfig('CodeDeployDefault.HalfAtATime');
public static readonly AllAtOnce: IServerDeploymentConfig = new DefaultServerDeploymentConfig('CodeDeployDefault.AllAtOnce');

public readonly deploymentConfigName: string;
public readonly deploymentConfigArn: string;

constructor(parent: cdk.Construct, id: string, props: ServerDeploymentConfigProps) {
super(parent, id);

const resource = new cloudformation.DeploymentConfigResource(this, 'Resource', {
deploymentConfigName: props.deploymentConfigName,
minimumHealthyHosts: this.minimumHealthyHosts(props),
});

this.deploymentConfigName = resource.ref.toString();
this.deploymentConfigArn = arnForDeploymentConfigName(this.deploymentConfigName);
}

private minimumHealthyHosts(props: ServerDeploymentConfigProps):
cloudformation.DeploymentConfigResource.MinimumHealthyHostsProperty {
if (props.minHealthyHostCount === undefined && props.minHealthyHostPercentage === undefined) {
throw new Error('At least one of minHealthyHostCount or minHealthyHostPercentage must be specified when creating ' +
'a custom Server DeploymentConfig');
}
if (props.minHealthyHostCount !== undefined && props.minHealthyHostPercentage !== undefined) {
throw new Error('Both minHealthyHostCount and minHealthyHostPercentage cannot be specified when creating ' +
'a custom Server DeploymentConfig');
}

return {
type: props.minHealthyHostCount !== undefined ? 'HOST_COUNT' : 'FLEET_PERCENT',
value: props.minHealthyHostCount !== undefined ? props.minHealthyHostCount : props.minHealthyHostPercentage!,
};
}
}

function arnForDeploymentConfigName(name: string): string {
return cdk.ArnUtils.fromComponents({
service: 'codedeploy',
resource: 'deploymentconfig',
resourceName: name,
sep: ':',
});
}
41 changes: 33 additions & 8 deletions packages/@aws-cdk/aws-codedeploy/lib/deployment-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import cdk = require("@aws-cdk/cdk");
import iam = require("../../aws-iam/lib/role");
import { ServerApplication, ServerApplicationRef } from "./application";
import { cloudformation } from './codedeploy.generated';
import { IServerDeploymentConfig, ServerDeploymentConfig } from "./deployment-config";

/**
* Properties of a reference to a CodeDeploy EC2/on-premise Deployment Group.
Expand All @@ -21,6 +22,13 @@ export interface ServerDeploymentGroupRefProps {
* that we are referencing.
*/
deploymentGroupName: string;

/**
* The Deployment Configuration this Deployment Group uses.
*
* @default ServerDeploymentConfig#OneAtATime
*/
deploymentConfig?: IServerDeploymentConfig;
}

/**
Expand Down Expand Up @@ -50,11 +58,19 @@ export abstract class ServerDeploymentGroupRef extends cdk.Construct {
public abstract readonly application: ServerApplicationRef;
public abstract readonly deploymentGroupName: string;
public abstract readonly deploymentGroupArn: string;
public readonly deploymentConfig: IServerDeploymentConfig;

constructor(parent: cdk.Construct, id: string, deploymentConfig?: IServerDeploymentConfig) {
super(parent, id);
this.deploymentConfig = deploymentConfig || ServerDeploymentConfig.OneAtATime;
}

public export(): ServerDeploymentGroupRefProps {
return {
application: this.application,
deploymentGroupName: new cdk.Output(this, 'DeploymentGroupName', { value: this.deploymentGroupName }).makeImportValue().toString()
deploymentGroupName: new cdk.Output(this, 'DeploymentGroupName', {
value: this.deploymentGroupName
}).makeImportValue().toString(),
};
}
}
Expand All @@ -65,7 +81,7 @@ class ImportedServerDeploymentGroupRef extends ServerDeploymentGroupRef {
public readonly deploymentGroupArn: string;

constructor(parent: cdk.Construct, id: string, props: ServerDeploymentGroupRefProps) {
super(parent, id);
super(parent, id, props.deploymentConfig);

this.application = props.application;
this.deploymentGroupName = props.deploymentGroupName;
Expand Down Expand Up @@ -96,6 +112,13 @@ export interface ServerDeploymentGroupProps {
* @default an auto-generated name will be used
*/
deploymentGroupName?: string;

/**
* The EC2/on-premise Deployment Configuration to use for this Deployment Group.
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is an interface. Provide some pointers/example on how to use a predefined one and how to define a custom one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Will fix.

* @default ServerDeploymentConfig#OneAtATime
*/
deploymentConfig?: IServerDeploymentConfig;
}

/**
Expand All @@ -107,23 +130,25 @@ export class ServerDeploymentGroup extends ServerDeploymentGroupRef {
public readonly deploymentGroupArn: string;
public readonly deploymentGroupName: string;

constructor(parent: cdk.Construct, id: string, props?: ServerDeploymentGroupProps) {
super(parent, id);
constructor(parent: cdk.Construct, id: string, props: ServerDeploymentGroupProps = {}) {
super(parent, id, props && props.deploymentConfig);

this.application = (props && props.application) || new ServerApplication(this, 'Application');
this.application = props.application || new ServerApplication(this, 'Application');

this.role = (props && props.role) || new iam.Role(this, 'Role', {
this.role = props.role || new iam.Role(this, 'Role', {
assumedBy: new cdk.ServicePrincipal('codedeploy.amazonaws.com'),
managedPolicyArns: ['arn:aws:iam::aws:policy/service-role/AWSCodeDeployRole'],
});

const resource = new cloudformation.DeploymentGroupResource(this, 'Resource', {
applicationName: this.application.applicationName,
deploymentGroupName: props && props.deploymentGroupName,
deploymentGroupName: props.deploymentGroupName,
serviceRoleArn: this.role.roleArn,
deploymentConfigName: props.deploymentConfig &&
props.deploymentConfig.deploymentConfigName,
});

this.deploymentGroupName = resource.ref;
this.deploymentGroupName = resource.deploymentGroupName;
this.deploymentGroupArn = deploymentGroupName2Arn(this.application.applicationName,
this.deploymentGroupName);
}
Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-codedeploy/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './application';
export * from './deployment-config';
export * from './deployment-group';
export * from './pipeline-action';

Expand Down
8 changes: 0 additions & 8 deletions packages/@aws-cdk/aws-codedeploy/test/test.codedeploy.ts

This file was deleted.

Loading