Skip to content

Commit

Permalink
fix(codepipeline-actions): use IBaseService instead of BaseService in…
Browse files Browse the repository at this point in the history
… EcsDeployActionProps (#6412)

* use IService instead of BaseService

* fromEc2ServiceArn -> fromEc2ServiceAttributes, fromFargateServiceArn -> fromFargateServiceAttributes

* fix ci error

* fix build error

* avoid breaking changes

* add IBaseService to implements of BaseService

* change to ICluster, add serviceName

* fix build error

* fix build error

* create ImportedBaseService, add some tests

* remove unused module, rename test

* fix build error

* create fromServiceAtrributes, remove ImportedBaseService. Some tests

* update comment, tests

* update comments, tests

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
atsushi-ishibashi and mergify[bot] authored Mar 11, 2020
1 parent 9858cdb commit bed5357
Show file tree
Hide file tree
Showing 8 changed files with 354 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export interface EcsDeployActionProps extends codepipeline.CommonAwsActionProps
/**
* The ECS Service to deploy.
*/
readonly service: ecs.BaseService;
readonly service: ecs.IBaseService;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { expect, haveResourceLike } from '@aws-cdk/assert';
import * as codepipeline from '@aws-cdk/aws-codepipeline';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as ecs from '@aws-cdk/aws-ecs';
import * as s3 from '@aws-cdk/aws-s3';
import * as cdk from '@aws-cdk/core';
import { Test } from 'nodeunit';
import * as cpactions from '../../lib';
Expand Down Expand Up @@ -80,6 +82,71 @@ export = {

test.done();
},

'can be created by existing service'(test: Test) {
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'Vpc');
const service = ecs.FargateService.fromFargateServiceAttributes(stack, 'FargateService', {
serviceName: 'service-name',
cluster: ecs.Cluster.fromClusterAttributes(stack, 'Cluster', {
vpc,
securityGroups: [],
clusterName: 'cluster-name',
}),
});
const artifact = new codepipeline.Artifact('Artifact');
const bucket = new s3.Bucket(stack, 'PipelineBucket', {
versioned: true,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
const source = new cpactions.S3SourceAction({
actionName: 'Source',
output: artifact,
bucket,
bucketKey: 'key',
});
const action = new cpactions.EcsDeployAction({
actionName: 'ECS',
service,
imageFile: artifact.atPath('imageFile.json'),
});
new codepipeline.Pipeline(stack, 'Pipeline', {
stages: [
{
stageName: 'Source',
actions: [source],
},
{
stageName: 'Deploy',
actions: [action],
}
],
});

expect(stack).to(haveResourceLike('AWS::CodePipeline::Pipeline', {
Stages: [
{},
{
Actions: [
{
Name: 'ECS',
ActionTypeId: {
Category: "Deploy",
Provider: "ECS"
},
Configuration: {
ClusterName: "cluster-name",
ServiceName: "service-name",
FileName: "imageFile.json"
}
}
]
}
]
}));

test.done();
},
},
};

Expand Down
19 changes: 18 additions & 1 deletion packages/@aws-cdk/aws-ecs/lib/base/base-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ export interface IService extends IResource {
* @attribute
*/
readonly serviceArn: string;

/**
* The name of the service.
*
* @attribute
*/
readonly serviceName: string;
}

/**
Expand Down Expand Up @@ -245,11 +252,21 @@ class NetworkListenerConfig extends ListenerConfig {
}
}

/**
* The interface for BaseService.
*/
export interface IBaseService extends IService {
/**
* The cluster that hosts the service.
*/
readonly cluster: ICluster;
}

