ecs_deploy_cli 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|