awx 0.5.1 → 0.6.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/aws/aws_cloudformation.rb +23 -7
- data/lib/aws/aws_outputter.rb +15 -11
- data/lib/aws/aws_profile.rb +13 -9
- data/lib/aws/aws_reports.rb +18 -16
- data/lib/awx.rb +76 -30
- data/lib/core/deployments.rb +65 -0
- data/lib/core/replacer.rb +282 -0
- data/lib/routes/{aws_cloudformation_create.rb → cloudformation_create.rb} +221 -116
- data/lib/routes/{aws_cloudformation_delete.rb → cloudformation_delete.rb} +1 -1
- data/lib/routes/{aws_cloudformation_detect_drift.rb → cloudformation_detect_drift.rb} +1 -1
- data/lib/routes/{aws_deploy.rb → deploy_deprecated.rb} +11 -8
- data/lib/routes/{aws_dynamo_db.rb → dynamo_db.rb} +1 -1
- data/lib/routes/infrastructure.rb +45 -0
- data/lib/routes/{aws_list.rb → list.rb} +20 -5
- data/lib/routes/ssh.rb +70 -0
- data/lib/routes/{aws_switch.rb → switch.rb} +1 -1
- data/lib/routes/upload.rb +49 -0
- data/lib/version.rb +1 -1
- data/opt/awx/deployment-schema.yml +57 -0
- data/opt/awx/reports.yml +105 -37
- data/opt/config/schema.yml +22 -0
- data/opt/config/template.yml +9 -1
- metadata +17 -11
@@ -3,13 +3,13 @@ require 'digest'
|
|
3
3
|
|
4
4
|
module AppCommand
|
5
5
|
|
6
|
-
class
|
6
|
+
class CloudFormationCreate < ::Convoy::ActionCommand::Base
|
7
7
|
|
8
8
|
DEFAULT = 'Default'
|
9
9
|
PARAMETERS = 'Parameters'
|
10
10
|
SPECIAL_METHODS = [:before_create, :after_create, :before_teardown, :after_teardown]
|
11
11
|
AWS_TAGS = %w(Environment Project Region Category Template)
|
12
|
-
RESERVED_WORDS = %w(
|
12
|
+
RESERVED_WORDS = %w(cache_uuid category description environment owner project projectid region resource stack stackname template terminationprotection timeout)
|
13
13
|
OPTION_PROJECT_ID = 'ProjectId'
|
14
14
|
OPTION_DEPLOYMENT_STACK = 'DeploymentStack'
|
15
15
|
OPTION_STACK_NAME = 'StackName'
|
@@ -21,6 +21,7 @@ module AppCommand
|
|
21
21
|
OPTION_CATEGORY = 'Category'
|
22
22
|
OPTION_TEMPLATE = 'Template'
|
23
23
|
OPTION_TERM_PROTECT = 'TerminationProtection'
|
24
|
+
OPTION_OWNER = 'Owner'
|
24
25
|
MATCHERS = %w(CATEGORY TEMPLATE PROJECT ENVIRONMENT REGION UUID)
|
25
26
|
OPTIONS = 'Options'
|
26
27
|
SPECIAL = 'AWS-specific'
|
@@ -29,8 +30,9 @@ module AppCommand
|
|
29
30
|
SPACER = '<<--Spacer-->>'
|
30
31
|
CAPABILITIES = 'Capabilities'
|
31
32
|
RETURN_VALUE = 'PjNkHK33EopWxCpzOQfuku3la'
|
32
|
-
SSH_USERS = 'SSHUsers'
|
33
33
|
SYSTEM = 'System'
|
34
|
+
NESTED = 'nested'
|
35
|
+
STACK = 'Stack'
|
34
36
|
|
35
37
|
def execute
|
36
38
|
|
@@ -55,11 +57,14 @@ module AppCommand
|
|
55
57
|
@cache = {}
|
56
58
|
@cache_valid = false
|
57
59
|
@projects = {}
|
60
|
+
@nested_stacks = {}
|
58
61
|
|
59
62
|
@terminal_width = Blufin::Terminal::get_terminal_width
|
60
63
|
@columns, @data, @export_map, @table_widths = App::AWSReports::parse_metadata(@regions)
|
61
64
|
|
62
|
-
Blufin::
|
65
|
+
@owner = Blufin::Config::get['DeveloperName']
|
66
|
+
|
67
|
+
Blufin::Projects::init(App::AWSProfile::get_profile['Projects'])
|
63
68
|
|
64
69
|
opts_validate
|
65
70
|
opts_routing
|
@@ -88,14 +93,15 @@ module AppCommand
|
|
88
93
|
terminal_required_width = 227
|
89
94
|
Blufin::Terminal::error("Output for this command \x1B[38;5;240m(#{Blufin::Terminal::format_action(terminal_required_width)}\x1B[38;5;240m-width)\x1B[0m does not fit in Terminal \x1B[38;5;240m(#{Blufin::Terminal::format_action(terminal_width_actual)}\x1B[38;5;240m-width)\x1B[0m", 'Please make your terminal wider and try again.', true) if terminal_width_actual < terminal_required_width
|
90
95
|
|
91
|
-
@warnings
|
92
|
-
@lookups
|
96
|
+
@warnings = []
|
97
|
+
@lookups = App::AWSReports::get_lookups(@data)
|
93
98
|
|
94
99
|
# Add SSH Users to Lookups (if they've been configured).
|
95
|
-
users
|
96
|
-
@lookups[SSH_USERS]
|
100
|
+
users = App::AWSProfile::get_ssh_users
|
101
|
+
@lookups[App::Replacer::SSH_USERS] = users if users.any?
|
97
102
|
|
98
|
-
|
103
|
+
# TODO - Environments cannot be hard-coded here. Possibly get from deployments (once we have it parsing)?
|
104
|
+
@options_default[OPTION_ENVIRONMENT] = %w(dev test staging prod)
|
99
105
|
@options_default[OPTION_REGION] = App::AWSProfile::get_profile[App::AWSProfile::CLOUDFORMATION]['Defaults']['Regions']
|
100
106
|
@options_default[OPTION_STACK_NAME] = App::AWSProfile::get_profile[App::AWSProfile::CLOUDFORMATION]['Defaults']['StackName']
|
101
107
|
@options_default[OPTION_TIMEOUT] = App::AWSProfile::get_profile[App::AWSProfile::CLOUDFORMATION]['Defaults']['Timeout']
|
@@ -267,7 +273,7 @@ module AppCommand
|
|
267
273
|
if stack_name.nil? || stack_name.strip == ''
|
268
274
|
stack_name = @options_default[OPTION_STACK_NAME]
|
269
275
|
end
|
270
|
-
results =
|
276
|
+
results = App::Replacer::scan_string(stack_name)
|
271
277
|
validate_matchers(file_cloudformation, results, template_name, parameters)
|
272
278
|
# Make sure deployment stack is not a reserved word.
|
273
279
|
if deployment_stack.is_a?(String) && %w(lambda).include?(deployment_stack.downcase)
|
@@ -330,41 +336,45 @@ module AppCommand
|
|
330
336
|
end
|
331
337
|
# Validate template matchers.
|
332
338
|
unless file_cloudformation.nil?
|
333
|
-
results =
|
339
|
+
results = App::Replacer::scan_file(file_cloudformation)
|
334
340
|
# Handle errors first.
|
335
341
|
validate_matchers(file_cloudformation, results, template_name, parameters)
|
336
342
|
end
|
337
343
|
@warnings << "\x1B[38;5;196m#{template_name}\x1B[38;5;240m \xe2\x80\x94 The #{Blufin::Terminal::format_highlight('template.yml')}\x1B[38;5;240m is missing, empty or invalid." if file_cloudformation.nil?
|
338
|
-
|
339
|
-
|
340
|
-
if @warnings.length > warnings_count
|
341
|
-
@templates[category][template] = {
|
342
|
-
:name => template,
|
343
|
-
:broken => 'Broken'
|
344
|
-
}
|
344
|
+
if category == NESTED
|
345
|
+
@warnings << "\x1B[38;5;196m#{template_name}\x1B[38;5;240m \xe2\x80\x94 #{Blufin::Terminal::format_highlight('template.rb')}\x1B[38;5;240m for nested stacks will be ignored, please remove." unless file_ruby.nil?
|
345
346
|
else
|
346
|
-
@
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
347
|
+
@warnings << "\x1B[38;5;196m#{template_name}\x1B[38;5;240m \xe2\x80\x94 The #{Blufin::Terminal::format_highlight('template.rb')}\x1B[38;5;240m is missing, empty or invalid." if file_ruby.nil?
|
348
|
+
@templates[category] = {} unless @templates.has_key?(category)
|
349
|
+
if @warnings.length > warnings_count
|
350
|
+
@templates[category][template] = {
|
351
|
+
:name => template,
|
352
|
+
:broken => 'Broken'
|
353
|
+
}
|
354
|
+
else
|
355
|
+
@templates[category][template] = {
|
356
|
+
:name => template,
|
357
|
+
:path => template_path,
|
358
|
+
:broken => false,
|
359
|
+
:file_cloudformation => file_cloudformation,
|
360
|
+
:file_ruby => file_ruby,
|
361
|
+
:method_before_create => method_before_create,
|
362
|
+
:method_after_create => method_after_create,
|
363
|
+
:method_before_teardown => method_before_teardown,
|
364
|
+
:method_after_teardown => method_after_teardown,
|
365
|
+
:parameters => parameters,
|
366
|
+
:parameters_no_sort => parameters_no_sort,
|
367
|
+
:intro => intro,
|
368
|
+
:description => description,
|
369
|
+
:stack_name => stack_name,
|
370
|
+
:projects => projects,
|
371
|
+
:environments => environments,
|
372
|
+
:regions => regions,
|
373
|
+
:timeout => timeout,
|
374
|
+
:single_serve => single_serve
|
375
|
+
}
|
376
|
+
@templates[category][template][:deployment_stack] = deployment_stack unless deployment_stack.nil?
|
377
|
+
end
|
368
378
|
end
|
369
379
|
end
|
370
380
|
end
|
@@ -381,12 +391,15 @@ module AppCommand
|
|
381
391
|
|
382
392
|
def opts_routing
|
383
393
|
|
384
|
-
used_cache
|
385
|
-
showing_tags
|
394
|
+
used_cache = true
|
395
|
+
showing_tags = false
|
386
396
|
|
387
397
|
# Show prompt to select template.
|
388
398
|
category, template, @template = select_template_prompt
|
389
399
|
|
400
|
+
# Will be something like: "ec2/build-server"
|
401
|
+
template_name = "#{category}/#{template}"
|
402
|
+
|
390
403
|
if @params.any?
|
391
404
|
# Here we replace the UUID for the stack-name at the last-second to enable parallel, identical runs (if exists).
|
392
405
|
@params[OPTION_STACK_NAME] = replace_stack_suffix(@params[OPTION_STACK_NAME]) if @params.has_key?(OPTION_STACK_NAME)
|
@@ -399,25 +412,28 @@ module AppCommand
|
|
399
412
|
puts "\x1B[38;5;#{App::AWSOutputter::DIVIDER_COLOR}m--- \x1B[38;5;136m[Tags]\x1B[0m\x1B[38;5;#{App::AWSOutputter::DIVIDER_COLOR}m #{'-' * (@terminal_width - 17)}\x1B[0m"
|
400
413
|
puts
|
401
414
|
end
|
415
|
+
|
416
|
+
# TODO NOW - REMOVE:DEPLOYMENT_STACK - This needs to be re-instated once we have the deployments.yml parsing.
|
402
417
|
# Deployment ID needs to be handled here because we need to know what the project/region/environment is before we can display the options.
|
403
|
-
if param_name == OPTION_PROJECT_ID
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
end
|
418
|
+
# if param_name == OPTION_PROJECT_ID
|
419
|
+
# # Figure out which deployments are eligible for this stack.
|
420
|
+
# if @template.has_key?(:deployment_stack)
|
421
|
+
# @projects = Blufin::Projects::get_deployments(
|
422
|
+
# @params[OPTION_PROJECT],
|
423
|
+
# @template[:deployment_stack],
|
424
|
+
# @params[OPTION_REGION],
|
425
|
+
# @params[OPTION_ENVIRONMENT]
|
426
|
+
# )
|
427
|
+
# if @projects.any?
|
428
|
+
# project_options = []
|
429
|
+
# @projects.each { |project| project_options << project[1][Blufin::Projects::PROJECT_ID] }
|
430
|
+
# @params[param_name] = Blufin::Terminal::prompt_select('Select Project ID:', project_options, help: param_data[OPTION_DESCRIPTION])
|
431
|
+
# puts
|
432
|
+
# end
|
433
|
+
# end
|
434
|
+
# next
|
435
|
+
# end
|
436
|
+
|
421
437
|
@params[param_name] = get_parameter_value(param_data, param_name, category, template)
|
422
438
|
# Puts space unless it's a hidden parameter. Keeps spacing consistent.
|
423
439
|
puts unless [OPTION_TIMEOUT].include?(param_name)
|
@@ -445,19 +461,52 @@ module AppCommand
|
|
445
461
|
capabilities_arr = []
|
446
462
|
capabilities_str = nil
|
447
463
|
|
464
|
+
# # TODO - REMOVE OR EMBED? This shows all the params eligible for replacing...
|
465
|
+
# puts get_replacer_params(category, template).to_yaml
|
466
|
+
# exit
|
467
|
+
|
448
468
|
# Replace matchers in CloudFormation Template before uploading to S3.
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
469
|
+
replacer_params = get_replacer_params(category, template)
|
470
|
+
|
471
|
+
if !@nested_stacks[template_name].nil? && @nested_stacks[template_name].any?
|
472
|
+
# First create a temporary filename map for all the stacks we need to upload.
|
473
|
+
tmp_filenames = {}
|
474
|
+
@nested_stacks[template_name].keys.each do |stack|
|
475
|
+
tmp_filenames[stack] = "#{stack.gsub('/', '-')}-#{DateTime.now.strftime('%Y%m%d-%H%M%S')}.yml"
|
476
|
+
end
|
477
|
+
|
478
|
+
replacer_params[STACK] = {}
|
479
|
+
@nested_stacks[template_name].each do |stack|
|
480
|
+
replacer_params[STACK][stack[0]] = {
|
481
|
+
:s3_url => "#{App::AWSCloudFormation::get_s3_bucket_url}/#{tmp_filenames[stack[0]]}"
|
482
|
+
}
|
483
|
+
end
|
484
|
+
|
485
|
+
# Upload Nested Stacks to S3.
|
486
|
+
@nested_stacks[template_name].each do |stack|
|
487
|
+
nested_template_converted = "/tmp/#{tmp_filenames[stack[0]]}"
|
488
|
+
nested_template_file = File.expand_path("#{App::AWSCloudFormation::get_cloudformation_path}/#{stack[0]}/template.yml")
|
489
|
+
file_lines = App::Replacer::replace_yml(nested_template_file, replacer_params)
|
490
|
+
Blufin::Files::write_file(nested_template_converted, file_lines)
|
491
|
+
# Upload nested template to S3.
|
492
|
+
App::AWSCloudFormation::upload_cloudformation_template(nested_template_converted, category, template, filename_override: tmp_filenames[stack[0]])
|
493
|
+
system("rm #{nested_template_converted}")
|
494
|
+
end
|
495
|
+
|
496
|
+
end
|
497
|
+
|
498
|
+
base_template_file = File.expand_path("#{App::AWSCloudFormation::get_cloudformation_path}/#{category}/#{template}/template.yml")
|
499
|
+
raise RuntimeError, "File does not exist: #{base_template_file}" unless Blufin::Files::file_exists(base_template_file)
|
500
|
+
tmp_file = "/tmp/converted-template-#{Blufin::Strings::random_string(4)}.txt"
|
501
|
+
base_template_lines = App::Replacer::replace_yml(base_template_file, replacer_params)
|
502
|
+
Blufin::Files::write_file(tmp_file, base_template_lines)
|
454
503
|
|
455
504
|
# Upload the template to S3.
|
456
|
-
|
505
|
+
base_template = App::AWSCloudFormation::upload_cloudformation_template(tmp_file, category, template)
|
457
506
|
system("rm #{tmp_file}")
|
458
507
|
|
459
|
-
# Validates the template.
|
460
|
-
validation = App::AWSCli::cloudformation_stack_validate(@params[OPTION_REGION],
|
508
|
+
# Validates the base template.
|
509
|
+
validation = App::AWSCli::cloudformation_stack_validate(@params[OPTION_REGION], base_template)
|
461
510
|
|
462
511
|
# Check if validation output is JSON (and output appropriate format).
|
463
512
|
begin
|
@@ -475,14 +524,17 @@ module AppCommand
|
|
475
524
|
Blufin::Terminal::error("AWS says your template is: \x1B[38;5;196mINVALID", App::AWSCli::format_cli_error(validation), true)
|
476
525
|
end
|
477
526
|
|
478
|
-
output
|
479
|
-
output[OPTION_STACK_NAME]
|
480
|
-
output[OPTION_DESCRIPTION]
|
481
|
-
output[SPACER]
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
527
|
+
output = {}
|
528
|
+
output[OPTION_STACK_NAME] = @params[OPTION_STACK_NAME]
|
529
|
+
output[OPTION_DESCRIPTION] = @params[OPTION_DESCRIPTION]
|
530
|
+
output[SPACER] = true
|
531
|
+
|
532
|
+
# TODO NOW - REMOVE:DEPLOYMENT_STACK - This needs to be re-instated once we have the deployments.yml parsing.
|
533
|
+
# if @template.has_key?(:deployment_stack) && !@params[OPTION_PROJECT_ID].nil? && @params[OPTION_PROJECT_ID].length > 0
|
534
|
+
# output[OPTION_DEPLOYMENT_STACK] = @template[:deployment_stack]
|
535
|
+
# output[OPTION_PROJECT_ID] = @params[OPTION_PROJECT_ID]
|
536
|
+
# end
|
537
|
+
|
486
538
|
output[OPTION_TIMEOUT] = @params[OPTION_TIMEOUT]
|
487
539
|
output[OPTION_TERM_PROTECT] = @params[OPTION_TERM_PROTECT] unless @template[:single_serve]
|
488
540
|
output[CAPABILITIES] = capabilities_arr.join(', ') unless capabilities_str.nil?
|
@@ -521,15 +573,24 @@ module AppCommand
|
|
521
573
|
# If no 'after' actions necessary, give option to terminate script.
|
522
574
|
term_script = false
|
523
575
|
if @template[:method_after_create].nil? && !@template[:single_serve]
|
524
|
-
|
576
|
+
options = [{:text => "Yes \xe2\x80\x94 Script will wait until stack is fully built.", :value => false}, {:text => 'No', :value => true}]
|
577
|
+
help_text = "Select 'No' to end the script immediately. Status can still be viewed in the AWS console."
|
578
|
+
term_script = Blufin::Terminal::prompt_select('Wait for stack to build?', options, help: help_text)
|
525
579
|
puts
|
526
580
|
end
|
527
581
|
|
528
582
|
# Call :before_create() -- if exists.
|
529
583
|
@template[:method_before_create].call(@params) unless @template[:method_before_create].nil?
|
530
584
|
|
585
|
+
# TODO - Uncomment to see template (or remove).
|
586
|
+
# # Output the base template to terminal (for reference). Nested stacks are currently not outputted.
|
587
|
+
# base_template_lines.each do |line|
|
588
|
+
# puts " \x1B[38;5;240m#{line}\x1B[0m"
|
589
|
+
# end
|
590
|
+
# puts
|
591
|
+
|
531
592
|
# Create the Stack (if term_scrip == TRUE, script terminates inside).
|
532
|
-
App::AWSCli::cloudformation_stack_create(@params[OPTION_REGION], @params[OPTION_STACK_NAME],
|
593
|
+
App::AWSCli::cloudformation_stack_create(@params[OPTION_REGION], @params[OPTION_STACK_NAME], base_template,
|
533
594
|
params: assemble_params(@params),
|
534
595
|
tags: assemble_tags(@params),
|
535
596
|
capabilities: capabilities_arr,
|
@@ -548,7 +609,7 @@ module AppCommand
|
|
548
609
|
Blufin::Terminal::error('Something went wrong.')
|
549
610
|
end
|
550
611
|
else
|
551
|
-
Blufin::Terminal::success('Stack creation was successful.') unless term_script
|
612
|
+
Blufin::Terminal::success('Stack creation was successful.', nil, false) unless term_script
|
552
613
|
end
|
553
614
|
end
|
554
615
|
end
|
@@ -641,12 +702,15 @@ module AppCommand
|
|
641
702
|
OPTIONS => options_current[key],
|
642
703
|
}
|
643
704
|
end
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
705
|
+
|
706
|
+
# TODO NOW - REMOVE:DEPLOYMENT_STACK - This needs to be re-instated once we have the deployments.yml parsing.
|
707
|
+
# unless @template[:deployment_stack].nil?
|
708
|
+
# @template[:parameters][OPTION_PROJECT_ID] = {
|
709
|
+
# 'Type' => 'String',
|
710
|
+
# OPTION_DESCRIPTION => 'Project ID (used for associating this Stack with an existing Project).'
|
711
|
+
# }
|
712
|
+
# end
|
713
|
+
|
650
714
|
@template[:parameters][OPTION_STACK_NAME] = {
|
651
715
|
'Type' => 'String',
|
652
716
|
OPTION_DESCRIPTION => @template[:stack_name].nil? ? default_options : @template[:stack_name],
|
@@ -664,6 +728,11 @@ module AppCommand
|
|
664
728
|
OPTION_DESCRIPTION => 'The amount of minutes that can pass before the stack aborts the mission.',
|
665
729
|
DEFAULT => options_current[OPTION_TIMEOUT]
|
666
730
|
}
|
731
|
+
@template[:parameters][OPTION_OWNER] = {
|
732
|
+
'Type' => 'String',
|
733
|
+
OPTION_DESCRIPTION => "Who is considered the 'Owner' of this Stack?",
|
734
|
+
DEFAULT => @owner
|
735
|
+
}
|
667
736
|
unless @template[:single_serve]
|
668
737
|
@template[:parameters][OPTION_TERM_PROTECT] = {
|
669
738
|
'Type' => 'Boolean',
|
@@ -741,11 +810,11 @@ module AppCommand
|
|
741
810
|
if empty_options.any?
|
742
811
|
Blufin::Terminal::error("Cannot currently use this template because empty #{Blufin::Terminal::format_highlight('required resource(s)')} were detected.", empty_options, true, false)
|
743
812
|
else
|
744
|
-
choices = [{
|
745
|
-
choices << {
|
813
|
+
choices = [{value: 'y', text: 'Select this template'}]
|
814
|
+
choices << {value: 'Y', text: "Select this template \x1B[38;5;198m(and apply cached values)\x1B[0m"} if @cache_valid
|
746
815
|
end
|
747
816
|
# The prompt at the end of the intro.
|
748
|
-
choices << {
|
817
|
+
choices << {value: 'n', text: "\x1B[38;5;240m#{Blufin::Strings::RETURN_CHARACTER}\x1B[0m"}
|
749
818
|
choice = Blufin::Terminal::prompt_select('What would you like to do?', choices)
|
750
819
|
case choice
|
751
820
|
when 'y'
|
@@ -773,7 +842,7 @@ module AppCommand
|
|
773
842
|
constraints << "\x1B[38;5;240mMinLength: \x1B[38;5;#{App::AWSOutputter::CONSTRAINT_COLOR}m#{param_data['MinLength']}" if param_data.has_key?('MinLength')
|
774
843
|
constraints << "\x1B[38;5;240mMaxLength: \x1B[38;5;#{App::AWSOutputter::CONSTRAINT_COLOR}m#{param_data['MaxLength']}" if param_data.has_key?('MaxLength')
|
775
844
|
default = @template[:stack_name]
|
776
|
-
default =
|
845
|
+
default = App::Replacer::replace_string(default, replace_hash)
|
777
846
|
default = default.downcase
|
778
847
|
help_text = 'Stack Name (will be displayed in CloudFormation console).'
|
779
848
|
return Blufin::Terminal::prompt_ask("Enter #{param_name}#{render_constraints(constraints)}", default: default, help: help_text)
|
@@ -782,14 +851,16 @@ module AppCommand
|
|
782
851
|
constraints << "\x1B[38;5;240mMinLength: \x1B[38;5;#{App::AWSOutputter::CONSTRAINT_COLOR}m#{param_data['MinLength']}" if param_data.has_key?('MinLength')
|
783
852
|
constraints << "\x1B[38;5;240mMaxLength: \x1B[38;5;#{App::AWSOutputter::CONSTRAINT_COLOR}m#{param_data['MaxLength']}" if param_data.has_key?('MaxLength')
|
784
853
|
default = param_data.has_key?(DEFAULT) ? param_data[DEFAULT] : nil
|
785
|
-
default =
|
854
|
+
default = App::Replacer::replace_string(default, replace_hash)
|
786
855
|
help_text = 'Description (will be displayed in CloudFormation console).'
|
787
856
|
return Blufin::Terminal::prompt_ask("Enter #{param_name}#{render_constraints(constraints)}", default: default, help: help_text)
|
788
857
|
elsif param_name == OPTION_TIMEOUT
|
789
858
|
return param_data[DEFAULT]
|
790
859
|
elsif param_name == OPTION_TERM_PROTECT
|
791
860
|
# Basically, by default Termination Protection is off (because most of the time your just testing and want to press Enter).
|
792
|
-
|
861
|
+
options = [{:text => "No \xe2\x80\x94 Don't protect this stack.", :value => false}, {:text => 'Yes', :value => true}]
|
862
|
+
help_text = "Select 'Yes' to require an extra step when deleting this Stack."
|
863
|
+
return Blufin::Terminal::prompt_select('Enable "Accidental Termination" protection?', options, help: help_text)
|
793
864
|
elsif @lookups.has_key?(param_name)
|
794
865
|
# Sort alphabetically.
|
795
866
|
options, multi = fetch_autocomplete_options(param_name, silent: true)
|
@@ -844,7 +915,7 @@ module AppCommand
|
|
844
915
|
if default.nil?
|
845
916
|
default = @cache[param_name] if @cache.has_key?(param_name)
|
846
917
|
else
|
847
|
-
default =
|
918
|
+
default = App::Replacer::replace_string(default, replace_hash)
|
848
919
|
end
|
849
920
|
loop do
|
850
921
|
value = Blufin::Terminal::prompt_ask("Enter #{param_name}#{render_constraints(constraints)}", default: default, help: description)
|
@@ -880,33 +951,64 @@ module AppCommand
|
|
880
951
|
results[:matchers].each do |key, matchers|
|
881
952
|
raise RuntimeError, "Expected Array, but got #{matchers.class}" unless matchers.is_a?(Array)
|
882
953
|
matchers.each do |m|
|
883
|
-
|
954
|
+
val = m[:key]
|
955
|
+
matcher_raw = "${{#{key}:#{val}#{m.has_key?(:modifier) ? ":#{m[:modifier]}" : ''}}}"
|
884
956
|
begin
|
885
957
|
case key
|
886
958
|
when PARAMETERS
|
887
959
|
valid_keys = parameters.keys.push(*[
|
888
960
|
OPTION_CATEGORY,
|
889
|
-
|
890
|
-
OPTION_PROJECT,
|
961
|
+
OPTION_DESCRIPTION,
|
891
962
|
OPTION_ENVIRONMENT,
|
892
|
-
OPTION_REGION
|
963
|
+
OPTION_REGION,
|
964
|
+
OPTION_OWNER,
|
965
|
+
OPTION_PROJECT,
|
966
|
+
OPTION_REGION,
|
967
|
+
OPTION_TEMPLATE,
|
968
|
+
OPTION_TIMEOUT
|
893
969
|
])
|
894
|
-
raise RuntimeError unless valid_keys.include?(
|
970
|
+
raise RuntimeError "${{Parameter:#{val}}} is not valid (or has not been added to the valid keys Array)." unless valid_keys.include?(val)
|
971
|
+
when STACK
|
972
|
+
ks = val.split('/')
|
973
|
+
raise RuntimeError, "Expected value to have a single slash, instead got: #{val}" unless ks.length == 2
|
974
|
+
stack_found = false
|
975
|
+
Blufin::Files::get_dirs_in_dir(App::AWSCloudFormation::get_cloudformation_path).each do |cat|
|
976
|
+
cs = cat.split('/')
|
977
|
+
if cs[cs.length - 1] == ks[0]
|
978
|
+
Blufin::Files::get_dirs_in_dir(cat).each do |tmpl|
|
979
|
+
ts = tmpl.split('/')
|
980
|
+
if ts[ts.length - 1] == ks[1]
|
981
|
+
stack_found = true
|
982
|
+
break
|
983
|
+
end
|
984
|
+
end
|
985
|
+
end
|
986
|
+
break if stack_found
|
987
|
+
end
|
988
|
+
if stack_found
|
989
|
+
@nested_stacks[template_name] = {} unless @nested_stacks.has_key?(template_name)
|
990
|
+
@nested_stacks[template_name][val] = [] unless @nested_stacks[template_name].has_key?(val)
|
991
|
+
@nested_stacks[template_name][val] << file_cloudformation
|
992
|
+
else
|
993
|
+
raise RuntimeError, "Stack not found: #{val}"
|
994
|
+
end
|
895
995
|
when SYSTEM
|
896
|
-
raise RuntimeError unless %w(UUID-8 UUID-16 UUID-AWX).include?(
|
996
|
+
raise RuntimeError unless %w(UUID-8 UUID-16 UUID-AWX).include?(val)
|
897
997
|
when 'file'
|
898
|
-
file_path = "#{Blufin::Files::extract_path_name(file_cloudformation)}/#{
|
998
|
+
file_path = "#{Blufin::Files::extract_path_name(file_cloudformation)}/#{val}"
|
899
999
|
unless Blufin::Files::file_exists(file_path)
|
900
1000
|
@warnings << "\x1B[38;5;196m#{template_name}\x1B[38;5;240m \xe2\x80\x94 File from matcher not found: #{Blufin::Terminal::format_invalid(file_path)}"
|
901
1001
|
next
|
902
1002
|
end
|
903
|
-
results_inner =
|
1003
|
+
results_inner = App::Replacer::scan_file(file_path)
|
904
1004
|
validate_matchers(file_path, results_inner, template_name, parameters)
|
905
1005
|
else
|
906
|
-
raise RuntimeError
|
1006
|
+
raise RuntimeError "Key: #{key} does not match any of the expected keys."
|
907
1007
|
end
|
908
|
-
rescue
|
909
|
-
|
1008
|
+
rescue => e
|
1009
|
+
message = e.message.to_s.strip
|
1010
|
+
@warnings << "\x1B[38;5;196m#{template_name}\x1B[38;5;240m \xe2\x80\x94 Invalid matcher found in template: #{Blufin::Terminal::format_invalid(matcher_raw)}\x1B[0m" if message == ''
|
1011
|
+
@warnings << "\x1B[38;5;196m#{template_name}\x1B[38;5;240m \xe2\x80\x94 Invalid matcher found in template: #{Blufin::Terminal::format_invalid(matcher_raw)} \x1B[38;5;240m \xe2\x80\x94 #{message}\x1B[0m" unless message == ''
|
910
1012
|
end
|
911
1013
|
end
|
912
1014
|
end
|
@@ -935,10 +1037,10 @@ module AppCommand
|
|
935
1037
|
raise RuntimeError, "Key not found in @lookups: #{resource_name}" unless @lookups.has_key?(resource_name)
|
936
1038
|
return @lookup_cache[resource_name] if @lookup_cache.has_key?(resource_name)
|
937
1039
|
multi = false
|
938
|
-
if resource_name == SSH_USERS
|
1040
|
+
if resource_name == App::Replacer::SSH_USERS
|
939
1041
|
multi = true
|
940
1042
|
options = []
|
941
|
-
@lookups[SSH_USERS].each do |user, pub_key|
|
1043
|
+
@lookups[App::Replacer::SSH_USERS].each do |user, pub_key|
|
942
1044
|
options << {
|
943
1045
|
:value => pub_key,
|
944
1046
|
:text => user,
|
@@ -958,7 +1060,7 @@ module AppCommand
|
|
958
1060
|
# Returns short-hand syntax for tags.
|
959
1061
|
# @return string
|
960
1062
|
def assemble_tags(params)
|
961
|
-
output = []
|
1063
|
+
output = [{'Key' => OPTION_OWNER, 'Value' => @owner}]
|
962
1064
|
params.each do |key, value|
|
963
1065
|
next unless AWS_TAGS.include?(key)
|
964
1066
|
output << {
|
@@ -966,16 +1068,19 @@ module AppCommand
|
|
966
1068
|
'Value' => value
|
967
1069
|
}
|
968
1070
|
end
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
1071
|
+
|
1072
|
+
# TODO NOW - REMOVE:DEPLOYMENT_STACK - This needs to be re-instated once we have the deployments.yml parsing.
|
1073
|
+
# if @template.has_key?(:deployment_stack) && params.has_key?(OPTION_PROJECT_ID)
|
1074
|
+
# output << {
|
1075
|
+
# 'Key' => OPTION_DEPLOYMENT_STACK,
|
1076
|
+
# 'Value' => @template[:deployment_stack].to_s
|
1077
|
+
# }
|
1078
|
+
# output << {
|
1079
|
+
# 'Key' => OPTION_PROJECT_ID,
|
1080
|
+
# 'Value' => params[OPTION_PROJECT_ID]
|
1081
|
+
# }
|
1082
|
+
# end
|
1083
|
+
|
979
1084
|
output.to_json
|
980
1085
|
end
|
981
1086
|
|