ecs_deploy_cli 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/ecs_deploy_cli.rb +1 -0
- data/lib/ecs_deploy_cli/cli.rb +7 -0
- data/lib/ecs_deploy_cli/cloudformation/default.yml +412 -0
- data/lib/ecs_deploy_cli/dsl/cluster.rb +70 -0
- data/lib/ecs_deploy_cli/dsl/parser.rb +6 -2
- data/lib/ecs_deploy_cli/dsl/service.rb +31 -2
- data/lib/ecs_deploy_cli/runner.rb +5 -0
- data/lib/ecs_deploy_cli/runners/base.rb +51 -0
- data/lib/ecs_deploy_cli/runners/setup.rb +128 -0
- data/lib/ecs_deploy_cli/version.rb +1 -1
- data/spec/ecs_deploy_cli/cli_spec.rb +8 -0
- data/spec/ecs_deploy_cli/dsl/cluster_spec.rb +48 -0
- data/spec/ecs_deploy_cli/dsl/service_spec.rb +31 -0
- data/spec/ecs_deploy_cli/runner_spec.rb +29 -0
- data/spec/ecs_deploy_cli/runners/base_spec.rb +57 -0
- data/spec/support/ECSFile +13 -1
- metadata +63 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36c1f26447ccb1748f1c064fbee8c5417502162d564e43b697148f0c9b399a12
|
4
|
+
data.tar.gz: d2571aff0f5ae5dcdf556a3b3cdeb9177310bcbb230a0817d92c7d276d8fcd30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71e0f4b2ac6191c6bc8194f07a352ddb51056caa1b30071e7a92a108ca22b0a2d6fe0fa4b5e92e7793a0df13c0260b96c1d0748939816014254bf4027afd524a
|
7
|
+
data.tar.gz: c40587c040e0077860b3cf573b8d03b31a189ec931143612de2f1184b72379562ff8cc03fe14da6415713cd230edf854ced7844c7b611435c1caa1f0b71021ab
|
data/lib/ecs_deploy_cli.rb
CHANGED
data/lib/ecs_deploy_cli/cli.rb
CHANGED
@@ -26,6 +26,13 @@ module EcsDeployCli
|
|
26
26
|
puts "ECS Deploy CLI Version #{EcsDeployCli::VERSION}."
|
27
27
|
end
|
28
28
|
|
29
|
+
desc 'setup', 'Creates the cluster and relative services'
|
30
|
+
option :file, default: 'ECSFile'
|
31
|
+
def setup
|
32
|
+
@parser = load(options[:file])
|
33
|
+
runner.setup!
|
34
|
+
end
|
35
|
+
|
29
36
|
desc 'deploy-scheduled-tasks', 'Updates all scheduled tasks defined in your ECSFile'
|
30
37
|
option :file, default: 'ECSFile'
|
31
38
|
def deploy_scheduled_tasks
|
@@ -0,0 +1,412 @@
|
|
1
|
+
AWSTemplateFormatVersion: '2010-09-09'
|
2
|
+
Description: >
|
3
|
+
AWS CloudFormation template to create a new VPC
|
4
|
+
or use an existing VPC for ECS deployment
|
5
|
+
in Create Cluster Wizard. Requires exactly 1
|
6
|
+
Instance Types for a Spot Request.
|
7
|
+
Parameters:
|
8
|
+
EcsClusterName:
|
9
|
+
Type: String
|
10
|
+
Description: >
|
11
|
+
Specifies the ECS Cluster Name with which the resources would be
|
12
|
+
associated
|
13
|
+
Default: default
|
14
|
+
EcsAmiId:
|
15
|
+
Type: String
|
16
|
+
Description: Specifies the AMI ID for your container instances.
|
17
|
+
EcsInstanceType:
|
18
|
+
Type: CommaDelimitedList
|
19
|
+
Description: >
|
20
|
+
Specifies the EC2 instance type for your container instances.
|
21
|
+
Defaults to m4.large
|
22
|
+
Default: m4.large
|
23
|
+
ConstraintDescription: must be a valid EC2 instance type.
|
24
|
+
KeyName:
|
25
|
+
Type: String
|
26
|
+
Description: >
|
27
|
+
Optional - Specifies the name of an existing Amazon EC2 key pair
|
28
|
+
to enable SSH access to the EC2 instances in your cluster.
|
29
|
+
Default: ''
|
30
|
+
VpcId:
|
31
|
+
Type: String
|
32
|
+
Description: >
|
33
|
+
Optional - Specifies the ID of an existing VPC in which to launch
|
34
|
+
your container instances. If you specify a VPC ID, you must specify a list of
|
35
|
+
existing subnets in that VPC. If you do not specify a VPC ID, a new VPC is created
|
36
|
+
with atleast 1 subnet.
|
37
|
+
Default: ''
|
38
|
+
ConstraintDescription: >
|
39
|
+
VPC Id must begin with 'vpc-' or leave blank to have a
|
40
|
+
new VPC created
|
41
|
+
SubnetIds:
|
42
|
+
Type: CommaDelimitedList
|
43
|
+
Description: >
|
44
|
+
Optional - Specifies the Comma separated list of existing VPC Subnet
|
45
|
+
Ids where ECS instances will run
|
46
|
+
Default: ''
|
47
|
+
SecurityGroupId:
|
48
|
+
Type: String
|
49
|
+
Description: >
|
50
|
+
Optional - Specifies the Security Group Id of an existing Security
|
51
|
+
Group. Leave blank to have a new Security Group created
|
52
|
+
Default: ''
|
53
|
+
VpcCidr:
|
54
|
+
Type: String
|
55
|
+
Description: Optional - Specifies the CIDR Block of VPC
|
56
|
+
Default: ''
|
57
|
+
SubnetCidr1:
|
58
|
+
Type: String
|
59
|
+
Description: Specifies the CIDR Block of Subnet 1
|
60
|
+
Default: ''
|
61
|
+
SubnetCidr2:
|
62
|
+
Type: String
|
63
|
+
Description: Specifies the CIDR Block of Subnet 2
|
64
|
+
Default: ''
|
65
|
+
SubnetCidr3:
|
66
|
+
Type: String
|
67
|
+
Description: Specifies the CIDR Block of Subnet 3
|
68
|
+
Default: ''
|
69
|
+
AsgMaxSize:
|
70
|
+
Type: Number
|
71
|
+
Description: >
|
72
|
+
Specifies the number of instances to launch and register to the cluster.
|
73
|
+
Defaults to 1.
|
74
|
+
Default: '1'
|
75
|
+
IamRoleInstanceProfile:
|
76
|
+
Type: String
|
77
|
+
Description: >
|
78
|
+
Specifies the Name or the Amazon Resource Name (ARN) of the instance
|
79
|
+
profile associated with the IAM role for the instance
|
80
|
+
SecurityIngressFromPort:
|
81
|
+
Type: Number
|
82
|
+
Description: >
|
83
|
+
Optional - Specifies the Start of Security Group port to open on
|
84
|
+
ECS instances - defaults to port 0
|
85
|
+
Default: '0'
|
86
|
+
SecurityIngressToPort:
|
87
|
+
Type: Number
|
88
|
+
Description: >
|
89
|
+
Optional - Specifies the End of Security Group port to open on ECS
|
90
|
+
instances - defaults to port 65535
|
91
|
+
Default: '65535'
|
92
|
+
SecurityIngressCidrIp:
|
93
|
+
Type: String
|
94
|
+
Description: >
|
95
|
+
Optional - Specifies the CIDR/IP range for Security Ports - defaults
|
96
|
+
to 0.0.0.0/0
|
97
|
+
Default: 0.0.0.0/0
|
98
|
+
EcsEndpoint:
|
99
|
+
Type: String
|
100
|
+
Description: >
|
101
|
+
Optional - Specifies the ECS Endpoint for the ECS Agent to connect to
|
102
|
+
Default: ''
|
103
|
+
VpcAvailabilityZones:
|
104
|
+
Type: CommaDelimitedList
|
105
|
+
Description: >
|
106
|
+
Specifies a comma-separated list of 3 VPC Availability Zones for
|
107
|
+
the creation of new subnets. These zones must have the available status.
|
108
|
+
Default: ''
|
109
|
+
RootEbsVolumeSize:
|
110
|
+
Type: Number
|
111
|
+
Description: >
|
112
|
+
Optional - Specifies the Size in GBs of the root EBS volume
|
113
|
+
Default: 30
|
114
|
+
EbsVolumeSize:
|
115
|
+
Type: Number
|
116
|
+
Description: >
|
117
|
+
Optional - Specifies the Size in GBs of the data storage EBS volume used by the Docker in the AL1 ECS-optimized AMI
|
118
|
+
Default: 22
|
119
|
+
EbsVolumeType:
|
120
|
+
Type: String
|
121
|
+
Description: Optional - Specifies the Type of (Amazon EBS) volume
|
122
|
+
Default: ''
|
123
|
+
AllowedValues:
|
124
|
+
- ''
|
125
|
+
- standard
|
126
|
+
- io1
|
127
|
+
- gp2
|
128
|
+
- sc1
|
129
|
+
- st1
|
130
|
+
ConstraintDescription: Must be a valid EC2 volume type.
|
131
|
+
RootDeviceName:
|
132
|
+
Type: String
|
133
|
+
Description: Optional - Specifies the device mapping for the root EBS volume.
|
134
|
+
Default: /dev/xvda
|
135
|
+
DeviceName:
|
136
|
+
Type: String
|
137
|
+
Description: Optional - Specifies the device mapping for the EBS volume used for data storage. Only applicable to AL1.
|
138
|
+
UseSpot:
|
139
|
+
Type: String
|
140
|
+
Default: 'false'
|
141
|
+
IamSpotFleetRoleArn:
|
142
|
+
Type: String
|
143
|
+
Default: ''
|
144
|
+
SpotPrice:
|
145
|
+
Type: String
|
146
|
+
Default: ''
|
147
|
+
SpotAllocationStrategy:
|
148
|
+
Type: String
|
149
|
+
Default: 'diversified'
|
150
|
+
AllowedValues:
|
151
|
+
- 'lowestPrice'
|
152
|
+
- 'diversified'
|
153
|
+
UserData:
|
154
|
+
Type: String
|
155
|
+
IsWindows:
|
156
|
+
Type: String
|
157
|
+
Default: 'false'
|
158
|
+
ConfigureRootVolume:
|
159
|
+
Type: String
|
160
|
+
Description: Optional - Specifies if there should be customization of the root volume
|
161
|
+
Default: 'false'
|
162
|
+
ConfigureDataVolume:
|
163
|
+
Type: String
|
164
|
+
Description: Optional - Specifies if there should be customization of the data volume
|
165
|
+
Default: 'true'
|
166
|
+
AutoAssignPublicIp:
|
167
|
+
Type: String
|
168
|
+
Default: 'INHERIT'
|
169
|
+
Conditions:
|
170
|
+
CreateEC2LCWithKeyPair:
|
171
|
+
!Not [!Equals [!Ref KeyName, '']]
|
172
|
+
SetEndpointToECSAgent:
|
173
|
+
!Not [!Equals [!Ref EcsEndpoint, '']]
|
174
|
+
CreateNewSecurityGroup:
|
175
|
+
!Equals [!Ref SecurityGroupId, '']
|
176
|
+
CreateNewVpc:
|
177
|
+
!Equals [!Ref VpcId, '']
|
178
|
+
CreateSubnet1: !And
|
179
|
+
- !Not [!Equals [!Ref SubnetCidr1, '']]
|
180
|
+
- !Condition CreateNewVpc
|
181
|
+
CreateSubnet2: !And
|
182
|
+
- !Not [!Equals [!Ref SubnetCidr2, '']]
|
183
|
+
- !Condition CreateSubnet1
|
184
|
+
CreateSubnet3: !And
|
185
|
+
- !Not [!Equals [!Ref SubnetCidr3, '']]
|
186
|
+
- !Condition CreateSubnet2
|
187
|
+
CreateWithSpot: !Equals [!Ref UseSpot, 'true']
|
188
|
+
CreateWithASG: !Not [!Condition CreateWithSpot]
|
189
|
+
CreateWithSpotPrice: !Not [!Equals [!Ref SpotPrice, '']]
|
190
|
+
IsConfiguringRootVolume: !Equals [!Ref ConfigureRootVolume, 'true']
|
191
|
+
IsConfiguringDataVolume: !Equals [!Ref ConfigureDataVolume, 'true']
|
192
|
+
IsInheritPublicIp: !Equals [!Ref AutoAssignPublicIp, 'INHERIT']
|
193
|
+
Resources:
|
194
|
+
Vpc:
|
195
|
+
Condition: CreateSubnet1
|
196
|
+
Type: AWS::EC2::VPC
|
197
|
+
Properties:
|
198
|
+
CidrBlock: !Ref VpcCidr
|
199
|
+
EnableDnsSupport: true
|
200
|
+
EnableDnsHostnames: true
|
201
|
+
PubSubnetAz1:
|
202
|
+
Condition: CreateSubnet1
|
203
|
+
Type: AWS::EC2::Subnet
|
204
|
+
Properties:
|
205
|
+
VpcId: !Ref Vpc
|
206
|
+
CidrBlock: !Ref SubnetCidr1
|
207
|
+
AvailabilityZone: !Select [ 0, !Ref VpcAvailabilityZones ]
|
208
|
+
MapPublicIpOnLaunch: true
|
209
|
+
PubSubnetAz2:
|
210
|
+
Condition: CreateSubnet2
|
211
|
+
Type: AWS::EC2::Subnet
|
212
|
+
Properties:
|
213
|
+
VpcId: !Ref Vpc
|
214
|
+
CidrBlock: !Ref SubnetCidr2
|
215
|
+
AvailabilityZone: !Select [ 1, !Ref VpcAvailabilityZones ]
|
216
|
+
MapPublicIpOnLaunch: true
|
217
|
+
PubSubnetAz3:
|
218
|
+
Condition: CreateSubnet3
|
219
|
+
Type: AWS::EC2::Subnet
|
220
|
+
Properties:
|
221
|
+
VpcId: !Ref Vpc
|
222
|
+
CidrBlock: !Ref SubnetCidr3
|
223
|
+
AvailabilityZone: !Select [ 2, !Ref VpcAvailabilityZones ]
|
224
|
+
MapPublicIpOnLaunch: true
|
225
|
+
InternetGateway:
|
226
|
+
Condition: CreateSubnet1
|
227
|
+
Type: AWS::EC2::InternetGateway
|
228
|
+
AttachGateway:
|
229
|
+
Condition: CreateSubnet1
|
230
|
+
Type: AWS::EC2::VPCGatewayAttachment
|
231
|
+
Properties:
|
232
|
+
VpcId: !Ref Vpc
|
233
|
+
InternetGatewayId: !Ref InternetGateway
|
234
|
+
RouteViaIgw:
|
235
|
+
Condition: CreateSubnet1
|
236
|
+
Type: AWS::EC2::RouteTable
|
237
|
+
Properties:
|
238
|
+
VpcId: !Ref Vpc
|
239
|
+
PublicRouteViaIgw:
|
240
|
+
Condition: CreateSubnet1
|
241
|
+
Type: AWS::EC2::Route
|
242
|
+
DependsOn: AttachGateway
|
243
|
+
Properties:
|
244
|
+
RouteTableId: !Ref RouteViaIgw
|
245
|
+
DestinationCidrBlock: 0.0.0.0/0
|
246
|
+
GatewayId: !Ref InternetGateway
|
247
|
+
PubSubnet1RouteTableAssociation:
|
248
|
+
Condition: CreateSubnet1
|
249
|
+
Type: AWS::EC2::SubnetRouteTableAssociation
|
250
|
+
Properties:
|
251
|
+
SubnetId: !Ref PubSubnetAz1
|
252
|
+
RouteTableId: !Ref RouteViaIgw
|
253
|
+
PubSubnet2RouteTableAssociation:
|
254
|
+
Condition: CreateSubnet2
|
255
|
+
Type: AWS::EC2::SubnetRouteTableAssociation
|
256
|
+
Properties:
|
257
|
+
SubnetId: !Ref PubSubnetAz2
|
258
|
+
RouteTableId: !Ref RouteViaIgw
|
259
|
+
PubSubnet3RouteTableAssociation:
|
260
|
+
Condition: CreateSubnet3
|
261
|
+
Type: AWS::EC2::SubnetRouteTableAssociation
|
262
|
+
Properties:
|
263
|
+
SubnetId: !Ref PubSubnetAz3
|
264
|
+
RouteTableId: !Ref RouteViaIgw
|
265
|
+
EcsSecurityGroup:
|
266
|
+
Condition: CreateNewSecurityGroup
|
267
|
+
Type: AWS::EC2::SecurityGroup
|
268
|
+
Properties:
|
269
|
+
GroupDescription: ECS Allowed Ports
|
270
|
+
VpcId: !If [ CreateSubnet1, !Ref Vpc, !Ref VpcId ]
|
271
|
+
SecurityGroupIngress:
|
272
|
+
IpProtocol: tcp
|
273
|
+
FromPort: !Ref SecurityIngressFromPort
|
274
|
+
ToPort: !Ref SecurityIngressToPort
|
275
|
+
CidrIp: !Ref SecurityIngressCidrIp
|
276
|
+
|
277
|
+
|
278
|
+
## TODO: FINISH
|
279
|
+
# ALBSecurityGroup:
|
280
|
+
# Condition: CreateNewLoadBalancer
|
281
|
+
# Type: AWS::EC2::SecurityGroup
|
282
|
+
# Properties:
|
283
|
+
# GroupDescription: ECS Allowed Ports
|
284
|
+
# VpcId: !If [ CreateSubnet1, !Ref Vpc, !Ref VpcId ]
|
285
|
+
# SecurityGroupIngress:
|
286
|
+
# IpProtocol: tcp
|
287
|
+
# FromPort: !Ref SecurityIngressFromPort
|
288
|
+
# ToPort: !Ref SecurityIngressToPort
|
289
|
+
# CidrIp: !Ref SecurityIngressCidrIp
|
290
|
+
|
291
|
+
# EcsLoadBalancer:
|
292
|
+
# Condition: CreateNewLoadBalancer
|
293
|
+
# Type: AWS::ElasticLoadBalancing::LoadBalancer
|
294
|
+
# Properties:
|
295
|
+
# LoadBalancerName: !Ref LoadBalancer
|
296
|
+
# AvailabilityZones:
|
297
|
+
# - !Select [ 0, !Ref VpcAvailabilityZones ]
|
298
|
+
# - !Select [ 1, !Ref VpcAvailabilityZones ]
|
299
|
+
# - !Select [ 2, !Ref VpcAvailabilityZones ]
|
300
|
+
# SecurityGroups:
|
301
|
+
# - !Select [ 0, !Ref VpcAvailabilityZones ]
|
302
|
+
# Subnets:
|
303
|
+
# -
|
304
|
+
## /TODO: FINISH
|
305
|
+
|
306
|
+
EcsInstanceLc:
|
307
|
+
Type: AWS::AutoScaling::LaunchConfiguration
|
308
|
+
Condition: CreateWithASG
|
309
|
+
Properties:
|
310
|
+
ImageId: !Ref EcsAmiId
|
311
|
+
InstanceType: !Select [ 0, !Ref EcsInstanceType ]
|
312
|
+
AssociatePublicIpAddress: !If [ IsInheritPublicIp, !Ref "AWS::NoValue", !Ref AutoAssignPublicIp ]
|
313
|
+
IamInstanceProfile: !Ref IamRoleInstanceProfile
|
314
|
+
KeyName: !If [ CreateEC2LCWithKeyPair, !Ref KeyName, !Ref "AWS::NoValue" ]
|
315
|
+
SecurityGroups: [ !If [ CreateNewSecurityGroup, !Ref EcsSecurityGroup, !Ref SecurityGroupId ] ]
|
316
|
+
BlockDeviceMappings:
|
317
|
+
- !If
|
318
|
+
- IsConfiguringRootVolume
|
319
|
+
- DeviceName: !Ref RootDeviceName
|
320
|
+
Ebs:
|
321
|
+
VolumeSize: !Ref RootEbsVolumeSize
|
322
|
+
VolumeType: !Ref EbsVolumeType
|
323
|
+
- !Ref AWS::NoValue
|
324
|
+
- !If
|
325
|
+
- IsConfiguringDataVolume
|
326
|
+
- DeviceName: !Ref DeviceName
|
327
|
+
Ebs:
|
328
|
+
VolumeSize: !Ref EbsVolumeSize
|
329
|
+
VolumeType: !Ref EbsVolumeType
|
330
|
+
- !Ref AWS::NoValue
|
331
|
+
UserData:
|
332
|
+
Fn::Base64: !Ref UserData
|
333
|
+
EcsInstanceAsg:
|
334
|
+
Type: AWS::AutoScaling::AutoScalingGroup
|
335
|
+
Condition: CreateWithASG
|
336
|
+
Properties:
|
337
|
+
VPCZoneIdentifier: !If
|
338
|
+
- CreateSubnet1
|
339
|
+
- !If
|
340
|
+
- CreateSubnet2
|
341
|
+
- !If
|
342
|
+
- CreateSubnet3
|
343
|
+
- [ !Sub "${PubSubnetAz1}, ${PubSubnetAz2}, ${PubSubnetAz3}" ]
|
344
|
+
- [ !Sub "${PubSubnetAz1}, ${PubSubnetAz2}" ]
|
345
|
+
- [ !Sub "${PubSubnetAz1}" ]
|
346
|
+
- !Ref SubnetIds
|
347
|
+
LaunchConfigurationName: !Ref EcsInstanceLc
|
348
|
+
MinSize: '0'
|
349
|
+
MaxSize: !Ref AsgMaxSize
|
350
|
+
DesiredCapacity: !Ref AsgMaxSize
|
351
|
+
Tags:
|
352
|
+
-
|
353
|
+
Key: Name
|
354
|
+
Value: !Sub "ECS Instance - ${AWS::StackName}"
|
355
|
+
PropagateAtLaunch: true
|
356
|
+
-
|
357
|
+
Key: Description
|
358
|
+
Value: "This instance is the part of the Auto Scaling group which was created through ECS Console"
|
359
|
+
PropagateAtLaunch: true
|
360
|
+
EcsSpotFleet:
|
361
|
+
Condition: CreateWithSpot
|
362
|
+
Type: AWS::EC2::SpotFleet
|
363
|
+
Properties:
|
364
|
+
SpotFleetRequestConfigData:
|
365
|
+
AllocationStrategy: !Ref SpotAllocationStrategy
|
366
|
+
IamFleetRole: !Ref IamSpotFleetRoleArn
|
367
|
+
TargetCapacity: !Ref AsgMaxSize
|
368
|
+
SpotPrice: !If [ CreateWithSpotPrice, !Ref SpotPrice, !Ref 'AWS::NoValue' ]
|
369
|
+
TerminateInstancesWithExpiration: true
|
370
|
+
LaunchSpecifications:
|
371
|
+
-
|
372
|
+
IamInstanceProfile:
|
373
|
+
Arn: !Ref IamRoleInstanceProfile
|
374
|
+
ImageId: !Ref EcsAmiId
|
375
|
+
InstanceType: !Select [ 0, !Ref EcsInstanceType ]
|
376
|
+
KeyName: !If [ CreateEC2LCWithKeyPair, !Ref KeyName, !Ref "AWS::NoValue" ]
|
377
|
+
Monitoring:
|
378
|
+
Enabled: true
|
379
|
+
SecurityGroups:
|
380
|
+
- GroupId: !If [ CreateNewSecurityGroup, !Ref EcsSecurityGroup, !Ref SecurityGroupId ]
|
381
|
+
SubnetId: !If
|
382
|
+
- CreateSubnet1
|
383
|
+
- !If
|
384
|
+
- CreateSubnet2
|
385
|
+
- !If
|
386
|
+
- CreateSubnet3
|
387
|
+
- !Join [ "," , [ !Ref PubSubnetAz1, !Ref PubSubnetAz2, !Ref PubSubnetAz3 ] ]
|
388
|
+
- !Join [ "," , [ !Ref PubSubnetAz1, !Ref PubSubnetAz2 ] ]
|
389
|
+
- !Ref PubSubnetAz1
|
390
|
+
- !Join [ "," , !Ref SubnetIds ]
|
391
|
+
BlockDeviceMappings:
|
392
|
+
- DeviceName: !Ref DeviceName
|
393
|
+
Ebs:
|
394
|
+
VolumeSize: !Ref EbsVolumeSize
|
395
|
+
VolumeType: !Ref EbsVolumeType
|
396
|
+
UserData:
|
397
|
+
Fn::Base64: !Ref UserData
|
398
|
+
Outputs:
|
399
|
+
EcsInstanceAsgName:
|
400
|
+
Condition: CreateWithASG
|
401
|
+
Description: Auto Scaling Group Name for ECS Instances
|
402
|
+
Value: !Ref EcsInstanceAsg
|
403
|
+
EcsSpotFleetRequestId:
|
404
|
+
Condition: CreateWithSpot
|
405
|
+
Description: Spot Fleet Request for ECS Instances
|
406
|
+
Value: !Ref EcsSpotFleet
|
407
|
+
UsedByECSCreateCluster:
|
408
|
+
Description: Flag used by ECS Create Cluster Wizard
|
409
|
+
Value: 'true'
|
410
|
+
TemplateVersion:
|
411
|
+
Description: The version of the template used by Create Cluster Wizard
|
412
|
+
Value: '2.0.0'
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module DSL
|
5
|
+
class Cluster
|
6
|
+
include AutoOptions
|
7
|
+
|
8
|
+
allowed_options :instances_count, :instance_type, :ebs_volume_size, :keypair_name
|
9
|
+
|
10
|
+
def initialize(name, config)
|
11
|
+
@config = config
|
12
|
+
_options[:name] = name.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def vpc(id = nil, &block)
|
16
|
+
@vpc = VPC.new(id)
|
17
|
+
@vpc.instance_exec(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def as_definition
|
21
|
+
{
|
22
|
+
instances_count: 1,
|
23
|
+
|
24
|
+
device_name: '/dev/xvda',
|
25
|
+
ebs_volume_size: 22,
|
26
|
+
ebs_volume_type: 'gp2',
|
27
|
+
|
28
|
+
root_device_name: '/dev/xvdcz',
|
29
|
+
root_ebs_volume_size: 30,
|
30
|
+
|
31
|
+
vpc: @vpc&.as_definition
|
32
|
+
}.merge(_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
class VPC
|
36
|
+
include AutoOptions
|
37
|
+
allowed_options :cidr, :subnet1, :subnet2, :subnet3
|
38
|
+
|
39
|
+
def initialize(id)
|
40
|
+
_options[:id] = id
|
41
|
+
end
|
42
|
+
|
43
|
+
def availability_zones(*values)
|
44
|
+
_options[:availability_zones] = values.join(',')
|
45
|
+
end
|
46
|
+
|
47
|
+
def subnet_ids(*values)
|
48
|
+
_options[:subnet_ids] = values.join(',')
|
49
|
+
end
|
50
|
+
|
51
|
+
def as_definition
|
52
|
+
validate! if _options[:id]
|
53
|
+
|
54
|
+
{
|
55
|
+
cidr: '10.0.0.0/16',
|
56
|
+
subnet1: '10.0.0.0/24',
|
57
|
+
subnet2: '10.0.1.0/24',
|
58
|
+
subnet3: '10.0.2.0/24'
|
59
|
+
}.merge(_options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def validate!
|
63
|
+
[
|
64
|
+
:subnet1, :subnet_ids, :availability_zones
|
65
|
+
].each { |key| raise "Missing required parameter #{key}" unless _options[key] }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -40,8 +40,11 @@ module EcsDeployCli
|
|
40
40
|
@crons[name].instance_exec(&block)
|
41
41
|
end
|
42
42
|
|
43
|
-
def cluster(name)
|
43
|
+
def cluster(name, &block)
|
44
44
|
config[:cluster] = name
|
45
|
+
@cluster ||= {}.with_indifferent_access
|
46
|
+
@cluster = Cluster.new(name, config)
|
47
|
+
@cluster.instance_exec(&block) if block
|
45
48
|
end
|
46
49
|
|
47
50
|
def config
|
@@ -58,7 +61,8 @@ module EcsDeployCli
|
|
58
61
|
resolved_containers = (@containers || {}).transform_values(&:as_definition)
|
59
62
|
resolved_tasks = (@tasks || {}).transform_values { |t| t.as_definition(resolved_containers) }
|
60
63
|
resolved_crons = (@crons || {}).transform_values { |t| t.as_definition(resolved_tasks) }
|
61
|
-
|
64
|
+
resolved_cluster = @cluster.as_definition
|
65
|
+
[@services, resolved_tasks, resolved_crons, resolved_cluster]
|
62
66
|
end
|
63
67
|
|
64
68
|
def self.load(file)
|
@@ -18,13 +18,42 @@ module EcsDeployCli
|
|
18
18
|
_options
|
19
19
|
end
|
20
20
|
|
21
|
+
def load_balancer(name, &block)
|
22
|
+
@load_balancers ||= []
|
23
|
+
|
24
|
+
load_balancer = LoadBalancer.new(name, @config)
|
25
|
+
load_balancer.instance_exec(&block)
|
26
|
+
|
27
|
+
@load_balancers << load_balancer
|
28
|
+
end
|
29
|
+
|
21
30
|
def as_definition(task)
|
22
31
|
{
|
23
32
|
cluster: @config[:cluster],
|
24
|
-
service:
|
25
|
-
task_definition: task
|
33
|
+
service: _options[:service],
|
34
|
+
task_definition: task,
|
35
|
+
load_balancers: @load_balancers&.map(&:as_definition) || []
|
26
36
|
}
|
27
37
|
end
|
38
|
+
|
39
|
+
class LoadBalancer
|
40
|
+
include AutoOptions
|
41
|
+
allowed_options :container_name, :container_port
|
42
|
+
|
43
|
+
def initialize(name, config)
|
44
|
+
_options[:load_balancer_name] = name
|
45
|
+
@config = config
|
46
|
+
end
|
47
|
+
|
48
|
+
def target_group_arn(value)
|
49
|
+
_options[:target_group_arn] = "arn:aws:elasticloadbalancing:#{@config[:aws_region]}:#{@config[:aws_profile_id]}:targetgroup/#{value}"
|
50
|
+
_options.delete(:load_balancer_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def as_definition
|
54
|
+
_options
|
55
|
+
end
|
56
|
+
end
|
28
57
|
end
|
29
58
|
end
|
30
59
|
end
|
@@ -7,6 +7,7 @@ require 'ecs_deploy_cli/runners/diff'
|
|
7
7
|
require 'ecs_deploy_cli/runners/update_crons'
|
8
8
|
require 'ecs_deploy_cli/runners/update_services'
|
9
9
|
require 'ecs_deploy_cli/runners/run_task'
|
10
|
+
require 'ecs_deploy_cli/runners/setup'
|
10
11
|
|
11
12
|
module EcsDeployCli
|
12
13
|
class Runner
|
@@ -14,6 +15,10 @@ module EcsDeployCli
|
|
14
15
|
@parser = parser
|
15
16
|
end
|
16
17
|
|
18
|
+
def setup!
|
19
|
+
EcsDeployCli::Runners::Setup.new(@parser).run!
|
20
|
+
end
|
21
|
+
|
17
22
|
def validate!
|
18
23
|
EcsDeployCli::Runners::Validate.new(@parser).run!
|
19
24
|
end
|
@@ -11,14 +11,35 @@ module EcsDeployCli
|
|
11
11
|
raise NotImplementedError, 'abstract method'
|
12
12
|
end
|
13
13
|
|
14
|
+
def update_task(definition)
|
15
|
+
_update_task(definition)
|
16
|
+
end
|
17
|
+
|
14
18
|
protected
|
15
19
|
|
16
20
|
def _update_task(definition)
|
21
|
+
definition[:container_definitions].each do |container|
|
22
|
+
next unless container.dig(:log_configuration, :log_driver) == 'awslogs'
|
23
|
+
|
24
|
+
_create_cloudwatch_logs_if_needed(container.dig(:log_configuration, :options, 'awslogs-group'))
|
25
|
+
end
|
26
|
+
|
17
27
|
ecs_client.register_task_definition(
|
18
28
|
definition
|
19
29
|
).to_h[:task_definition]
|
20
30
|
end
|
21
31
|
|
32
|
+
def _create_cloudwatch_logs_if_needed(prefix)
|
33
|
+
log_group = cwl_client.describe_log_groups(log_group_name_prefix: prefix, limit: 1).to_h[:log_groups]
|
34
|
+
return if log_group.any?
|
35
|
+
|
36
|
+
cwl_client.create_log_group(log_group_name: prefix)
|
37
|
+
cwl_client.put_retention_policy(
|
38
|
+
log_group_name: prefix,
|
39
|
+
retention_in_days: 14
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
22
43
|
def ec2_client
|
23
44
|
@ec2_client ||= begin
|
24
45
|
require 'aws-sdk-ec2'
|
@@ -46,6 +67,36 @@ module EcsDeployCli
|
|
46
67
|
end
|
47
68
|
end
|
48
69
|
|
70
|
+
def ssm_client
|
71
|
+
@cwl_client ||= begin
|
72
|
+
require 'aws-sdk-ssm'
|
73
|
+
Aws::SSM::Client.new(
|
74
|
+
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
75
|
+
region: config[:aws_region]
|
76
|
+
)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def cwl_client
|
81
|
+
@cwl_client ||= begin
|
82
|
+
require 'aws-sdk-cloudwatchlogs'
|
83
|
+
Aws::CloudWatchLogs::Client.new(
|
84
|
+
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
85
|
+
region: config[:aws_region]
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def cf_client
|
91
|
+
@cl_client ||= begin
|
92
|
+
require 'aws-sdk-cloudformation'
|
93
|
+
Aws::CloudFormation::Client.new(
|
94
|
+
profile: ENV.fetch('AWS_PROFILE', 'default'),
|
95
|
+
region: config[:aws_region]
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
49
100
|
def config
|
50
101
|
@parser.config
|
51
102
|
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EcsDeployCli
|
4
|
+
module Runners
|
5
|
+
class Setup < Base
|
6
|
+
def run!
|
7
|
+
services, resolved_tasks, _, cluster_options = @parser.resolve
|
8
|
+
|
9
|
+
setup_cluster! cluster_options
|
10
|
+
setup_services! services, resolved_tasks: resolved_tasks
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def setup_cluster!(cluster_options)
|
16
|
+
clusters = ecs_client.describe_clusters(clusters: [config[:cluster]]).to_h[:clusters]
|
17
|
+
if clusters.length == 1
|
18
|
+
EcsDeployCli.logger.info 'Cluster already created, skipping.'
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
EcsDeployCli.logger.info "Creating cluster #{config[:cluster]}..."
|
23
|
+
|
24
|
+
params = create_params(cluster_options)
|
25
|
+
|
26
|
+
ecs_client.create_cluster(
|
27
|
+
cluster_name: config[:cluster]
|
28
|
+
)
|
29
|
+
|
30
|
+
stack_name = "EC2ContainerService-#{config[:cluster]}"
|
31
|
+
|
32
|
+
|
33
|
+
cf_client.create_stack(
|
34
|
+
stack_name: stack_name,
|
35
|
+
template_body: File.read(File.join(__dir__, '..', 'cloudformation', 'default.yml')),
|
36
|
+
on_failure: 'ROLLBACK',
|
37
|
+
parameters: format_cloudformation_params(params)
|
38
|
+
)
|
39
|
+
|
40
|
+
cf_client.wait_until(:stack_create_complete, { stack_name: stack_name }, delay: 30, max_attempts: 120)
|
41
|
+
EcsDeployCli.logger.info "Cluster #{config[:cluster]} created!"
|
42
|
+
end
|
43
|
+
|
44
|
+
def setup_services!(services, resolved_tasks:)
|
45
|
+
services.each do |service_name, service_definition|
|
46
|
+
if ecs_client.describe_services(cluster: config[:cluster], services: [service_name]).to_h[:services].any?
|
47
|
+
EcsDeployCli.logger.info "Service #{service_name} already created, skipping."
|
48
|
+
next
|
49
|
+
end
|
50
|
+
|
51
|
+
EcsDeployCli.logger.info "Creating service #{service_name}..."
|
52
|
+
task_definition = _update_task resolved_tasks[service_definition.options[:task]]
|
53
|
+
task_name = "#{task_definition[:family]}:#{task_definition[:revision]}"
|
54
|
+
|
55
|
+
ecs_client.create_service(
|
56
|
+
cluster: config[:cluster],
|
57
|
+
desired_count: 1,
|
58
|
+
load_balancers: service_definition.as_definition(task_definition)[:load_balancers],
|
59
|
+
service_name: service_name,
|
60
|
+
task_definition: task_name
|
61
|
+
)
|
62
|
+
EcsDeployCli.logger.info "Service #{service_name} created!"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_params(cluster_options)
|
67
|
+
raise ArgumentError, 'Missing vpc configuration' unless cluster_options[:vpc]
|
68
|
+
|
69
|
+
{
|
70
|
+
'AsgMaxSize' => cluster_options[:instances_count],
|
71
|
+
'AutoAssignPublicIp' => 'INHERIT',
|
72
|
+
'ConfigureDataVolume' => false,
|
73
|
+
'ConfigureRootVolume' => true,
|
74
|
+
'DeviceName' => cluster_options[:device_name],
|
75
|
+
'EbsVolumeSize' => cluster_options[:ebs_volume_size],
|
76
|
+
'EbsVolumeType' => cluster_options[:ebs_volume_type],
|
77
|
+
'EcsAmiId' => load_ami_id,
|
78
|
+
'EcsClusterName' => config[:cluster],
|
79
|
+
'EcsEndpoint' => nil,
|
80
|
+
'EcsInstanceType' => cluster_options[:instance_type],
|
81
|
+
'IamRoleInstanceProfile' => "arn:aws:iam::#{config[:aws_profile_id]}:instance-profile/ecsInstanceRole",
|
82
|
+
'IamSpotFleetRoleArn' => nil,
|
83
|
+
'IsWindows' => false,
|
84
|
+
'KeyName' => cluster_options[:keypair_name],
|
85
|
+
'RootDeviceName' => cluster_options[:root_device_name],
|
86
|
+
'RootEbsVolumeSize' => cluster_options[:root_ebs_volume_size],
|
87
|
+
|
88
|
+
##### TODO: Implement this feature
|
89
|
+
'SecurityGroupId' => nil,
|
90
|
+
'SecurityIngressCidrIp' => '0.0.0.0/0',
|
91
|
+
'SecurityIngressFromPort' => 80,
|
92
|
+
'SecurityIngressToPort' => 80,
|
93
|
+
#####
|
94
|
+
|
95
|
+
##### TODO: Implement this feature
|
96
|
+
'SpotAllocationStrategy' => 'diversified',
|
97
|
+
'SpotPrice' => nil,
|
98
|
+
'UseSpot' => false,
|
99
|
+
#####
|
100
|
+
|
101
|
+
'UserData' => "#!/bin/bash\necho ECS_CLUSTER=#{config[:cluster]} >> /etc/ecs/ecs.config;echo ECS_BACKEND_HOST= >> /etc/ecs/ecs.config;",
|
102
|
+
'VpcAvailabilityZones' => cluster_options.dig(:vpc, :availability_zones),
|
103
|
+
'VpcCidr' => cluster_options.dig(:vpc, :cidr),
|
104
|
+
'SubnetCidr1' => cluster_options.dig(:vpc, :subnet1),
|
105
|
+
'SubnetCidr2' => cluster_options.dig(:vpc, :subnet2),
|
106
|
+
'SubnetCidr3' => cluster_options.dig(:vpc, :subnet3),
|
107
|
+
|
108
|
+
'VpcId' => cluster_options.dig(:vpc, :id),
|
109
|
+
'SubnetIds' => cluster_options.dig(:vpc, :subnet_ids)
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
def format_cloudformation_params(params)
|
114
|
+
params.map { |k, v| { parameter_key: k, parameter_value: v.to_s } }
|
115
|
+
end
|
116
|
+
|
117
|
+
def load_ami_id
|
118
|
+
ami_data = ssm_client.get_parameter(
|
119
|
+
name: '/aws/service/ecs/optimized-ami/amazon-linux-2/recommended'
|
120
|
+
).to_h[:parameter]
|
121
|
+
|
122
|
+
ami_details = JSON.parse(ami_data[:value]).with_indifferent_access
|
123
|
+
|
124
|
+
ami_details[:image_id]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -48,6 +48,14 @@ describe EcsDeployCli::CLI do
|
|
48
48
|
described_class.start(['run-task', 'yourproject', '--subnets', 'subnet-123123', '--file', 'spec/support/ECSFile'])
|
49
49
|
end
|
50
50
|
|
51
|
+
it 'runs setup' do
|
52
|
+
expect(runner).to receive(:setup!)
|
53
|
+
described_class.no_commands do
|
54
|
+
expect_any_instance_of(described_class).to receive(:runner).at_least(:once).and_return(runner)
|
55
|
+
end
|
56
|
+
expect { described_class.start(['setup', '--file', 'spec/support/ECSFile']) }.to output(/[WARNING]/).to_stdout
|
57
|
+
end
|
58
|
+
|
51
59
|
it 'runs deploy' do
|
52
60
|
expect(runner).to receive(:update_crons!)
|
53
61
|
expect(runner).to receive(:update_services!).with(timeout: 500)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe EcsDeployCli::DSL::Cluster do
|
6
|
+
context 'defines cluster data' do
|
7
|
+
subject { described_class.new('mydata-cluster', { aws_profile_id: '123123', aws_region: 'eu-central-1' }) }
|
8
|
+
|
9
|
+
it '#vpc' do
|
10
|
+
subject.instances_count 1
|
11
|
+
subject.instance_type 't2.small'
|
12
|
+
subject.keypair_name 'test'
|
13
|
+
|
14
|
+
subject.vpc do
|
15
|
+
cidr '11.0.0.0/16'
|
16
|
+
subnet1 '11.0.0.0/24'
|
17
|
+
subnet2 '11.0.1.0/24'
|
18
|
+
subnet3 '11.0.2.0/24'
|
19
|
+
subnet_ids 'subnet-123', 'subnet-321', 'subnet-333'
|
20
|
+
|
21
|
+
availability_zones 'eu-central-1a', 'eu-central-1b', 'eu-central-1c'
|
22
|
+
end
|
23
|
+
|
24
|
+
expect(subject.as_definition).to eq(
|
25
|
+
{
|
26
|
+
device_name: '/dev/xvda',
|
27
|
+
ebs_volume_size: 22,
|
28
|
+
ebs_volume_type: 'gp2',
|
29
|
+
instances_count: 1,
|
30
|
+
instance_type: 't2.small',
|
31
|
+
keypair_name: 'test',
|
32
|
+
name: 'mydata-cluster',
|
33
|
+
root_device_name: '/dev/xvdcz',
|
34
|
+
root_ebs_volume_size: 30,
|
35
|
+
vpc: {
|
36
|
+
availability_zones: 'eu-central-1a,eu-central-1b,eu-central-1c',
|
37
|
+
cidr: '11.0.0.0/16',
|
38
|
+
id: nil,
|
39
|
+
subnet1: '11.0.0.0/24',
|
40
|
+
subnet2: '11.0.1.0/24',
|
41
|
+
subnet3: '11.0.2.0/24',
|
42
|
+
subnet_ids: 'subnet-123,subnet-321,subnet-333'
|
43
|
+
}
|
44
|
+
}
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe EcsDeployCli::DSL::Service do
|
6
|
+
context 'defines service data' do
|
7
|
+
subject { described_class.new('test', { aws_profile_id: '123123', aws_region: 'eu-central-1' }) }
|
8
|
+
|
9
|
+
it 'has the correct name' do
|
10
|
+
expect(subject.as_definition({})[:service]).to eq('test')
|
11
|
+
end
|
12
|
+
|
13
|
+
it '#load_balancer' do
|
14
|
+
subject.load_balancer :'yourproject-load-balancer' do
|
15
|
+
target_group_arn 'loader-target-group/123abc'
|
16
|
+
container_name :web
|
17
|
+
container_port 80
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(subject.as_definition({})[:load_balancers]).to eq(
|
21
|
+
[
|
22
|
+
{
|
23
|
+
container_name: :web,
|
24
|
+
container_port: 80,
|
25
|
+
target_group_arn: 'arn:aws:elasticloadbalancing:eu-central-1:123123:targetgroup/loader-target-group/123abc'
|
26
|
+
}
|
27
|
+
]
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -2,14 +2,20 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
require 'aws-sdk-cloudwatchevents'
|
5
|
+
require 'aws-sdk-cloudwatchlogs'
|
5
6
|
require 'aws-sdk-ec2'
|
7
|
+
require 'aws-sdk-ssm'
|
8
|
+
require 'aws-sdk-cloudformation'
|
6
9
|
|
7
10
|
describe EcsDeployCli::Runner do
|
8
11
|
context 'defines task data' do
|
9
12
|
let(:parser) { EcsDeployCli::DSL::Parser.load('spec/support/ECSFile') }
|
10
13
|
subject { described_class.new(parser) }
|
14
|
+
let(:mock_cf_client) { Aws::CloudFormation::Client.new(stub_responses: true) }
|
15
|
+
let(:mock_ssm_client) { Aws::SSM::Client.new(stub_responses: true) }
|
11
16
|
let(:mock_ecs_client) { Aws::ECS::Client.new(stub_responses: true) }
|
12
17
|
let(:mock_ec2_client) { Aws::EC2::Client.new(stub_responses: true) }
|
18
|
+
let(:mock_cwl_client) { Aws::CloudWatchLogs::Client.new(stub_responses: true) }
|
13
19
|
let(:mock_cwe_client) do
|
14
20
|
Aws::CloudWatchEvents::Client.new(stub_responses: true)
|
15
21
|
end
|
@@ -84,6 +90,26 @@ describe EcsDeployCli::Runner do
|
|
84
90
|
ENV['AWS_REGION'] = nil
|
85
91
|
end
|
86
92
|
|
93
|
+
it '#setup!' do
|
94
|
+
mock_ssm_client.stub_responses(:get_parameter, {
|
95
|
+
parameter: {
|
96
|
+
name: '/aws/service/ecs/optimized-ami/amazon-linux-2/recommended',
|
97
|
+
type: 'String',
|
98
|
+
value: '{"schema_version":1,"image_name":"amzn2-ami-ecs-hvm-2.0.20210331-x86_64-ebs","image_id":"ami-03bbf53329af34379","os":"Amazon Linux 2","ecs_runtime_version":"Docker version 19.03.13-ce","ecs_agent_version":"1.51.0"}'
|
99
|
+
}
|
100
|
+
})
|
101
|
+
|
102
|
+
expect(mock_cf_client).to receive(:wait_until)
|
103
|
+
expect(mock_ecs_client).to receive(:create_service)
|
104
|
+
|
105
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cwl_client).at_least(:once).and_return(mock_cwl_client)
|
106
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
107
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ssm_client).at_least(:once).and_return(mock_ssm_client)
|
108
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cf_client).at_least(:once).and_return(mock_cf_client)
|
109
|
+
|
110
|
+
subject.setup!
|
111
|
+
end
|
112
|
+
|
87
113
|
it '#ssh' do
|
88
114
|
expect(mock_ecs_client).to receive(:list_container_instances).and_return({ container_instance_arns: ['arn:123123'] })
|
89
115
|
expect(mock_ecs_client).to receive(:describe_container_instances).and_return(double(container_instances: [double(ec2_instance_id: 'i-123123')]))
|
@@ -124,6 +150,7 @@ describe EcsDeployCli::Runner do
|
|
124
150
|
|
125
151
|
mock_cwe_client.stub_responses(:run_task)
|
126
152
|
|
153
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cwl_client).at_least(:once).and_return(mock_cwl_client)
|
127
154
|
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
128
155
|
|
129
156
|
subject.run_task!('yourproject-cron', launch_type: 'FARGATE', security_groups: [], subnets: [])
|
@@ -134,6 +161,7 @@ describe EcsDeployCli::Runner do
|
|
134
161
|
|
135
162
|
mock_cwe_client.stub_responses(:list_targets_by_rule, { targets: [{ id: '123', arn: 'arn:123' }] })
|
136
163
|
|
164
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cwl_client).at_least(:once).and_return(mock_cwl_client)
|
137
165
|
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
138
166
|
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cwe_client).at_least(:once).and_return(mock_cwe_client)
|
139
167
|
|
@@ -149,6 +177,7 @@ describe EcsDeployCli::Runner do
|
|
149
177
|
)
|
150
178
|
expect(mock_ecs_client).to receive(:wait_until)
|
151
179
|
|
180
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cwl_client).at_least(:once).and_return(mock_cwl_client)
|
152
181
|
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
153
182
|
|
154
183
|
subject.update_services!
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'aws-sdk-cloudwatchevents'
|
5
|
+
require 'aws-sdk-cloudwatchlogs'
|
6
|
+
require 'aws-sdk-ec2'
|
7
|
+
require 'aws-sdk-ssm'
|
8
|
+
require 'aws-sdk-cloudformation'
|
9
|
+
|
10
|
+
describe EcsDeployCli::Runners::Base do
|
11
|
+
let(:parser) { EcsDeployCli::DSL::Parser.load('spec/support/ECSFile') }
|
12
|
+
subject { described_class.new(parser) }
|
13
|
+
let(:mock_cf_client) { Aws::CloudFormation::Client.new(stub_responses: true) }
|
14
|
+
let(:mock_ssm_client) { Aws::SSM::Client.new(stub_responses: true) }
|
15
|
+
let(:mock_ecs_client) { Aws::ECS::Client.new(stub_responses: true) }
|
16
|
+
let(:mock_ec2_client) { Aws::EC2::Client.new(stub_responses: true) }
|
17
|
+
let(:mock_cwl_client) { Aws::CloudWatchLogs::Client.new(stub_responses: true) }
|
18
|
+
let(:mock_cwe_client) do
|
19
|
+
Aws::CloudWatchEvents::Client.new(stub_responses: true)
|
20
|
+
end
|
21
|
+
|
22
|
+
around(:each) do |example|
|
23
|
+
ENV['AWS_PROFILE_ID'] = '123123123'
|
24
|
+
ENV['AWS_REGION'] = 'us-east-1'
|
25
|
+
example.run
|
26
|
+
ENV['AWS_PROFILE_ID'] = nil
|
27
|
+
ENV['AWS_REGION'] = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
context '#update_task' do
|
31
|
+
it 'creates cloud watch logs if missing' do
|
32
|
+
_, tasks, = parser.resolve
|
33
|
+
|
34
|
+
expect(mock_cwl_client).to receive(:describe_log_groups).at_least(:once).and_return({ log_groups: [] })
|
35
|
+
expect(mock_cwl_client).to receive(:create_log_group).at_least(:once)
|
36
|
+
expect(mock_ecs_client).to receive(:register_task_definition).at_least(:once).and_return({ task_definition: { family: 'some', revision: '1' } })
|
37
|
+
|
38
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cwl_client).at_least(:once).and_return(mock_cwl_client)
|
39
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
40
|
+
|
41
|
+
subject.update_task(tasks.values.first)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'creates no cloudwatch log group if is already there' do
|
45
|
+
_, tasks, = parser.resolve
|
46
|
+
|
47
|
+
expect(mock_cwl_client).to receive(:describe_log_groups).at_least(:once).and_return({ log_groups: [{}] })
|
48
|
+
expect(mock_cwl_client).not_to receive(:create_log_group)
|
49
|
+
expect(mock_ecs_client).to receive(:register_task_definition).at_least(:once).and_return({ task_definition: { family: 'some', revision: '1' } })
|
50
|
+
|
51
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:cwl_client).at_least(:once).and_return(mock_cwl_client)
|
52
|
+
expect_any_instance_of(EcsDeployCli::Runners::Base).to receive(:ecs_client).at_least(:once).and_return(mock_ecs_client)
|
53
|
+
|
54
|
+
subject.update_task(tasks.values.first)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/support/ECSFile
CHANGED
@@ -4,7 +4,14 @@ aws_region ENV.fetch('AWS_REGION', 'eu-central-1')
|
|
4
4
|
aws_profile_id ENV['AWS_PROFILE_ID']
|
5
5
|
|
6
6
|
# Defining the cluster name
|
7
|
-
cluster 'yourproject-cluster'
|
7
|
+
cluster 'yourproject-cluster' do
|
8
|
+
instance_type 't3.small'
|
9
|
+
keypair_name 'test'
|
10
|
+
|
11
|
+
vpc do
|
12
|
+
availability_zones 'eu-central-1a', 'eu-central-1b', 'eu-central-1c'
|
13
|
+
end
|
14
|
+
end
|
8
15
|
|
9
16
|
# This is used as a template for the next two containers, it will not be used inside a task
|
10
17
|
container :base_container do
|
@@ -49,6 +56,11 @@ end
|
|
49
56
|
# The main service
|
50
57
|
service :'yourproject-service' do
|
51
58
|
task :yourproject
|
59
|
+
load_balancer :'yourproject-load-balancer' do
|
60
|
+
target_group_arn 'loader-target-group/123abc'
|
61
|
+
container_name :web
|
62
|
+
container_port 80
|
63
|
+
end
|
52
64
|
end
|
53
65
|
|
54
66
|
# A task for cron jobs
|
metadata
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ecs_deploy_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mònade
|
8
8
|
- ProGM
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
date: 2021-03-31 00:00:00.000000000 Z
|
@@ -31,6 +31,20 @@ dependencies:
|
|
31
31
|
- - "<"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '7'
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: aws-sdk-cloudformation
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1'
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1'
|
34
48
|
- !ruby/object:Gem::Dependency
|
35
49
|
name: aws-sdk-cloudwatchevents
|
36
50
|
requirement: !ruby/object:Gem::Requirement
|
@@ -45,6 +59,20 @@ dependencies:
|
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
61
|
version: '1'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: aws-sdk-cloudwatchlogs
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1'
|
69
|
+
type: :runtime
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1'
|
48
76
|
- !ruby/object:Gem::Dependency
|
49
77
|
name: aws-sdk-ec2
|
50
78
|
requirement: !ruby/object:Gem::Requirement
|
@@ -73,6 +101,20 @@ dependencies:
|
|
73
101
|
- - "~>"
|
74
102
|
- !ruby/object:Gem::Version
|
75
103
|
version: '1'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: aws-sdk-ssm
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1'
|
111
|
+
type: :runtime
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1'
|
76
118
|
- !ruby/object:Gem::Dependency
|
77
119
|
name: colorize
|
78
120
|
requirement: !ruby/object:Gem::Requirement
|
@@ -155,7 +197,9 @@ files:
|
|
155
197
|
- lib/ecs-deploy-cli.rb
|
156
198
|
- lib/ecs_deploy_cli.rb
|
157
199
|
- lib/ecs_deploy_cli/cli.rb
|
200
|
+
- lib/ecs_deploy_cli/cloudformation/default.yml
|
158
201
|
- lib/ecs_deploy_cli/dsl/auto_options.rb
|
202
|
+
- lib/ecs_deploy_cli/dsl/cluster.rb
|
159
203
|
- lib/ecs_deploy_cli/dsl/container.rb
|
160
204
|
- lib/ecs_deploy_cli/dsl/cron.rb
|
161
205
|
- lib/ecs_deploy_cli/dsl/parser.rb
|
@@ -166,6 +210,7 @@ files:
|
|
166
210
|
- lib/ecs_deploy_cli/runners/diff.rb
|
167
211
|
- lib/ecs_deploy_cli/runners/logs.rb
|
168
212
|
- lib/ecs_deploy_cli/runners/run_task.rb
|
213
|
+
- lib/ecs_deploy_cli/runners/setup.rb
|
169
214
|
- lib/ecs_deploy_cli/runners/ssh.rb
|
170
215
|
- lib/ecs_deploy_cli/runners/status.rb
|
171
216
|
- lib/ecs_deploy_cli/runners/update_crons.rb
|
@@ -173,11 +218,14 @@ files:
|
|
173
218
|
- lib/ecs_deploy_cli/runners/validate.rb
|
174
219
|
- lib/ecs_deploy_cli/version.rb
|
175
220
|
- spec/ecs_deploy_cli/cli_spec.rb
|
221
|
+
- spec/ecs_deploy_cli/dsl/cluster_spec.rb
|
176
222
|
- spec/ecs_deploy_cli/dsl/container_spec.rb
|
177
223
|
- spec/ecs_deploy_cli/dsl/cron_spec.rb
|
178
224
|
- spec/ecs_deploy_cli/dsl/parser_spec.rb
|
225
|
+
- spec/ecs_deploy_cli/dsl/service_spec.rb
|
179
226
|
- spec/ecs_deploy_cli/dsl/task_spec.rb
|
180
227
|
- spec/ecs_deploy_cli/runner_spec.rb
|
228
|
+
- spec/ecs_deploy_cli/runners/base_spec.rb
|
181
229
|
- spec/spec_helper.rb
|
182
230
|
- spec/support/ECSFile
|
183
231
|
- spec/support/ECSFile.minimal
|
@@ -188,7 +236,7 @@ licenses:
|
|
188
236
|
- MIT
|
189
237
|
metadata:
|
190
238
|
source_code_uri: https://github.com/monade/ecs-deploy-cli
|
191
|
-
post_install_message:
|
239
|
+
post_install_message:
|
192
240
|
rdoc_options: []
|
193
241
|
require_paths:
|
194
242
|
- lib
|
@@ -203,19 +251,22 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
203
251
|
- !ruby/object:Gem::Version
|
204
252
|
version: '0'
|
205
253
|
requirements: []
|
206
|
-
rubygems_version: 3.2.
|
207
|
-
signing_key:
|
254
|
+
rubygems_version: 3.2.8
|
255
|
+
signing_key:
|
208
256
|
specification_version: 4
|
209
257
|
summary: A command line interface to make ECS deployments easier
|
210
258
|
test_files:
|
259
|
+
- spec/ecs_deploy_cli/cli_spec.rb
|
260
|
+
- spec/ecs_deploy_cli/dsl/cluster_spec.rb
|
261
|
+
- spec/ecs_deploy_cli/dsl/container_spec.rb
|
262
|
+
- spec/ecs_deploy_cli/dsl/cron_spec.rb
|
263
|
+
- spec/ecs_deploy_cli/dsl/parser_spec.rb
|
264
|
+
- spec/ecs_deploy_cli/dsl/service_spec.rb
|
265
|
+
- spec/ecs_deploy_cli/dsl/task_spec.rb
|
266
|
+
- spec/ecs_deploy_cli/runner_spec.rb
|
267
|
+
- spec/ecs_deploy_cli/runners/base_spec.rb
|
211
268
|
- spec/spec_helper.rb
|
212
269
|
- spec/support/ECSFile
|
213
270
|
- spec/support/ECSFile.minimal
|
214
|
-
- spec/support/env_file.yml
|
215
271
|
- spec/support/env_file.ext.yml
|
216
|
-
- spec/
|
217
|
-
- spec/ecs_deploy_cli/cli_spec.rb
|
218
|
-
- spec/ecs_deploy_cli/dsl/task_spec.rb
|
219
|
-
- spec/ecs_deploy_cli/dsl/parser_spec.rb
|
220
|
-
- spec/ecs_deploy_cli/dsl/cron_spec.rb
|
221
|
-
- spec/ecs_deploy_cli/dsl/container_spec.rb
|
272
|
+
- spec/support/env_file.yml
|