aws-cft-tools 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +10 -0
- data/.gitignore +52 -0
- data/.rspec +2 -0
- data/.rubocop.yml +19 -0
- data/.travis.yml +5 -0
- data/.yardopts +1 -0
- data/BEST-PRACTICES.md +136 -0
- data/CONTRIBUTING.md +38 -0
- data/Gemfile +8 -0
- data/LICENSE +15 -0
- data/README.md +118 -0
- data/Rakefile +17 -0
- data/USAGE.adoc +404 -0
- data/aws-cft-tools.gemspec +53 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/code.json +24 -0
- data/exe/aws-cft +176 -0
- data/lib/aws-cft-tools.rb +3 -0
- data/lib/aws_cft_tools.rb +31 -0
- data/lib/aws_cft_tools/aws_enumerator.rb +55 -0
- data/lib/aws_cft_tools/change.rb +66 -0
- data/lib/aws_cft_tools/client.rb +84 -0
- data/lib/aws_cft_tools/client/base.rb +40 -0
- data/lib/aws_cft_tools/client/cft.rb +93 -0
- data/lib/aws_cft_tools/client/cft/changeset_management.rb +109 -0
- data/lib/aws_cft_tools/client/cft/stack_management.rb +85 -0
- data/lib/aws_cft_tools/client/ec2.rb +136 -0
- data/lib/aws_cft_tools/client/templates.rb +84 -0
- data/lib/aws_cft_tools/deletion_change.rb +43 -0
- data/lib/aws_cft_tools/dependency_tree.rb +109 -0
- data/lib/aws_cft_tools/dependency_tree/nodes.rb +71 -0
- data/lib/aws_cft_tools/dependency_tree/variables.rb +37 -0
- data/lib/aws_cft_tools/errors.rb +25 -0
- data/lib/aws_cft_tools/runbook.rb +166 -0
- data/lib/aws_cft_tools/runbook/report.rb +30 -0
- data/lib/aws_cft_tools/runbooks.rb +16 -0
- data/lib/aws_cft_tools/runbooks/common/changesets.rb +30 -0
- data/lib/aws_cft_tools/runbooks/common/templates.rb +38 -0
- data/lib/aws_cft_tools/runbooks/deploy.rb +107 -0
- data/lib/aws_cft_tools/runbooks/deploy/reporting.rb +50 -0
- data/lib/aws_cft_tools/runbooks/deploy/stacks.rb +109 -0
- data/lib/aws_cft_tools/runbooks/deploy/templates.rb +37 -0
- data/lib/aws_cft_tools/runbooks/deploy/threading.rb +37 -0
- data/lib/aws_cft_tools/runbooks/diff.rb +28 -0
- data/lib/aws_cft_tools/runbooks/diff/context.rb +86 -0
- data/lib/aws_cft_tools/runbooks/diff/context/reporting.rb +87 -0
- data/lib/aws_cft_tools/runbooks/hosts.rb +43 -0
- data/lib/aws_cft_tools/runbooks/images.rb +43 -0
- data/lib/aws_cft_tools/runbooks/init.rb +86 -0
- data/lib/aws_cft_tools/runbooks/retract.rb +69 -0
- data/lib/aws_cft_tools/runbooks/retract/templates.rb +44 -0
- data/lib/aws_cft_tools/runbooks/stacks.rb +43 -0
- data/lib/aws_cft_tools/stack.rb +83 -0
- data/lib/aws_cft_tools/template.rb +177 -0
- data/lib/aws_cft_tools/template/dsl_context.rb +14 -0
- data/lib/aws_cft_tools/template/file_system.rb +62 -0
- data/lib/aws_cft_tools/template/metadata.rb +144 -0
- data/lib/aws_cft_tools/template/properties.rb +129 -0
- data/lib/aws_cft_tools/template_set.rb +120 -0
- data/lib/aws_cft_tools/template_set/array_methods.rb +63 -0
- data/lib/aws_cft_tools/template_set/closure.rb +77 -0
- data/lib/aws_cft_tools/template_set/dependencies.rb +55 -0
- data/lib/aws_cft_tools/template_set/each_slice_state.rb +58 -0
- data/lib/aws_cft_tools/version.rb +8 -0
- data/rubycritic.reek +3 -0
- metadata +321 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'diffy'
|
4
|
+
|
5
|
+
module AwsCftTools
|
6
|
+
module Runbooks
|
7
|
+
##
|
8
|
+
# Images - report on available AMIs
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# % aws-cli diff -e QA # list the differences between deployed and local definitions for QA
|
12
|
+
# % aws-cli diff -e QA -r App # list the differences between deployed and local definitions for
|
13
|
+
# # the App role in QA
|
14
|
+
#
|
15
|
+
class Diff < Runbook
|
16
|
+
require_relative 'diff/context'
|
17
|
+
|
18
|
+
def run
|
19
|
+
context = Context.new(client.stacks, client.templates, options)
|
20
|
+
|
21
|
+
# now match them up
|
22
|
+
context.report_on_missing_templates
|
23
|
+
context.report_on_missing_stacks
|
24
|
+
context.report_on_differences
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'diffy'
|
4
|
+
|
5
|
+
module AwsCftTools
|
6
|
+
module Runbooks
|
7
|
+
class Diff
|
8
|
+
##
|
9
|
+
# The context of stacks and templates for a Diff report.
|
10
|
+
#
|
11
|
+
class Context
|
12
|
+
attr_reader :stacks, :templates, :options
|
13
|
+
|
14
|
+
##
|
15
|
+
# The options provided to the +diff+ command to build template diffs.
|
16
|
+
#
|
17
|
+
DIFF_OPTIONS = %w[-w -U5 -t].freeze
|
18
|
+
|
19
|
+
require_relative '../common/templates'
|
20
|
+
require_relative 'context/reporting'
|
21
|
+
|
22
|
+
include Common::Templates
|
23
|
+
include Context::Reporting
|
24
|
+
|
25
|
+
##
|
26
|
+
# @param stacks [Array<AwsCftTools::Stack>]
|
27
|
+
# @param templates [AwsCftTools::TemplateSet]
|
28
|
+
# @param options [Hash]
|
29
|
+
def initialize(stacks, templates, options = {})
|
30
|
+
@stacks = build_map(stacks)
|
31
|
+
@templates = build_map(templates)
|
32
|
+
@options = options
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Reports out stacks that do not have corresponding templates.
|
37
|
+
#
|
38
|
+
def report_on_missing_templates
|
39
|
+
output_report_on_missing_templates(stacks.keys - templates.keys)
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Reports out templates that do not have corresponding stacks.
|
44
|
+
#
|
45
|
+
def report_on_missing_stacks
|
46
|
+
output_report_on_missing_stacks(templates.keys - stacks.keys)
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Reports on the differences in the template bodies between the set of templates and the
|
51
|
+
# deployed stacks.
|
52
|
+
#
|
53
|
+
def report_on_differences
|
54
|
+
# these are stacks with templates
|
55
|
+
output_report_on_differences(build_diffs)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def build_map(list)
|
61
|
+
list.each_with_object({}) do |thing, map|
|
62
|
+
map[thing.name] = thing
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def build_diffs
|
67
|
+
stacks
|
68
|
+
.keys
|
69
|
+
.sort
|
70
|
+
.select { |fn| templates[fn] }
|
71
|
+
.each_with_object({}) do |name, acc|
|
72
|
+
acc[name] = build_diff(stacks[name], templates[name])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def build_diff(stack, template)
|
77
|
+
output_type = options[:colorize] ? :color : :text
|
78
|
+
Diffy::Diff.new(
|
79
|
+
stack.template_source, template.template_source_for_aws,
|
80
|
+
include_diff_info: true, diff: DIFF_OPTIONS
|
81
|
+
).to_s(output_type)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'diffy'
|
4
|
+
|
5
|
+
module AwsCftTools
|
6
|
+
module Runbooks
|
7
|
+
class Diff
|
8
|
+
class Context
|
9
|
+
##
|
10
|
+
# Reporting functions for the Diff context
|
11
|
+
#
|
12
|
+
module Reporting
|
13
|
+
def output_report_on_missing_templates(missing)
|
14
|
+
return if missing.empty?
|
15
|
+
puts "\nStacks with no template:\n #{missing.sort.join("\n ")}\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
def output_report_on_missing_stacks(missing)
|
19
|
+
missing = template_filenames(missing)
|
20
|
+
return if missing.empty?
|
21
|
+
puts "\nUndeployed templates:\n #{missing.sort.join("\n ")}\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
def output_report_on_differences(diffs)
|
25
|
+
report_on_blank_diffs(diffs)
|
26
|
+
report_on_real_diffs(diffs)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def report_on_blank_diffs(diffs)
|
32
|
+
no_diffs = diffs.keys.select { |name| diffs[name].match(/\A\s*\Z/) }
|
33
|
+
return if no_diffs.empty?
|
34
|
+
|
35
|
+
puts "\nTemplates with no changes:\n #{template_filenames(no_diffs).sort.join("\n ")}\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
def report_on_real_diffs(diffs)
|
39
|
+
real_diffs = diffs.keys.reject { |name| diffs[name].match(/\A\s*\Z/) }
|
40
|
+
|
41
|
+
return if real_diffs.empty?
|
42
|
+
|
43
|
+
if options[:verbose]
|
44
|
+
report_full_diffs(real_diffs, diffs)
|
45
|
+
else
|
46
|
+
report_list_of_diffs(real_diffs)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def report_full_diffs(names, diffs)
|
51
|
+
names.sort.each do |name|
|
52
|
+
report_pos_diff(templates[name].filename.to_s, diffs[name])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def report_list_of_diffs(real_diffs)
|
57
|
+
puts "\nTemplates with changes:\n #{template_filenames(real_diffs).sort.join("\n ")}\n"
|
58
|
+
end
|
59
|
+
|
60
|
+
def template_filenames(stack_names)
|
61
|
+
set = templates.values_at(*stack_names).map(&:filename).map(&:to_s)
|
62
|
+
allowed = options[:templates]
|
63
|
+
if allowed && allowed.any?
|
64
|
+
set & allowed
|
65
|
+
else
|
66
|
+
set
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def report_pos_diff(filename, diff)
|
71
|
+
return unless template_in_consideration(options[:templates], filename)
|
72
|
+
puts diff_with_filenames(diff, filename), ''
|
73
|
+
end
|
74
|
+
|
75
|
+
def diff_with_filenames(diff, filename)
|
76
|
+
diff.sub(%r{--- /.*$}, "--- #{filename} @ AWS")
|
77
|
+
.sub(%r{\+\+\+ /.*$}, "+++ #{filename} @ Local")
|
78
|
+
end
|
79
|
+
|
80
|
+
def template_in_consideration(list, filename)
|
81
|
+
!list || list.empty? || list.include?(filename)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AwsCftTools
|
4
|
+
module Runbooks
|
5
|
+
##
|
6
|
+
# Hosts - report on EC2 instances
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# % aws-cli hosts # list all known EC2 instances
|
10
|
+
# % aws-cli hosts -e QA # list all known EC2 instances in the QA environment
|
11
|
+
# % aws-cli hosts -r Bastion -e QA # list all known Bastion hosts in the QA environment
|
12
|
+
#
|
13
|
+
class Hosts < Runbook::Report
|
14
|
+
###
|
15
|
+
# @return [Array<OpenStruct>]
|
16
|
+
#
|
17
|
+
def items
|
18
|
+
client.instances.sort_by(&method(:sort_key))
|
19
|
+
end
|
20
|
+
|
21
|
+
###
|
22
|
+
# @return [Array<String>]
|
23
|
+
#
|
24
|
+
def columns
|
25
|
+
%w[public_ip private_ip] + environment_column + role_column + ['instance']
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def sort_key(host)
|
31
|
+
[host.environment, host.role, host.ip].compact
|
32
|
+
end
|
33
|
+
|
34
|
+
def environment_column
|
35
|
+
options[:environment] ? [] : ['environment']
|
36
|
+
end
|
37
|
+
|
38
|
+
def role_column
|
39
|
+
options[:role] ? [] : ['role']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AwsCftTools
|
4
|
+
module Runbooks
|
5
|
+
##
|
6
|
+
# Images - report on available AMIs
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# % aws-cli images # list all known AMIs
|
10
|
+
# % aws-cli images -e QA # list all known AMIs tagged for the QA environment
|
11
|
+
# % aws-cli images -e QA -r App # list all known AMIs tagged for the QA environment and App role
|
12
|
+
#
|
13
|
+
class Images < Runbook::Report
|
14
|
+
###
|
15
|
+
# @return [Array<OpenStruct>]
|
16
|
+
#
|
17
|
+
def items
|
18
|
+
client.images.sort_by(&method(:sort_key))
|
19
|
+
end
|
20
|
+
|
21
|
+
###
|
22
|
+
# @return [Array<String>]
|
23
|
+
#
|
24
|
+
def columns
|
25
|
+
environment_column + role_column + %w[created_at public type image_id]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def sort_key(image)
|
31
|
+
[image.environment, image.role, image.created_at].compact
|
32
|
+
end
|
33
|
+
|
34
|
+
def environment_column
|
35
|
+
options[:environment] ? [] : ['environment']
|
36
|
+
end
|
37
|
+
|
38
|
+
def role_column
|
39
|
+
options[:role] ? [] : ['role']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AwsCftTools
|
4
|
+
module Runbooks
|
5
|
+
##
|
6
|
+
# Deploy - manage CloudFormation stack deployment
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# % aws-cli init # create skeleton project in the current directory
|
10
|
+
#
|
11
|
+
class Init < Runbook
|
12
|
+
##
|
13
|
+
# The default project configuration file.
|
14
|
+
#
|
15
|
+
DEFAULT_CONFIG = <<~EOF
|
16
|
+
---
|
17
|
+
###
|
18
|
+
# Values in this file override the defaults in aws-cft. Command line options override these values.
|
19
|
+
#
|
20
|
+
# This is a good place to put project- or account-wide defaults for teams using the templates in this
|
21
|
+
# repo.
|
22
|
+
###
|
23
|
+
|
24
|
+
###
|
25
|
+
# By default, we want as much detail as possible.
|
26
|
+
#
|
27
|
+
:verbose: true
|
28
|
+
|
29
|
+
###
|
30
|
+
# When different templates have nothing indicating their relative ordering, they are ordered based on the
|
31
|
+
# directory/folder in which they appear ordered by this list.
|
32
|
+
#
|
33
|
+
:template_folder_priorities:
|
34
|
+
- vpcs
|
35
|
+
- networks
|
36
|
+
- security
|
37
|
+
- data-resources
|
38
|
+
- data-services
|
39
|
+
- applications
|
40
|
+
EOF
|
41
|
+
|
42
|
+
##
|
43
|
+
# The template role directories to build out when creating a project.
|
44
|
+
#
|
45
|
+
TEMPLATE_ROLES = %w[applications data-resources data-services networks security vpcs].freeze
|
46
|
+
|
47
|
+
##
|
48
|
+
# The different types of files used when managing templates and stacks.
|
49
|
+
#
|
50
|
+
FILE_TYPES = %w[parameters templates].freeze
|
51
|
+
|
52
|
+
def run
|
53
|
+
ensure_project_directory
|
54
|
+
ensure_cloudformation_directories
|
55
|
+
ensure_config_file
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def ensure_config_file
|
61
|
+
operation('Creating configuration file') do
|
62
|
+
file = options[:root] + options[:config_file]
|
63
|
+
if file.exist?
|
64
|
+
narrative 'Configuration file already exists. Not overwriting.'
|
65
|
+
else
|
66
|
+
doing { file.write(DEFAULT_CONFIG) }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def ensure_project_directory
|
72
|
+
ensure_directory(options[:root])
|
73
|
+
end
|
74
|
+
|
75
|
+
def ensure_cloudformation_directories
|
76
|
+
FILE_TYPES.product(TEMPLATE_ROLES).map { |list| list.join('/') }.each do |dir|
|
77
|
+
ensure_directory(options[:root] + 'cloudformation/' + dir)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def ensure_directory(dir)
|
82
|
+
operation("Ensure #{dir} exists") { dir.mkpath }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'table_print'
|
5
|
+
require 'securerandom'
|
6
|
+
|
7
|
+
module AwsCftTools
|
8
|
+
module Runbooks
|
9
|
+
##
|
10
|
+
# Retract - manage CloudFormation stack retraction
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# % aws-cft retract -e QA # delete all templates in the QA environment
|
14
|
+
# % aws-cft retract -e Staging -n -v # narrate the templates that would be deleted in Staging
|
15
|
+
# % aws-cft retract -e Production -c -v # narrate the changes implied by deleting stacks in Production
|
16
|
+
#
|
17
|
+
class Retract < Runbook
|
18
|
+
require_relative 'common/changesets'
|
19
|
+
require_relative 'retract/templates'
|
20
|
+
|
21
|
+
extend Forwardable
|
22
|
+
|
23
|
+
include Common::Changesets
|
24
|
+
include Templates
|
25
|
+
|
26
|
+
def_delegators :client, :images, :stacks
|
27
|
+
|
28
|
+
def run
|
29
|
+
report_template_dependencies
|
30
|
+
|
31
|
+
detail do
|
32
|
+
tp(free_templates, ['filename'])
|
33
|
+
end
|
34
|
+
|
35
|
+
remove_deployed_templates
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
##
|
41
|
+
# run appropriate update function against deployed templates/stacks
|
42
|
+
#
|
43
|
+
def remove_deployed_templates
|
44
|
+
free_templates.each(&method(:remove_deployed_template))
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove_deployed_template(template)
|
48
|
+
operation("Removing: #{template.name}") do
|
49
|
+
checking { narrate_changes(client.changes_on_stack_delete(template, changeset_set)) }
|
50
|
+
doing { client.delete_stack(template) }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# report_undefined_image - provide list of undefined imports that block stack deployment
|
56
|
+
#
|
57
|
+
def report_template_dependencies
|
58
|
+
diff = (templates - free_templates).map { |template| template.filename.to_s }
|
59
|
+
error_on_dependencies(diff) if diff.any?
|
60
|
+
end
|
61
|
+
|
62
|
+
def error_on_dependencies(templates)
|
63
|
+
puts '*** Unable to remove templates.'
|
64
|
+
puts 'The following templates are dependencies for templates not marked for removal: ', templates
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|