/**
* The base class for Ec2Service and FargateService services.
*/
export abstract class BaseService extends Resource
implements IService, elbv2.IApplicationLoadBalancerTarget, elbv2.INetworkLoadBalancerTarget, elb.ILoadBalancerTarget {
implements IBaseService, elbv2.IApplicationLoadBalancerTarget, elbv2.INetworkLoadBalancerTarget, elb.ILoadBalancerTarget {

/**
* The security groups which manage the allowed network traffic for the service.
Expand Down
57 changes: 57 additions & 0 deletions packages/@aws-cdk/aws-ecs/lib/base/from-service-attributes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Construct, Resource, Stack } from '@aws-cdk/core';
import { IBaseService } from '../base/base-service';
import { ICluster } from '../cluster';

/**
* The properties to import from the service.
*/
export interface ServiceAttributes {
/**
* The cluster that hosts the service.
*/
readonly cluster: ICluster;

/**
* The service ARN.
*
* @default - either this, or {@link serviceName}, is required
*/
readonly serviceArn?: string;

/**
* The name of the service.
*
* @default - either this, or {@link serviceArn}, is required
*/
readonly serviceName?: string;
}

export function fromServiceAtrributes(scope: Construct, id: string, attrs: ServiceAttributes): IBaseService {
if ((attrs.serviceArn && attrs.serviceName) || (!attrs.serviceArn && !attrs.serviceName)) {
throw new Error('You can only specify either serviceArn or serviceName.');
}

const stack = Stack.of(scope);
let name: string;
let arn: string;
if (attrs.serviceName) {
name = attrs.serviceName as string;
arn = stack.formatArn({
partition: stack.partition,
service: 'ecs',
region: stack.region,
account: stack.account,
resource: 'service',
resourceName: name,
});
} else {
arn = attrs.serviceArn as string;
name = stack.parseArn(arn).resourceName as string;
}
class Import extends Resource implements IBaseService {
public readonly serviceArn = arn;
public readonly serviceName = name;
public readonly cluster = attrs.cluster;
}
return new Import(scope, id);
}
36 changes: 35 additions & 1 deletion packages/@aws-cdk/aws-ecs/lib/ec2/ec2-service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as ec2 from '@aws-cdk/aws-ec2';
import { Construct, Lazy, Resource, Stack } from '@aws-cdk/core';
import { BaseService, BaseServiceOptions, IService, LaunchType, PropagatedTagSource } from '../base/base-service';
import { BaseService, BaseServiceOptions, IBaseService, IService, LaunchType, PropagatedTagSource } from '../base/base-service';
import { fromServiceAtrributes } from '../base/from-service-attributes';
import { NetworkMode, TaskDefinition } from '../base/task-definition';
import { ICluster } from '../cluster';
import { CfnService } from '../ecs.generated';
import { PlacementConstraint, PlacementStrategy } from '../placement';

Expand Down Expand Up @@ -87,6 +89,30 @@ export interface IEc2Service extends IService {

}

/**
* The properties to import from the service using the EC2 launch type.
*/
export interface Ec2ServiceAttributes {
/**
* The cluster that hosts the service.
*/
readonly cluster: ICluster;

/**
* The service ARN.
*
* @default - either this, or {@link serviceName}, is required
*/
readonly serviceArn?: string;

/**
* The name of the service.
*
* @default - either this, or {@link serviceArn}, is required
*/
readonly serviceName?: string;
}

/**
* This creates a service using the EC2 launch type on an ECS cluster.
*
Expand All @@ -100,10 +126,18 @@ export class Ec2Service extends BaseService implements IEc2Service {
public static fromEc2ServiceArn(scope: Construct, id: string, ec2ServiceArn: string): IEc2Service {
class Import extends Resource implements IEc2Service {
public readonly serviceArn = ec2ServiceArn;
public readonly serviceName = Stack.of(scope).parseArn(ec2ServiceArn).resourceName as string;
}
return new Import(scope, id);
}

/**
* Imports from the specified service attrributes.
*/
public static fromEc2ServiceAttributes(scope: Construct, id: string, attrs: Ec2ServiceAttributes): IBaseService {
return fromServiceAtrributes(scope, id, attrs);
}

private readonly constraints: CfnService.PlacementConstraintProperty[];
private readonly strategies: CfnService.PlacementStrategyProperty[];
private readonly daemon: boolean;
Expand Down
38 changes: 36 additions & 2 deletions packages/@aws-cdk/aws-ecs/lib/fargate/fargate-service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as ec2 from '@aws-cdk/aws-ec2';
import * as cdk from '@aws-cdk/core';
import { BaseService, BaseServiceOptions, IService, LaunchType, PropagatedTagSource } from '../base/base-service';
import { BaseService, BaseServiceOptions, IBaseService, IService, LaunchType, PropagatedTagSource } from '../base/base-service';
import { fromServiceAtrributes } from '../base/from-service-attributes';
import { TaskDefinition } from '../base/task-definition';
import { ICluster } from '../cluster';

/**
* The properties for defining a service using the Fargate launch type.
Expand Down Expand Up @@ -65,6 +67,30 @@ export interface IFargateService extends IService {

}

/**
* The properties to import from the service using the Fargate launch type.
*/
export interface FargateServiceAttributes {
/**
* The cluster that hosts the service.
*/
readonly cluster: ICluster;

/**
* The service ARN.
*
* @default - either this, or {@link serviceName}, is required
*/
readonly serviceArn?: string;

/**
* The name of the service.
*
* @default - either this, or {@link serviceArn}, is required
*/
readonly serviceName?: string;
}

/**
* This creates a service using the Fargate launch type on an ECS cluster.
*
Expand All @@ -73,15 +99,23 @@ export interface IFargateService extends IService {
export class FargateService extends BaseService implements IFargateService {

/**
* Import a task definition from the specified task definition ARN.
* Imports from the specified service ARN.
*/
public static fromFargateServiceArn(scope: cdk.Construct, id: string, fargateServiceArn: string): IFargateService {
class Import extends cdk.Resource implements IFargateService {
public readonly serviceArn = fargateServiceArn;
public readonly serviceName = cdk.Stack.of(scope).parseArn(fargateServiceArn).resourceName as string;
}
return new Import(scope, id);
}

/**
* Imports from the specified service attrributes.
*/
public static fromFargateServiceAttributes(scope: cdk.Construct, id: string, attrs: FargateServiceAttributes): IBaseService {
return fromServiceAtrributes(scope, id, attrs);
}

/**
* Constructs a new instance of the FargateService class.
*/
Expand Down
71 changes: 70 additions & 1 deletion packages/@aws-cdk/aws-ecs/test/ec2/test.ec2-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1936,5 +1936,74 @@ export = {
});

test.done();
}
},

