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.
@@ -3,13 +3,13 @@ require 'digest'
3
3
 
4
4
  module AppCommand
5
5
 
6
- class AWSCloudFormationCreate < ::Convoy::ActionCommand::Base
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(stackname description environment project region timeout resource cache_uuid category template terminationprotection projectid)
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::Projects::init(App::AWSProfile::get_profile, App::CONFIG_FILE)
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 = App::AWSReports::get_lookups(@data)
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 = App::AWSProfile::get_ssh_users
96
- @lookups[SSH_USERS] = users if users.any?
100
+ users = App::AWSProfile::get_ssh_users
101
+ @lookups[App::Replacer::SSH_USERS] = users if users.any?
97
102
 
98
- @options_default[OPTION_ENVIRONMENT] = Blufin::Projects::get_environments
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 = Blufin::Replacer::scan_string(stack_name)
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 = Blufin::Replacer::scan_file(file_cloudformation)
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
- @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?
339
- @templates[category] = {} unless @templates.has_key?(category)
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
- @templates[category][template] = {
347
- :name => template,
348
- :path => template_path,
349
- :broken => false,
350
- :file_cloudformation => file_cloudformation,
351
- :file_ruby => file_ruby,
352
- :method_before_create => method_before_create,
353
- :method_after_create => method_after_create,
354
- :method_before_teardown => method_before_teardown,
355
- :method_after_teardown => method_after_teardown,
356
- :parameters => parameters,
357
- :parameters_no_sort => parameters_no_sort,
358
- :intro => intro,
359
- :description => description,
360
- :stack_name => stack_name,
361
- :projects => projects,
362
- :environments => environments,
363
- :regions => regions,
364
- :timeout => timeout,
365
- :single_serve => single_serve
366
- }
367
- @templates[category][template][:deployment_stack] = deployment_stack unless deployment_stack.nil?
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 = true
385
- showing_tags = false
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
- # Figure out which deployments are eligible for this stack.
405
- if @template.has_key?(:deployment_stack)
406
- @projects = Blufin::Projects::get_deployments(
407
- @params[OPTION_PROJECT],
408
- @template[:deployment_stack],
409
- @params[OPTION_REGION],
410
- @params[OPTION_ENVIRONMENT]
411
- )
412
- if @projects.any?
413
- project_options = []
414
- @projects.each { |project| project_options << project[1][Blufin::Projects::DEPLOYMENT_ID] }
415
- @params[param_name] = Blufin::Terminal::prompt_select('Select Project ID:', project_options, help: param_data[OPTION_DESCRIPTION])
416
- puts
417
- end
418
- end
419
- next
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
- source = File.expand_path("#{App::AWSCloudFormation::get_cloudformation_path}/#{category}/#{template}/template.yml")
450
- raise RuntimeError, "File does not exist: #{source}" unless Blufin::Files::file_exists(source)
451
- file_lines = Blufin::Replacer::replace_yml(source, get_replacer_params(category, template))
452
- tmp_file = "/tmp/converted-template-#{Blufin::Strings::random_string(4)}.txt"
453
- Blufin::Files::write_file(tmp_file, file_lines)
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
- s3_url = App::AWSCloudFormation::upload_cloudformation_template(tmp_file, category, template)
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], s3_url)
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] = @params[OPTION_STACK_NAME]
480
- output[OPTION_DESCRIPTION] = @params[OPTION_DESCRIPTION]
481
- output[SPACER] = true
482
- if @template.has_key?(:deployment_stack) && !@params[OPTION_PROJECT_ID].nil? && @params[OPTION_PROJECT_ID].length > 0
483
- output[OPTION_DEPLOYMENT_STACK] = @template[:deployment_stack]
484
- output[OPTION_PROJECT_ID] = @params[OPTION_PROJECT_ID]
485
- end
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
- term_script = Blufin::Terminal::prompt_yes?(' End script after CloudFormation starts running?')
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], s3_url,
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
- unless @template[:deployment_stack].nil?
645
- @template[:parameters][OPTION_PROJECT_ID] = {
646
- 'Type' => 'String',
647
- OPTION_DESCRIPTION => 'Project ID (used for associating this Stack with an existing Project).'
648
- }
649
- end
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 = [{ value: 'y', text: 'Select this template' }]
745
- choices << { value: 'Y', text: "Select this template \x1B[38;5;198m(and apply cached values)\x1B[0m" } if @cache_valid
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 << { value: 'n', text: "\x1B[38;5;240m#{Blufin::Strings::RETURN_CHARACTER}\x1B[0m" }
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 = Blufin::Replacer::replace_string(default, replace_hash)
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 = Blufin::Replacer::replace_string(default, replace_hash)
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
- return !Blufin::Terminal::prompt_yes?("Allow accidental Termination? (type 'n' to enable Termination Protection)")
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 = Blufin::Replacer::replace_string(default, replace_hash)
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
- matcher_raw = "${{#{key}:#{m[:key]}#{m.has_key?(:modifier) ? ":#{m[:modifier]}" : ''}}}"
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
- OPTION_TEMPLATE,
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?(m[:key])
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?(m[:key])
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)}/#{m[:key]}"
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 = Blufin::Replacer::scan_file(file_path)
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
- @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)}"
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
- if @template.has_key?(:deployment_stack) && params.has_key?(OPTION_PROJECT_ID)
970
- output << {
971
- 'Key' => OPTION_DEPLOYMENT_STACK,
972
- 'Value' => @template[:deployment_stack].to_s
973
- }
974
- output << {
975
- 'Key' => OPTION_PROJECT_ID,
976
- 'Value' => params[OPTION_PROJECT_ID]
977
- }
978
- end
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
 
@@ -1,6 +1,6 @@
1
1
  module AppCommand
2
2
 
3
- class AWSCloudFormationDelete < ::Convoy::ActionCommand::Base
3
+ class CloudFormationDelete < ::Convoy::ActionCommand::Base
4
4
 
5
5
  def execute
6
6
 
@@ -1,6 +1,6 @@
1
1
  module AppCommand
2
2
 
3
- class AWSCloudFormationDetectDrift < ::Convoy::ActionCommand::Base
3
+ class CloudFormationDetectDrift < ::Convoy::ActionCommand::Base
4
4
 
5
5
  TMP_OUTPUT = '/tmp/execute-output'
6
6