ciinabox-ecs 0.1.6
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 +7 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +458 -0
- data/Rakefile +649 -0
- data/bin/Rakefile +1 -0
- data/bin/ciinabox-ecs +2 -0
- data/bin/ciinabox-ecs.rb +60 -0
- data/config/ciinabox_params.yml.erb +71 -0
- data/config/default_lambdas.yml +26 -0
- data/config/default_params.yml +303 -0
- data/config/default_params.yml.example +124 -0
- data/config/default_services.yml +62 -0
- data/ext/common_helper.rb +21 -0
- data/ext/config/managed_policies.yml +156 -0
- data/ext/helper.rb +29 -0
- data/ext/policies.rb +53 -0
- data/ext/zip_helper.rb +57 -0
- data/lambdas/acm_issuer_validator/lib/install.sh +20 -0
- data/templates/bastion.rb +121 -0
- data/templates/ciinabox.rb +159 -0
- data/templates/ecs-cluster.rb +252 -0
- data/templates/ecs-services.rb +340 -0
- data/templates/lambdas.rb +172 -0
- data/templates/services/bitbucket.rb +81 -0
- data/templates/services/drone.rb +394 -0
- data/templates/services/hawtio.rb +100 -0
- data/templates/services/icinga2.rb +79 -0
- data/templates/services/jenkins.rb +209 -0
- data/templates/services/nexus.rb +96 -0
- data/templates/vpc.rb +290 -0
- metadata +144 -0
data/Rakefile
ADDED
@@ -0,0 +1,649 @@
|
|
1
|
+
require 'cfndsl/rake_task'
|
2
|
+
require 'rake'
|
3
|
+
require 'yaml'
|
4
|
+
require 'erb'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'pathname'
|
7
|
+
require 'net/http'
|
8
|
+
require 'securerandom'
|
9
|
+
require 'base64'
|
10
|
+
require 'tempfile'
|
11
|
+
require 'json'
|
12
|
+
require_relative './ext/common_helper'
|
13
|
+
require_relative './ext/zip_helper'
|
14
|
+
namespace :ciinabox do
|
15
|
+
|
16
|
+
#load config
|
17
|
+
current_dir = File.expand_path File.dirname(__FILE__)
|
18
|
+
|
19
|
+
templates = Dir["#{current_dir}/templates/**/*.rb"]
|
20
|
+
ciinaboxes_dir = ENV['CIINABOXES_DIR'] || 'ciinaboxes'
|
21
|
+
ciinabox_name = ENV['CIINABOX'] || ''
|
22
|
+
|
23
|
+
@ciinaboxes_dir = ciinaboxes_dir
|
24
|
+
@ciinabox_name = ciinabox_name
|
25
|
+
|
26
|
+
#Load and merge standard ciinabox-provided parameters
|
27
|
+
default_params = YAML.load(File.read("#{current_dir}/config/default_params.yml")) if File.exist?("#{current_dir}/config/default_params.yml")
|
28
|
+
lambda_params = YAML.load(File.read("#{current_dir}/config/default_lambdas.yml"))
|
29
|
+
default_params.merge!(lambda_params)
|
30
|
+
|
31
|
+
if File.exist?("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml")
|
32
|
+
user_params = YAML.load(File.read("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml"))
|
33
|
+
config = default_params.merge(user_params)
|
34
|
+
else
|
35
|
+
user_params = {}
|
36
|
+
config = default_params
|
37
|
+
end
|
38
|
+
|
39
|
+
Dir["#{ciinaboxes_dir}/#{ciinabox_name}/config/*.yml"].each { |config_file|
|
40
|
+
if not config_file.include?('params.yml')
|
41
|
+
config = config.merge(YAML.load(File.read(config_file)))
|
42
|
+
end
|
43
|
+
}
|
44
|
+
config['lambdas'] = {} unless config.key? 'lambdas'
|
45
|
+
config['lambdas'].extend(config['default_lambdas'])
|
46
|
+
|
47
|
+
File.write('debug-ciinabox.config.yaml',config.to_yaml) if ENV['DEBUG']
|
48
|
+
|
49
|
+
stack_name = config["stack_name"] || "ciinabox"
|
50
|
+
|
51
|
+
#if {ciinaboxes_dir}/#{ciinabox_name}/templates
|
52
|
+
#render and add to templates
|
53
|
+
|
54
|
+
if File.exist?("#{ciinaboxes_dir}/#{ciinabox_name}/templates")
|
55
|
+
templates2 = Dir["#{ciinaboxes_dir}/#{ciinabox_name}/templates/**/*.rb"]
|
56
|
+
|
57
|
+
## we want to exclude overridden templates
|
58
|
+
templatesLocalFileNames = templates2.collect { |templateFile| File.basename(templateFile) }
|
59
|
+
templates = templates.select { |templateFile| not templatesLocalFileNames.include? File.basename(templateFile) }
|
60
|
+
templates = templates + templates2
|
61
|
+
end
|
62
|
+
|
63
|
+
files = []
|
64
|
+
templates.each do |template|
|
65
|
+
filename = "#{template}"
|
66
|
+
output = template.sub! /.*templates\//, ''
|
67
|
+
output = output.sub! '.rb', '.json'
|
68
|
+
files << { filename: filename, output: "output/#{output}" }
|
69
|
+
end
|
70
|
+
|
71
|
+
# Generate cloudformation templates, includes packaging of lambda functions
|
72
|
+
desc("Generate CloudFormation templates")
|
73
|
+
task :generate => ['ciinabox:package_lambdas'] do
|
74
|
+
check_active_ciinabox(config)
|
75
|
+
FileUtils.mkdir_p 'output/services'
|
76
|
+
|
77
|
+
# Write config generated by lambda package to tmp file, and pass to templates
|
78
|
+
tmp_file = write_config_tmp_file(config)
|
79
|
+
|
80
|
+
CfnDsl::RakeTask.new do |t|
|
81
|
+
extras = [[:yaml, "#{current_dir}/config/default_params.yml"]]
|
82
|
+
extras << [:yaml, "#{current_dir}/config/default_lambdas.yml"]
|
83
|
+
if File.exist? "#{ciinaboxes_dir}/ciinabox_config.yml"
|
84
|
+
extras << [:yaml, "#{ciinaboxes_dir}/ciinabox_config.yml"]
|
85
|
+
end
|
86
|
+
(Dir["#{ciinaboxes_dir}/#{ciinabox_name}/config/*.yml"].map { |f| [:yaml, f] }).each { |c| extras<<c }
|
87
|
+
extras << [:ruby, "#{current_dir}/ext/helper.rb"]
|
88
|
+
extras << [:yaml, tmp_file.path]
|
89
|
+
t.cfndsl_opts = {
|
90
|
+
verbose: true,
|
91
|
+
files: files,
|
92
|
+
extras: extras
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
Rake::Task['generate'].invoke
|
97
|
+
end
|
98
|
+
|
99
|
+
# Header output
|
100
|
+
def log_header(header)
|
101
|
+
puts "\n\n ========== #{header} ========== \n [#{Time.now}]\n\n\n"
|
102
|
+
end
|
103
|
+
|
104
|
+
desc('Initialise a new ciinabox environment')
|
105
|
+
task :init do |t, args|
|
106
|
+
|
107
|
+
autogenerated_bucket_name = "ciinabox-deployment-#{SecureRandom.uuid}"
|
108
|
+
|
109
|
+
ciinabox_name = get_input("Enter the name of your ciinabox:")
|
110
|
+
@ciinabox_name = ciinabox_name
|
111
|
+
ENV['CIINABOX'] = ciinabox_name
|
112
|
+
|
113
|
+
ciinabox_region = get_input("Enter the AWS region to create your ciinabox [us-east-1]:")
|
114
|
+
puts 'Using us-east-1 as AWS region' if ciinabox_region.strip == ''
|
115
|
+
ciinabox_region = 'us-east-1' if ciinabox_region.strip == ''
|
116
|
+
|
117
|
+
ciinabox_source_bucket = get_input("Enter the name of the S3 bucket to deploy ciinabox to [#{autogenerated_bucket_name}]:")
|
118
|
+
ciinabox_source_bucket = autogenerated_bucket_name if ciinabox_source_bucket.strip == ''
|
119
|
+
|
120
|
+
ciinabox_tools_domain = get_input("Enter top level domain (e.g tools.example.com), must exist in Route53 in the same AWS account:")
|
121
|
+
ciinabox_aws_profile = get_input("Enter AWS profile you wish to use for provisioning (empty for default):")
|
122
|
+
|
123
|
+
profile_switch = ciinabox_aws_profile != '' ? "--profile #{ciinabox_aws_profile}" : ''
|
124
|
+
ciinabox_aws_account = `aws sts get-caller-identity --region #{ciinabox_region} #{profile_switch} --output text --query Account`.sub('\n', '').strip
|
125
|
+
|
126
|
+
puts "Using AWS Account #{ciinabox_aws_account}"
|
127
|
+
|
128
|
+
stack_name = get_input("Enter the name of created Cloud Formation stack [ciinabox]:")
|
129
|
+
stack_name = 'ciinabox' if (stack_name.strip == '')
|
130
|
+
|
131
|
+
include_dood_slave = yesno("Include docker-outside-of-docker slave", false)
|
132
|
+
include_dind_slave = yesno("Include docker-in-docker slave", true)
|
133
|
+
self_signed = yesno("Use selfsigned rather than ACM issued and validated certificate", false)
|
134
|
+
acm_auto_issue_validate = !self_signed
|
135
|
+
use_iam_role = yesno("Use existing role for CIINABOX cluster", true)
|
136
|
+
if use_iam_role then
|
137
|
+
ciinabox_iam_role_name = get_input('Enter name of iam role to use with CIINABOX cluster [ciinabox]:')
|
138
|
+
ciinabox_iam_role_name = 'ciinabox' if ciinabox_iam_role_name.strip == ''
|
139
|
+
end
|
140
|
+
|
141
|
+
ciinabox_docker_repo = get_input('Enter name of private docker repository for images [empty for public images]:')
|
142
|
+
|
143
|
+
if ciinabox_name == ''
|
144
|
+
puts 'You must enter a name for your ciinabox'
|
145
|
+
exit 1
|
146
|
+
end
|
147
|
+
|
148
|
+
my_public_ip = get_my_public_ip_address + "/32"
|
149
|
+
create_dirs ciinaboxes_dir, ciinabox_name
|
150
|
+
|
151
|
+
#Settings preference - 1) User-input 2) User-provided params.yml 3) Default template
|
152
|
+
|
153
|
+
ciinabox_params = File.read("#{current_dir}/config/ciinabox_params.yml.erb")
|
154
|
+
input_result = ERB.new(ciinabox_params).result(binding)
|
155
|
+
input_hash = YAML.load(input_result) #Converts user input to hash
|
156
|
+
if File.exist?("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml")
|
157
|
+
config_output = user_params.merge(input_hash) #Merges input hash into user-provided template
|
158
|
+
config_yaml = config_output.to_yaml #Convert output to YAML for writing
|
159
|
+
File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml", 'w') { |f| f.write(config_yaml) }
|
160
|
+
else
|
161
|
+
File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml", 'w') { |f| f.write(input_result) }
|
162
|
+
end
|
163
|
+
|
164
|
+
default_services = YAML.load(File.read("#{current_dir}/config/default_services.yml"))
|
165
|
+
|
166
|
+
class ::Hash
|
167
|
+
def deep_merge(second)
|
168
|
+
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : Array === v1 && Array === v2 ? v1 | v2 : [:undefined, nil, :nil].include?(v2) ? v1 : v2 }
|
169
|
+
self.merge(second.to_h, &merger)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
if File.exist?("#{ciinaboxes_dir}/#{ciinabox_name}/config/services.yml")
|
174
|
+
puts "Using user-provided services.yml File"
|
175
|
+
user_services = YAML.load(File.read("#{ciinaboxes_dir}/#{ciinabox_name}/config/services.yml"))
|
176
|
+
combined_services = default_services.deep_merge(user_services)
|
177
|
+
yml_combined_services = combined_services.to_yaml
|
178
|
+
File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/services.yml", 'w') { |f| f.write(yml_combined_services) }
|
179
|
+
else
|
180
|
+
yml_default_services = default_services.to_yaml
|
181
|
+
File.open("#{ciinaboxes_dir}/#{ciinabox_name}/config/services.yml", 'w') { |f| f.write(yml_default_services) }
|
182
|
+
end
|
183
|
+
|
184
|
+
display_active_ciinabox ciinaboxes_dir, ciinabox_name
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
desc('Current status of the active ciinabox')
|
189
|
+
task :status do
|
190
|
+
check_active_ciinabox(config)
|
191
|
+
status, result = aws_execute(config, ['cloudformation', 'describe-stacks', "--stack-name #{stack_name}", '--query "Stacks[0].StackStatus"', '--out text'])
|
192
|
+
if status > 0
|
193
|
+
puts "fail to get status for #{config['ciinabox_name']}...has it been created?"
|
194
|
+
exit 1
|
195
|
+
end
|
196
|
+
output = result.chop!
|
197
|
+
if output == 'CREATE_COMPLETE' || output == 'UPDATE_COMPLETE'
|
198
|
+
puts "#{config['ciinabox_name']} ciinabox is alive!!!!"
|
199
|
+
display_ecs_ip_address config
|
200
|
+
else
|
201
|
+
puts "#{config['ciinabox_name']} ciinabox is in state: #{output}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
desc('Creates the source bucket for deploying ciinabox')
|
206
|
+
task :create_source_bucket do
|
207
|
+
check_active_ciinabox(config)
|
208
|
+
status, result = aws_execute(config, ['s3', 'ls', "s3://#{config['source_bucket']}/ciinabox/#{config['ciinabox_version']}/"])
|
209
|
+
if status > 0
|
210
|
+
status, result = aws_execute(config, ['s3', 'mb', "s3://#{config['source_bucket']}"])
|
211
|
+
puts result
|
212
|
+
if status > 0
|
213
|
+
puts "fail to create source bucket see error logs for details"
|
214
|
+
exit status
|
215
|
+
else
|
216
|
+
puts "Successfully created S3 source deployment bucket #{config['source_bucket']}"
|
217
|
+
end
|
218
|
+
else
|
219
|
+
puts "Source deployment bucket #{config['source_bucket']} already exists"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
desc('Create self-signed SSL certs for use with ciinabox')
|
224
|
+
task :create_server_cert do
|
225
|
+
check_active_ciinabox(config)
|
226
|
+
ciinabox_name = config['ciinabox_name']
|
227
|
+
dns_domain = config['dns_domain']
|
228
|
+
script = "
|
229
|
+
openssl req -nodes -new -x509 -newkey rsa:4096 -days 3650 \
|
230
|
+
-keyout #{ciinaboxes_dir}/#{ciinabox_name}/ssl/ciinabox.key \
|
231
|
+
-out #{ciinaboxes_dir}/#{ciinabox_name}/ssl/ciinabox.crt \
|
232
|
+
-subj '/C=AU/ST=Melbourne/L=Melbourne/O=#{ciinabox_name}/OU=ciinabox/CN=*.#{dns_domain}'
|
233
|
+
"
|
234
|
+
result = `#{script}`
|
235
|
+
puts result
|
236
|
+
end
|
237
|
+
|
238
|
+
desc('Uploads SSL server certs for ciinabox')
|
239
|
+
task :upload_server_cert do
|
240
|
+
check_active_ciinabox(config)
|
241
|
+
|
242
|
+
check_active_ciinabox(config)
|
243
|
+
ciinabox_name = config['ciinabox_name']
|
244
|
+
cert_dir = "#{ciinaboxes_dir}/#{ciinabox_name}"
|
245
|
+
status, result = aws_execute( config, [
|
246
|
+
'iam', 'upload-server-certificate',
|
247
|
+
'--server-certificate-name ciinabox',
|
248
|
+
"--certificate-body file://#{cert_dir}/ssl/ciinabox.crt",
|
249
|
+
"--private-key file://#{cert_dir}/ssl/ciinabox.key",
|
250
|
+
"--certificate-chain file://#{cert_dir}/ssl/ciinabox.crt"
|
251
|
+
])
|
252
|
+
if status > 0
|
253
|
+
puts "fail to create or update IAM server-certificates. See error logs for details"
|
254
|
+
puts result
|
255
|
+
exit status
|
256
|
+
end
|
257
|
+
|
258
|
+
certificate_arn = JSON.parse(result)['CertificateArn']
|
259
|
+
puts "Successfully uploaded ACM certificate #{certificate_arn}."
|
260
|
+
# remove_update_ciinabox_config_setting('default_ssl_cert_id', certificate_arn)
|
261
|
+
# puts "Ciinabox #{ciinabox_name} config file updated with new cert"
|
262
|
+
end
|
263
|
+
|
264
|
+
desc('Generate ciinabox AWS keypair')
|
265
|
+
task :generate_keypair do
|
266
|
+
check_active_ciinabox(config)
|
267
|
+
ciinabox_name = config['ciinabox_name']
|
268
|
+
keypair_dir = "#{ciinaboxes_dir}/#{ciinabox_name}/ssl"
|
269
|
+
if File.exists?("#{keypair_dir}/ciinabox.pem")
|
270
|
+
puts "keypair for ciinabox #{ciinabox_name} already exists...please delete if you wish to re-create it"
|
271
|
+
exit 1
|
272
|
+
end
|
273
|
+
status, result = aws_execute(config, ['ec2', 'create-key-pair',
|
274
|
+
"--key-name ciinabox",
|
275
|
+
"--query 'KeyMaterial'",
|
276
|
+
"--out text"
|
277
|
+
], "#{keypair_dir}/ciinabox.pem")
|
278
|
+
puts result
|
279
|
+
if status > 0
|
280
|
+
puts "fail to create keypair see error logs for details"
|
281
|
+
exit status
|
282
|
+
else
|
283
|
+
result = `chmod 0600 #{keypair_dir}/ciinabox.pem`
|
284
|
+
puts "Successfully created ciinabox ssh keypair"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
desc('Deploy Cloudformation templates to S3')
|
289
|
+
task :deploy do
|
290
|
+
check_active_ciinabox(config)
|
291
|
+
status, result = aws_execute(config, ['s3', 'sync', 'output/', "s3://#{config['source_bucket']}/ciinabox/#{config['ciinabox_version']}/"])
|
292
|
+
puts result
|
293
|
+
if status > 0
|
294
|
+
puts "fail to upload rendered templates to S3 bucket #{config['source_bucket']}"
|
295
|
+
exit status
|
296
|
+
else
|
297
|
+
puts "Successfully uploaded rendered templates to S3 bucket #{config['source_bucket']}"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
desc('Creates the ciinabox environment')
|
302
|
+
task :create do
|
303
|
+
check_active_ciinabox(config)
|
304
|
+
status, result = aws_execute(config, ['cloudformation', 'create-stack',
|
305
|
+
"--stack-name #{stack_name}",
|
306
|
+
"--template-url https://#{config['source_bucket']}.s3.amazonaws.com/ciinabox/#{config['ciinabox_version']}/ciinabox.json",
|
307
|
+
'--capabilities CAPABILITY_IAM'
|
308
|
+
])
|
309
|
+
puts result
|
310
|
+
if status > 0
|
311
|
+
puts "Failed to create ciinabox environment"
|
312
|
+
exit status
|
313
|
+
else
|
314
|
+
puts "Starting creation of ciinabox environment"
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
desc('Updates the ciinabox environment')
|
319
|
+
task :update do
|
320
|
+
check_active_ciinabox(config)
|
321
|
+
status, result = aws_execute(config, ['cloudformation', 'update-stack',
|
322
|
+
"--stack-name #{stack_name}",
|
323
|
+
"--template-url https://#{config['source_bucket']}.s3.amazonaws.com/ciinabox/#{config['ciinabox_version']}/ciinabox.json",
|
324
|
+
'--capabilities CAPABILITY_IAM'
|
325
|
+
])
|
326
|
+
puts result
|
327
|
+
if status > 0
|
328
|
+
puts "Failed to update ciinabox environment"
|
329
|
+
exit status
|
330
|
+
else
|
331
|
+
puts "Starting updating of ciinabox environment"
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
desc('Turn off your ciinabox environment')
|
336
|
+
task :down do
|
337
|
+
# Use cfn_manage gem for this
|
338
|
+
command = 'stop'
|
339
|
+
start_stop_env(command, config)
|
340
|
+
end
|
341
|
+
|
342
|
+
desc('Turn on your ciinabox environment')
|
343
|
+
task :up do
|
344
|
+
# Use cfn_manage gem for this
|
345
|
+
command = 'start'
|
346
|
+
start_stop_env(command, config)
|
347
|
+
end
|
348
|
+
|
349
|
+
desc('Deletes/tears down the ciinabox environment')
|
350
|
+
task :tear_down do
|
351
|
+
check_active_ciinabox(config)
|
352
|
+
STDOUT.puts "Are you sure you want to tear down the #{config['ciinabox_name']} ciinabox? (y/n)"
|
353
|
+
input = STDIN.gets.strip
|
354
|
+
if input == 'y'
|
355
|
+
status, result = aws_execute(config, ['cloudformation', 'delete-stack', "--stack-name #{stack_name}"])
|
356
|
+
puts result
|
357
|
+
if status > 0
|
358
|
+
puts "fail to tear down ciinabox environment"
|
359
|
+
exit status
|
360
|
+
else
|
361
|
+
puts "Starting tear down of ciinabox environment"
|
362
|
+
end
|
363
|
+
else
|
364
|
+
puts "good choice...keep enjoying your ciinabox"
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
desc('SSH into your ciinabox environment')
|
369
|
+
task :ssh do
|
370
|
+
keypair = "#{ciinaboxes_dir}/#{ciinabox_name}/ssl/ciinabox.pem"
|
371
|
+
`ssh-add #{ciinaboxes_dir}/#{ciinabox_name}/ssl/ciinabox.pem`
|
372
|
+
puts "# execute the following:"
|
373
|
+
puts "ssh -A ec2-user@nata.#{config['dns_domain']} -i #{keypair}"
|
374
|
+
puts "# and then"
|
375
|
+
puts "ssh #{get_ecs_ip_address(config)}"
|
376
|
+
end
|
377
|
+
|
378
|
+
desc('Package Lambda Functions as ZipFiles')
|
379
|
+
task :package_lambdas do
|
380
|
+
check_active_ciinabox(config)
|
381
|
+
if !config['lambdas'].nil? && !config['lambdas']['functions'].nil?
|
382
|
+
log_header 'Package lambda functions'
|
383
|
+
|
384
|
+
# Clear previous packages
|
385
|
+
|
386
|
+
FileUtils.rmtree 'output/lambdas'
|
387
|
+
|
388
|
+
# Cached downloads map
|
389
|
+
cached_downloads = {}
|
390
|
+
config['lambdas']['functions'].each do |name, lambda_config|
|
391
|
+
timestamp = Time.now.getutc.to_i
|
392
|
+
# create folder
|
393
|
+
|
394
|
+
config_file_folder = "output/lambdas/#{name}/#{timestamp}"
|
395
|
+
FileUtils.mkdir_p config_file_folder
|
396
|
+
|
397
|
+
# download file if code remote archive
|
398
|
+
puts "Processing function #{name}...\n"
|
399
|
+
|
400
|
+
if lambda_config['local']
|
401
|
+
lambda_source_path = "#{current_dir}/#{lambda_config['code']}" if lambda_config['local']
|
402
|
+
lambda_source_file = File.basename(lambda_source_path)
|
403
|
+
tmpdir = "output/package_lambdas/#{name}"
|
404
|
+
FileUtils.mkdir_p tmpdir
|
405
|
+
FileUtils.cp_r(lambda_source_path, tmpdir)
|
406
|
+
lambda_source_path = "#{tmpdir}/#{lambda_source_file}"
|
407
|
+
else
|
408
|
+
lambda_source_path = "#{ciinaboxes_dir}/#{ciinabox_name}/#{lambda_config['code']}"
|
409
|
+
end
|
410
|
+
|
411
|
+
lambda_source_dir = File.dirname(lambda_source_path)
|
412
|
+
|
413
|
+
lambda_source_file = File.basename(lambda_source_path)
|
414
|
+
lambda_source_file = '.' if Pathname.new(lambda_source_path).directory?
|
415
|
+
|
416
|
+
lambda_source_dir = lambda_source_path if Pathname.new(lambda_source_path).directory?
|
417
|
+
puts "Lambda source path: #{lambda_source_path}"
|
418
|
+
puts "Lambda source dir: #{lambda_source_dir}"
|
419
|
+
|
420
|
+
unless lambda_config['package_cmd'].nil?
|
421
|
+
package_cmd = "cd #{lambda_source_dir} && #{lambda_config['package_cmd']}"
|
422
|
+
puts 'Processing package command...'
|
423
|
+
package_result = system(package_cmd)
|
424
|
+
unless package_result
|
425
|
+
puts "Error packaging lambda function, following command failed\n\n#{package_cmd}\n\n"
|
426
|
+
exit -4
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
if lambda_config['code'].include? 'http'
|
431
|
+
if cached_downloads.key? lambda_config['code']
|
432
|
+
puts "Using already downloaded archive #{lambda_config['code']}"
|
433
|
+
FileUtils.copy(cached_downloads[lambda_config['code']], "#{config_file_folder}/src.zip")
|
434
|
+
else
|
435
|
+
puts "Downloading file #{lambda_config['code']} ..."
|
436
|
+
File.write("#{config_file_folder}/src.zip", Net::HTTP.get(URI.parse(lambda_config['code'])))
|
437
|
+
puts 'Download complete'
|
438
|
+
cached_downloads[lambda_config['code']] = "#{config_file_folder}/src.zip"
|
439
|
+
end
|
440
|
+
else
|
441
|
+
|
442
|
+
zip_generator = Ciinabox::Util::ZipFileGenerator.new(lambda_source_dir,
|
443
|
+
"#{config_file_folder}/src.zip")
|
444
|
+
|
445
|
+
zip_generator.write
|
446
|
+
|
447
|
+
end
|
448
|
+
|
449
|
+
sha256 = Digest::SHA256.file "#{config_file_folder}/src.zip"
|
450
|
+
sha256 = sha256.base64digest
|
451
|
+
puts "Created zip package #{config_file_folder}/src.zip for lambda #{name} with digest #{sha256}"
|
452
|
+
lambda_config['code_sha256'] = sha256
|
453
|
+
lambda_config['timestamp'] = timestamp
|
454
|
+
end
|
455
|
+
|
456
|
+
FileUtils.rmtree 'output/package_lambdas'
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
desc('Initialize configuration, create required assets in AWS account, create Cloud Formation stack')
|
461
|
+
task :full_install do
|
462
|
+
|
463
|
+
Rake::Task['ciinabox:init'].invoke
|
464
|
+
|
465
|
+
# Reload config
|
466
|
+
user_params = YAML.load(File.read("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml"))
|
467
|
+
config = default_params.merge(user_params)
|
468
|
+
|
469
|
+
if (yesno('Create source bucket', true))
|
470
|
+
Rake::Task['ciinabox:create_source_bucket'].invoke
|
471
|
+
end
|
472
|
+
|
473
|
+
if (yesno("Create and upload server certificate?\n(chose yes if using local hosts file for DNS to tools)", false))
|
474
|
+
Rake::Task['ciinabox:create_server_cert'].invoke
|
475
|
+
Rake::Task['ciinabox:upload_server_cert'].invoke
|
476
|
+
user_params = YAML.load(File.read("#{ciinaboxes_dir}/#{ciinabox_name}/config/params.yml"))
|
477
|
+
end
|
478
|
+
|
479
|
+
# Create ciinabox keypair
|
480
|
+
if (yesno('Create and upload ciinabox key', true))
|
481
|
+
Rake::Task['ciinabox:generate_keypair'].invoke
|
482
|
+
end
|
483
|
+
|
484
|
+
# Generate CF
|
485
|
+
Rake::Task['ciinabox:generate'].invoke
|
486
|
+
|
487
|
+
# Deploy CF
|
488
|
+
Rake::Task['ciinabox:deploy'].invoke
|
489
|
+
|
490
|
+
# Create stack
|
491
|
+
Rake::Task['ciinabox:create'].invoke
|
492
|
+
|
493
|
+
puts "Waiting for Cloud Formation stack creation completion ..."
|
494
|
+
aws_execute(config, ["cloudformation wait stack-create-complete --stack-name #{stack_name}"])
|
495
|
+
|
496
|
+
end
|
497
|
+
|
498
|
+
desc('Replace previously auto-generated IAM certificate with auto-validated ACM certificate (if one exists)')
|
499
|
+
task :update_cert_to_acm do
|
500
|
+
status, result = aws_execute(config, [
|
501
|
+
'cloudformation',
|
502
|
+
'describe-stacks',
|
503
|
+
"--stack-name #{stack_name}",
|
504
|
+
'--out json'
|
505
|
+
])
|
506
|
+
resp = JSON.parse(result)
|
507
|
+
cert_output = resp['Stacks'][0]['Outputs'].find { |k| k['OutputKey'] == 'DefaultSSLCertificate' }
|
508
|
+
if cert_output.nil?
|
509
|
+
STDERR.puts("ACM certificate is not present in stack outputs")
|
510
|
+
exit -1
|
511
|
+
end
|
512
|
+
cert_arn = cert_output['OutputValue']
|
513
|
+
|
514
|
+
# as we don't want to remove any comments
|
515
|
+
remove_update_ciinabox_config_setting('default_ssl_cert_id', cert_arn)
|
516
|
+
puts "Set #{cert_arn} as default_cert_arn"
|
517
|
+
end
|
518
|
+
|
519
|
+
def add_ciinabox_config_setting(element, value)
|
520
|
+
file_name = "#{@ciinaboxes_dir}/#{@ciinabox_name}/config/params.yml"
|
521
|
+
File.write(file_name,
|
522
|
+
"\n" + "#{element}: #{value}",
|
523
|
+
File.size(file_name),
|
524
|
+
mode: 'a'
|
525
|
+
)
|
526
|
+
end
|
527
|
+
|
528
|
+
def remove_update_ciinabox_config_setting(element, new_value='')
|
529
|
+
f = File.new("#{@ciinaboxes_dir}/#{@ciinabox_name}/config/params.yml", 'r+')
|
530
|
+
found = false
|
531
|
+
f.each do |line|
|
532
|
+
if line.include?(element)
|
533
|
+
# seek back to the beginning of the line.
|
534
|
+
f.seek(-line.length, IO::SEEK_CUR)
|
535
|
+
|
536
|
+
# overwrite line with spaces and add a newline char
|
537
|
+
f.write('') if new_value.empty?
|
538
|
+
f.write("#{element}: #{new_value}") unless new_value.empty?
|
539
|
+
f.write("\n")
|
540
|
+
found = true
|
541
|
+
end
|
542
|
+
end
|
543
|
+
f.close
|
544
|
+
add_ciinabox_config_setting(element, new_value) if((not found) and (not new_value.empty?))
|
545
|
+
end
|
546
|
+
|
547
|
+
def check_active_ciinabox(config)
|
548
|
+
if (config.nil? || config['ciinabox_name'].nil?)
|
549
|
+
puts "no active ciinabox - either export CIINABOX variable or set ciinabox name as last command line argument"
|
550
|
+
exit 1
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
def aws_execute(config, cmd, output = nil)
|
555
|
+
config['aws_profile'].nil? ? '' : cmd << "--profile #{config['aws_profile']}"
|
556
|
+
config['aws_region'].nil? ? '' : cmd << "--region #{config['aws_region']}"
|
557
|
+
args = cmd.join(" ")
|
558
|
+
if config['log_level'] == :debug
|
559
|
+
puts "executing: aws #{args}"
|
560
|
+
end
|
561
|
+
if output.nil?
|
562
|
+
result = `aws #{args} 2>&1`
|
563
|
+
else
|
564
|
+
result = `aws #{args} > #{output}`
|
565
|
+
end
|
566
|
+
return $?.to_i, result
|
567
|
+
end
|
568
|
+
|
569
|
+
def display_active_ciinabox(ciinaboxes_dir, ciinabox)
|
570
|
+
puts "# Enable active ciinabox by executing or override ciinaboxes base directory:"
|
571
|
+
puts "export CIINABOXES_DIR=\"#{ciinaboxes_dir}\""
|
572
|
+
puts "export CIINABOX=\"#{ciinabox}\""
|
573
|
+
end
|
574
|
+
|
575
|
+
def display_ecs_ip_address(config)
|
576
|
+
ip_address = get_ecs_ip_address(config)
|
577
|
+
if ip_address.nil?
|
578
|
+
puts "Unable to get ECS cluster private ip"
|
579
|
+
else
|
580
|
+
puts "ECS cluster private ip:#{ip_address}"
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
def get_ecs_ip_address(config)
|
585
|
+
status, result = aws_execute(config, [
|
586
|
+
'ec2',
|
587
|
+
'describe-instances',
|
588
|
+
'--query Reservations[*].Instances[?Tags[?Value==\`ciinabox-ecs\`]].PrivateIpAddress',
|
589
|
+
'--out text'
|
590
|
+
])
|
591
|
+
if status > 0
|
592
|
+
return nil
|
593
|
+
else
|
594
|
+
return result
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
def yesno(question, default)
|
599
|
+
question = ("#{question} (y/n)? [#{default ? 'y' : 'n'}]")
|
600
|
+
while true
|
601
|
+
case get_input(question)
|
602
|
+
when ''
|
603
|
+
return default
|
604
|
+
when 'Y', 'y', 'yes'
|
605
|
+
return true
|
606
|
+
when /\A[nN]o?\Z/ #n or no
|
607
|
+
return false
|
608
|
+
end
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
def get_input(prompt)
|
613
|
+
puts prompt
|
614
|
+
$stdin.gets.chomp
|
615
|
+
end
|
616
|
+
|
617
|
+
def create_dirs(dir, name)
|
618
|
+
config_dirname = File.dirname("#{dir}/#{name}/config/ignore.txt")
|
619
|
+
unless File.directory?(config_dirname)
|
620
|
+
FileUtils.mkdir_p(config_dirname)
|
621
|
+
end
|
622
|
+
ssl_dirname = File.dirname("#{dir}/#{name}/ssl/ignore.txt")
|
623
|
+
unless File.directory?(ssl_dirname)
|
624
|
+
FileUtils.mkdir_p(ssl_dirname)
|
625
|
+
end
|
626
|
+
config_dirname
|
627
|
+
end
|
628
|
+
|
629
|
+
def get_my_public_ip_address
|
630
|
+
Net::HTTP.get(URI("http://api.ipify.org"))
|
631
|
+
end
|
632
|
+
|
633
|
+
def write_config_tmp_file(config)
|
634
|
+
#write config to tmp file
|
635
|
+
tmp_file = Tempfile.new(%w(config_obj .yml))
|
636
|
+
tmp_file << { config: config }.to_yaml
|
637
|
+
tmp_file.rewind
|
638
|
+
return tmp_file
|
639
|
+
end
|
640
|
+
|
641
|
+
def start_stop_env(command, config)
|
642
|
+
cmd = "cfn_manage #{command}-environment --stack-name #{config['stack_name']} "
|
643
|
+
cmd += " --source-bucket #{config['source_bucket']}"
|
644
|
+
cmd += " --region #{config['source_region']}"
|
645
|
+
cmd += " --profile #{config['aws_profile']}" if not config['aws_profile'].nil?
|
646
|
+
result = system(cmd)
|
647
|
+
exit -1 if not result
|
648
|
+
end
|
649
|
+
end
|