'When import an EC2 Service': {
'with serviceArn'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const cluster = new ecs.Cluster(stack, 'EcsCluster');

// WHEN
const service = ecs.Ec2Service.fromEc2ServiceAttributes(stack, 'EcsService', {
serviceArn: 'arn:aws:ecs:us-west-2:123456789012:service/my-http-service',
cluster,
});

// THEN
test.equal(service.serviceArn, 'arn:aws:ecs:us-west-2:123456789012:service/my-http-service');
test.equal(service.serviceName, 'my-http-service');

test.done();
},

'with serviceName'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const pseudo = new cdk.ScopedAws(stack);
const cluster = new ecs.Cluster(stack, 'EcsCluster');

// WHEN
const service = ecs.Ec2Service.fromEc2ServiceAttributes(stack, 'EcsService', {
serviceName: 'my-http-service',
cluster,
});

// THEN
test.deepEqual(stack.resolve(service.serviceArn), stack.resolve(`arn:${pseudo.partition}:ecs:${pseudo.region}:${pseudo.accountId}:service/my-http-service`));
test.equal(service.serviceName, 'my-http-service');

test.done();
},

'throws an exception if both serviceArn and serviceName were provided for fromEc2ServiceAttributes'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const cluster = new ecs.Cluster(stack, 'EcsCluster');

test.throws(() => {
ecs.Ec2Service.fromEc2ServiceAttributes(stack, 'EcsService', {
serviceArn: 'arn:aws:ecs:us-west-2:123456789012:service/my-http-service',
serviceName: 'my-http-service',
cluster,
});
}, /only specify either serviceArn or serviceName/);

test.done();
},

'throws an exception if neither serviceArn nor serviceName were provided for fromEc2ServiceAttributes'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const cluster = new ecs.Cluster(stack, 'EcsCluster');

test.throws(() => {
ecs.Ec2Service.fromEc2ServiceAttributes(stack, 'EcsService', {
cluster,
});
}, /only specify either serviceArn or serviceName/);

test.done();
},
},
};
Loading

0 comments on commit bed5357

Please sign in to comment.