auster 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +13 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +41 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +152 -0
- data/Rakefile +6 -0
- data/auster.gemspec +37 -0
- data/bin/auster +8 -0
- data/bin/console +8 -0
- data/example-repo/.auster.yaml +0 -0
- data/example-repo/cfer-helpers/.keep +0 -0
- data/example-repo/config/.keep +0 -0
- data/example-repo/config/schema.yaml +10 -0
- data/example-repo/config/us-west-2/dev-ed1.yaml +7 -0
- data/example-repo/config/validator.rb +5 -0
- data/example-repo/steps/.keep +0 -0
- data/example-repo/steps/00.bootstrap/.keep +0 -0
- data/example-repo/steps/00.bootstrap/cfer/defs/s3.rb +3 -0
- data/example-repo/steps/00.bootstrap/cfer/outputs.rb +1 -0
- data/example-repo/steps/00.bootstrap/cfer/parameters.rb +0 -0
- data/example-repo/steps/00.bootstrap/cfer/require.rb +0 -0
- data/example-repo/steps/00.bootstrap/on-create.d/00-debug.rb +4 -0
- data/example-repo/steps/00.bootstrap/on-destroy.d/00-debug.rb +4 -0
- data/example-repo/steps/00.bootstrap/post-converge.d/00-debug.rb +5 -0
- data/example-repo/steps/00.bootstrap/pre-converge.d/00-debug.rb +4 -0
- data/example-repo/steps/01.dependent/.keep +0 -0
- data/example-repo/steps/01.dependent/cfer/defs/s3.rb +3 -0
- data/example-repo/steps/01.dependent/cfer/outputs.rb +1 -0
- data/example-repo/steps/01.dependent/cfer/parameters.rb +0 -0
- data/example-repo/steps/01.dependent/cfer/require.rb +0 -0
- data/example-repo/steps/01.dependent/on-create.d/00-debug.rb +4 -0
- data/example-repo/steps/01.dependent/on-destroy.d/00-debug.rb +4 -0
- data/example-repo/steps/01.dependent/post-converge.d/00-debug.rb +7 -0
- data/example-repo/steps/01.dependent/pre-converge.d/00-debug.rb +5 -0
- data/lib/cfer/auster/cfer_evaluator.rb +125 -0
- data/lib/cfer/auster/cfer_helpers.rb +71 -0
- data/lib/cfer/auster/cli/_shared.rb +49 -0
- data/lib/cfer/auster/cli/destroy.rb +34 -0
- data/lib/cfer/auster/cli/generate/repo.rb +27 -0
- data/lib/cfer/auster/cli/generate/step.rb +27 -0
- data/lib/cfer/auster/cli/generate.rb +35 -0
- data/lib/cfer/auster/cli/json.rb +44 -0
- data/lib/cfer/auster/cli/nuke.rb +62 -0
- data/lib/cfer/auster/cli/run.rb +34 -0
- data/lib/cfer/auster/cli.rb +60 -0
- data/lib/cfer/auster/config.rb +70 -0
- data/lib/cfer/auster/logging.rb +32 -0
- data/lib/cfer/auster/param_validator.rb +22 -0
- data/lib/cfer/auster/repo.rb +158 -0
- data/lib/cfer/auster/script_executor.rb +57 -0
- data/lib/cfer/auster/step.rb +181 -0
- data/lib/cfer/auster/version.rb +5 -0
- data/lib/cfer/auster.rb +22 -0
- metadata +270 -0
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cfer"
|
4
|
+
|
5
|
+
module Cfer
|
6
|
+
module Core
|
7
|
+
class Resource
|
8
|
+
# TODO: we need a better way to inject general helpers.
|
9
|
+
|
10
|
+
def cfize(text, capture_regexp: nil)
|
11
|
+
Cfer::Auster::CferHelpers.cfize(text, capture_regexp: capture_regexp)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Auster
|
17
|
+
class CloudFormationError < RuntimeError; end
|
18
|
+
|
19
|
+
class CferEvaluator
|
20
|
+
include Cfer::Auster::Logging::Mixin
|
21
|
+
|
22
|
+
def initialize(path:, stack_name:, parameters:, metadata:, client_options: {}, stack_options: {})
|
23
|
+
raise "path must be a String" unless path.is_a?(String)
|
24
|
+
raise "path must be a directory" unless File.directory?(path)
|
25
|
+
|
26
|
+
raise "stack_name must be a String" unless stack_name.is_a?(String)
|
27
|
+
|
28
|
+
raise "parameters must be a Hash" unless parameters.is_a?(Hash)
|
29
|
+
|
30
|
+
parameters[:AusterOptions] ||= {}
|
31
|
+
|
32
|
+
@stack_name = stack_name
|
33
|
+
@stack_options = stack_options
|
34
|
+
@cfer_client = Cfer::Cfn::Client.new(client_options.merge(stack_name: stack_name, region: parameters[:AWSRegion]))
|
35
|
+
@cfer_stack = Cfer::Core::Stack.new(
|
36
|
+
stack_options.merge(
|
37
|
+
client: @cfer_client, include_base: path, parameters: parameters,
|
38
|
+
force_s3: !!parameters[:AusterOptions][:S3Path],
|
39
|
+
s3_path: parameters[:AusterOptions][:S3Path]
|
40
|
+
)
|
41
|
+
)
|
42
|
+
|
43
|
+
require_rb = File.join(path, "require.rb")
|
44
|
+
parameters_rb = File.join(path, "parameters.rb")
|
45
|
+
outputs_rb = File.join(path, "outputs.rb")
|
46
|
+
|
47
|
+
global_helpers_dir = File.join(path, "../../../cfer-helpers")
|
48
|
+
global_helpers = Dir["#{global_helpers_dir}/**/*.rb"].reject { |f| File.basename(f).start_with?("_") }
|
49
|
+
helpers_dir = File.join(path, "helpers")
|
50
|
+
helpers = Dir["#{helpers_dir}/**/*.rb"].reject { |f| File.basename(f).start_with?("_") }
|
51
|
+
defs_dir = File.join(path, "defs")
|
52
|
+
|
53
|
+
@cfer_stack.extend Cfer::Auster::CferHelpers
|
54
|
+
@cfer_stack.build_from_block do
|
55
|
+
self[:Metadata][:Auster] = metadata
|
56
|
+
|
57
|
+
global_helpers.each do |helper_file|
|
58
|
+
eval_file helper_file
|
59
|
+
end
|
60
|
+
|
61
|
+
helpers.each do |helper_file|
|
62
|
+
eval_file helper_file
|
63
|
+
end
|
64
|
+
|
65
|
+
[require_rb, parameters_rb, outputs_rb].each do |file|
|
66
|
+
f = file.gsub(path, ".")
|
67
|
+
include_template f if File.file?(file)
|
68
|
+
end
|
69
|
+
|
70
|
+
Dir["#{defs_dir}/**/*.rb"].each do |def_file|
|
71
|
+
f = def_file.gsub(path, ".")
|
72
|
+
include_template f
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def converge!(block: true)
|
78
|
+
# because it isn't actually redundant...
|
79
|
+
# rubocop:disable Style/RedundantBegin
|
80
|
+
begin
|
81
|
+
@cfer_stack.converge!(@stack_options)
|
82
|
+
tail! if block
|
83
|
+
rescue Aws::CloudFormation::Errors::ValidationError => err
|
84
|
+
if err.message == "No updates are to be performed."
|
85
|
+
logger.info "CloudFormation has no updates to perform."
|
86
|
+
else
|
87
|
+
logger.error "Error (#{err.class.name}) in converge: #{err.message}"
|
88
|
+
raise err
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def destroy!(block: true)
|
94
|
+
@cfer_client.delete_stack(stack_name: @stack_name)
|
95
|
+
tail! if block
|
96
|
+
end
|
97
|
+
|
98
|
+
def tail!(throw_if_failed: true)
|
99
|
+
tail_start = DateTime.now
|
100
|
+
has_shown_bar = false
|
101
|
+
has_failed = false
|
102
|
+
|
103
|
+
@cfer_client.tail(number: 0, follow: true) do |event|
|
104
|
+
has_failed = true if event.timestamp >= tail_start && event.resource_status.include?("FAILED")
|
105
|
+
if event.timestamp >= tail_start && !has_shown_bar
|
106
|
+
logger.info "CFN >> ----- CURRENT RUN START -----"
|
107
|
+
has_failed = false
|
108
|
+
has_shown_bar = true
|
109
|
+
end
|
110
|
+
logger.info "CFN >> %-30s %-40s %-20s %s" % [
|
111
|
+
event.resource_status, event.resource_type,
|
112
|
+
event.logical_resource_id, event.resource_status_reason
|
113
|
+
]
|
114
|
+
end
|
115
|
+
|
116
|
+
raise "Operation failed. Please check the log." if has_failed && throw_if_failed
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
def generate_json
|
121
|
+
JSON.pretty_generate(@cfer_stack)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Cfer
|
2
|
+
module Auster
|
3
|
+
module CferHelpers
|
4
|
+
CFIZER_DEFAULT_CAPTURE_REGEXP = /C\{(?<directive>.*?)\}/
|
5
|
+
|
6
|
+
def eval_file(filename)
|
7
|
+
instance_eval IO.read(filename), filename
|
8
|
+
end
|
9
|
+
|
10
|
+
def import(name)
|
11
|
+
{ "Fn::ImportValue" => _exported_name(name) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def export(name, value)
|
15
|
+
output name, value, Export: { Name: _exported_name(name) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def _exported_name(name)
|
19
|
+
"#{parameters[:PlanID]}--#{name}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def cfize(text, capture_regexp: nil)
|
23
|
+
CferHelpers.cfize(text, capture_regexp: capture_regexp)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.cfize(text, capture_regexp: nil)
|
27
|
+
raise "'text' must be a string." unless text.is_a?(String)
|
28
|
+
|
29
|
+
capture_regexp ||= CFIZER_DEFAULT_CAPTURE_REGEXP
|
30
|
+
|
31
|
+
raise "'capture_regexp' must be a Regexp." unless capture_regexp.is_a?(Regexp)
|
32
|
+
raise "'capture_regexp' must include a 'contents' named 'directive'." \
|
33
|
+
unless capture_regexp.named_captures.key?("directive")
|
34
|
+
|
35
|
+
working = []
|
36
|
+
until working[-2] == "" && working[-1] == "" do
|
37
|
+
if working.empty?
|
38
|
+
working = text.partition(capture_regexp)
|
39
|
+
else
|
40
|
+
working[-1] = working[-1].partition(capture_regexp)
|
41
|
+
working = working.flatten
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
cfizer = Cfizer.new
|
46
|
+
Cfizer::Fn.join("", working.map do |token|
|
47
|
+
match = capture_regexp.match(token)
|
48
|
+
if match.nil?
|
49
|
+
token
|
50
|
+
else
|
51
|
+
cfizer.cfize(match["directive"])
|
52
|
+
end
|
53
|
+
end.reject { |t| t == ""})
|
54
|
+
end
|
55
|
+
|
56
|
+
class Cfizer
|
57
|
+
begin
|
58
|
+
include Cfer::Core::Functions
|
59
|
+
rescue NameError => _
|
60
|
+
# we need to fall back to the old Cfer setup
|
61
|
+
include Cfer::Core
|
62
|
+
include Cfer::Cfn
|
63
|
+
end
|
64
|
+
|
65
|
+
def cfize(directive)
|
66
|
+
instance_eval directive
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "cri"
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
module Cfer
|
6
|
+
module Auster
|
7
|
+
module CLI
|
8
|
+
def self.base_options(cmd)
|
9
|
+
cmd.instance_eval do
|
10
|
+
flag :v, :verbose, "sets logging to DEBUG" do |_, _|
|
11
|
+
Cfer::Auster::Logging.logger.level = Logger::DEBUG
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.standard_options(cmd)
|
17
|
+
cmd.instance_eval do
|
18
|
+
CLI.base_options(cmd)
|
19
|
+
|
20
|
+
flag :h, :help, "show help for this command" do |_, cmd|
|
21
|
+
puts cmd.help
|
22
|
+
Kernel.exit 0
|
23
|
+
end
|
24
|
+
|
25
|
+
option :l, :"log-level",
|
26
|
+
"Configures the verbosity of the Auster and Cfer loggers. (default: info)",
|
27
|
+
argument: :required
|
28
|
+
|
29
|
+
option :p, :"plan-path",
|
30
|
+
"The path to the Auster plan repo that should be used (otherwise searches from pwd)",
|
31
|
+
argument: :required
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.repo_from_options(opts, &block)
|
36
|
+
require "cfer/auster/repo"
|
37
|
+
|
38
|
+
repo =
|
39
|
+
if opts[:"plan-path"]
|
40
|
+
Cfer::Auster::Repo.new(opts[:"plan-path"])
|
41
|
+
else
|
42
|
+
Cfer::Auster::Repo.discover_from_cwd
|
43
|
+
end
|
44
|
+
|
45
|
+
block.call(repo)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "cri"
|
3
|
+
|
4
|
+
require "cfer/auster/cli/_shared"
|
5
|
+
|
6
|
+
module Cfer
|
7
|
+
module Auster
|
8
|
+
module CLI
|
9
|
+
def self.destroy
|
10
|
+
Cri::Command.define do
|
11
|
+
name "destroy"
|
12
|
+
usage "destroy aws-region/config-set count-or-tag"
|
13
|
+
description "Destroys this Auster step in your AWS account."
|
14
|
+
|
15
|
+
CLI.standard_options(self)
|
16
|
+
|
17
|
+
run do |opts, args, cmd|
|
18
|
+
if args.length < 2
|
19
|
+
puts cmd.help
|
20
|
+
exit 1
|
21
|
+
else
|
22
|
+
CLI.repo_from_options(opts) do |repo|
|
23
|
+
config_set = repo.config_set(args[0])
|
24
|
+
step = repo.step_by_count_or_tag(args[1])
|
25
|
+
|
26
|
+
step.destroy(config_set)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "cri"
|
3
|
+
|
4
|
+
module Cfer
|
5
|
+
module Auster
|
6
|
+
module CLI
|
7
|
+
def self.generate_repo
|
8
|
+
Cri::Command.define do
|
9
|
+
name "repo"
|
10
|
+
usage "repo OUTPUT_PATH"
|
11
|
+
description "Generates a new Auster plan repo."
|
12
|
+
|
13
|
+
CLI.base_options(self)
|
14
|
+
|
15
|
+
flag :h, :help, "show help for this command" do |_, cmd|
|
16
|
+
puts cmd.help
|
17
|
+
Kernel.exit 0
|
18
|
+
end
|
19
|
+
|
20
|
+
run do |_, _, cmd|
|
21
|
+
raise "TODO: implement"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "cri"
|
3
|
+
|
4
|
+
module Cfer
|
5
|
+
module Auster
|
6
|
+
module CLI
|
7
|
+
def self.generate_step
|
8
|
+
Cri::Command.define do
|
9
|
+
name "step"
|
10
|
+
usage "step ##"
|
11
|
+
description "Generates a step in the current Auster repo."
|
12
|
+
|
13
|
+
CLI.base_options(self)
|
14
|
+
|
15
|
+
flag :h, :help, "show help for this command" do |_, cmd|
|
16
|
+
puts cmd.help
|
17
|
+
Kernel.exit 0
|
18
|
+
end
|
19
|
+
|
20
|
+
run do |_, _, cmd|
|
21
|
+
raise "TODO: implement"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "cri"
|
3
|
+
|
4
|
+
require "cfer/auster/cli/generate/repo"
|
5
|
+
require "cfer/auster/cli/generate/step"
|
6
|
+
|
7
|
+
module Cfer
|
8
|
+
module Auster
|
9
|
+
module CLI
|
10
|
+
def self.generate
|
11
|
+
ret = Cri::Command.define do
|
12
|
+
name "generate"
|
13
|
+
description "Encapsulates generators for Auster."
|
14
|
+
|
15
|
+
CLI.base_options(self)
|
16
|
+
|
17
|
+
flag :h, :help, "show help for this command" do |_, cmd|
|
18
|
+
puts cmd.help
|
19
|
+
Kernel.exit 0
|
20
|
+
end
|
21
|
+
|
22
|
+
run do |_, _, cmd|
|
23
|
+
puts cmd.help
|
24
|
+
Kernel.exit 0
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
ret.add_command(CLI.generate_repo)
|
29
|
+
ret.add_command(CLI.generate_step)
|
30
|
+
|
31
|
+
ret
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "cri"
|
3
|
+
|
4
|
+
require "cfer/auster/cli/_shared"
|
5
|
+
|
6
|
+
module Cfer
|
7
|
+
module Auster
|
8
|
+
module CLI
|
9
|
+
def self.json
|
10
|
+
Cri::Command.define do
|
11
|
+
name "json"
|
12
|
+
usage "json aws-region/config-set count-or-tag"
|
13
|
+
description "Generates the CloudFormation JSON for this step."
|
14
|
+
|
15
|
+
CLI.standard_options(self)
|
16
|
+
|
17
|
+
option :o, :"output-file",
|
18
|
+
"Saves the JSON output to a file (otherwise prints to stdout)",
|
19
|
+
argument: :required
|
20
|
+
|
21
|
+
run do |opts, args, cmd|
|
22
|
+
if args.length < 2
|
23
|
+
puts cmd.help
|
24
|
+
exit 1
|
25
|
+
else
|
26
|
+
CLI.repo_from_options(opts) do |repo|
|
27
|
+
config_set = repo.config_set(args[0])
|
28
|
+
step = repo.step_by_count_or_tag(args[1])
|
29
|
+
|
30
|
+
ret = step.json(config_set)
|
31
|
+
|
32
|
+
if opts[:"output-file"]
|
33
|
+
IO.write(opts[:"output-file"], ret)
|
34
|
+
else
|
35
|
+
puts ret
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "cri"
|
3
|
+
|
4
|
+
require "cfer/auster/cli/_shared"
|
5
|
+
|
6
|
+
module Cfer
|
7
|
+
module Auster
|
8
|
+
module CLI
|
9
|
+
def self.nuke
|
10
|
+
Cri::Command.define do
|
11
|
+
extend Cfer::Auster::Logging::Mixin
|
12
|
+
|
13
|
+
name "nuke"
|
14
|
+
usage "nuke aws-region/config-set"
|
15
|
+
description "Destroys ALL AWS RESOURCES related to this config set."
|
16
|
+
|
17
|
+
CLI.standard_options(self)
|
18
|
+
|
19
|
+
flag nil, :force, "bypasses confirmation - use with care!"
|
20
|
+
|
21
|
+
run do |opts, args, cmd|
|
22
|
+
if args.length < 1
|
23
|
+
puts cmd.help
|
24
|
+
exit 1
|
25
|
+
else
|
26
|
+
CLI.repo_from_options(opts) do |repo|
|
27
|
+
config_set = repo.config_set(args[0])
|
28
|
+
|
29
|
+
accepted = !!opts[:force]
|
30
|
+
|
31
|
+
if !accepted && $stdin.tty?
|
32
|
+
$stderr.write "\n\n"
|
33
|
+
$stderr.write "!!! YOU ARE ABOUT TO DO SOMETHING VERY DRASTIC! !!!\n"
|
34
|
+
$stderr.write "You are requesting to destroy ALL STEPS of the config set '#{config_set.full_name}'.\n"
|
35
|
+
$stderr.write "If you are certain you wish to do this, please type CONFIRM: "
|
36
|
+
|
37
|
+
input = $stdin.readline.chomp
|
38
|
+
|
39
|
+
if input != "CONFIRM"
|
40
|
+
$stderr.write "\n\nInvalid input. Aborting nuke.\n\n"
|
41
|
+
Kernel.exit 1
|
42
|
+
end
|
43
|
+
|
44
|
+
accepted = true
|
45
|
+
end
|
46
|
+
|
47
|
+
unless accepted
|
48
|
+
logger.error "You must pass interactive confirmation or use the --force parameter to nuke."
|
49
|
+
Kernel.exit 1
|
50
|
+
end
|
51
|
+
|
52
|
+
repo.nuke(config_set)
|
53
|
+
|
54
|
+
logger.warn "I really, really hope you meant to do that."
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "cri"
|
3
|
+
|
4
|
+
require "cfer/auster/cli/_shared"
|
5
|
+
|
6
|
+
module Cfer
|
7
|
+
module Auster
|
8
|
+
module CLI
|
9
|
+
def self.run
|
10
|
+
Cri::Command.define do
|
11
|
+
name "run"
|
12
|
+
usage "run aws-region/config-set count-or-tag"
|
13
|
+
description "Runs this Auster step against your AWS infrastructure."
|
14
|
+
|
15
|
+
CLI.standard_options(self)
|
16
|
+
|
17
|
+
run do |opts, args, cmd|
|
18
|
+
if args.length < 2
|
19
|
+
puts cmd.help
|
20
|
+
exit 1
|
21
|
+
else
|
22
|
+
CLI.repo_from_options(opts) do |repo|
|
23
|
+
config_set = repo.config_set(args[0])
|
24
|
+
step = repo.step_by_count_or_tag(args[1])
|
25
|
+
|
26
|
+
step.run(config_set)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "cri"
|
3
|
+
require "semantic"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
require "cfer/auster"
|
7
|
+
require "cfer/auster/cli/_shared"
|
8
|
+
require "cfer/auster/cli/generate"
|
9
|
+
require "cfer/auster/cli/json"
|
10
|
+
require "cfer/auster/cli/run"
|
11
|
+
require "cfer/auster/cli/destroy"
|
12
|
+
require "cfer/auster/cli/nuke"
|
13
|
+
|
14
|
+
module Cfer
|
15
|
+
module Auster
|
16
|
+
module CLI
|
17
|
+
def self.root
|
18
|
+
ret = Cri::Command.define do
|
19
|
+
name "auster"
|
20
|
+
description "The best way to manage CloudFormation. Ever. (We think.)"
|
21
|
+
|
22
|
+
CLI.base_options(self)
|
23
|
+
|
24
|
+
flag :h, :help, "show help for this command" do |_, cmd|
|
25
|
+
puts cmd.help
|
26
|
+
Kernel.exit 0
|
27
|
+
end
|
28
|
+
|
29
|
+
flag nil, :version, "show version information for this command" do |_, _|
|
30
|
+
puts Cfer::Auster::VERSION
|
31
|
+
Kernel.exit 0
|
32
|
+
end
|
33
|
+
flag nil, :"version-json", "show version information for this command in JSON" do |_, _|
|
34
|
+
puts JSON.pretty_generate(
|
35
|
+
Semantic::Version.new(Cfer::Auster::VERSION).to_h.reject { |_, v| v.nil? }
|
36
|
+
)
|
37
|
+
Kernel.exit 0
|
38
|
+
end
|
39
|
+
|
40
|
+
run do |_, _, cmd|
|
41
|
+
puts cmd.help
|
42
|
+
Kernel.exit 0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
ret.add_command(CLI.generate)
|
47
|
+
ret.add_command(CLI.json)
|
48
|
+
ret.add_command(CLI.run)
|
49
|
+
ret.add_command(CLI.destroy)
|
50
|
+
ret.add_command(CLI.nuke)
|
51
|
+
|
52
|
+
ret
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.execute(args)
|
56
|
+
CLI.root.run(args)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "kwalify"
|
3
|
+
|
4
|
+
module Cfer
|
5
|
+
module Auster
|
6
|
+
class Config
|
7
|
+
include Cfer::Auster::Logging::Mixin
|
8
|
+
|
9
|
+
attr_reader :name
|
10
|
+
attr_reader :aws_region
|
11
|
+
attr_reader :data
|
12
|
+
|
13
|
+
def initialize(name:, aws_region:, data:)
|
14
|
+
raise "name must be a String" unless name.is_a?(String)
|
15
|
+
raise "aws_region must be a String" unless aws_region.is_a?(String)
|
16
|
+
raise "data must be a Hash" unless data.is_a?(Hash)
|
17
|
+
|
18
|
+
@name = name.dup.freeze
|
19
|
+
@aws_region = aws_region.dup.freeze
|
20
|
+
@data = data.deep_symbolize_keys
|
21
|
+
|
22
|
+
@data[:PlanID] = @name
|
23
|
+
@data[:AWSRegion] = @aws_region
|
24
|
+
|
25
|
+
IceNine.deep_freeze(@data)
|
26
|
+
end
|
27
|
+
|
28
|
+
def full_name
|
29
|
+
"#{aws_region}/#{name}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def env_vars_for_shell
|
33
|
+
{
|
34
|
+
"PLAN_ID" => name,
|
35
|
+
"AWS_REGION" => aws_region,
|
36
|
+
"AWS_DEFAULT_REGION" => aws_region
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
include Cfer::Auster::Logging::Mixin
|
42
|
+
|
43
|
+
def from_file(name:, aws_region:, data_file:, schema_file:)
|
44
|
+
logger.debug "Loading config set from #{data_file}"
|
45
|
+
schema = schema_file.nil? ? nil : Kwalify::Yaml.load_file(schema_file)
|
46
|
+
validator = schema.nil? ? nil : Kwalify::Validator.new(schema)
|
47
|
+
|
48
|
+
parser = Kwalify::Yaml::Parser.new(validator)
|
49
|
+
|
50
|
+
data = parser.parse_file(data_file)
|
51
|
+
errors = parser.errors()
|
52
|
+
|
53
|
+
if errors && !errors.empty?
|
54
|
+
# TODO: make a better error to raise that can encapsulate these validation failures.
|
55
|
+
msg = "Schema validation failed for #{data_file}."
|
56
|
+
|
57
|
+
logger.error "Schema validation failed for #{data_file}."
|
58
|
+
errors.each do |e|
|
59
|
+
logger.error "#{e.linenum}:#{e.column} [#{e.path}] #{e.message}"
|
60
|
+
end
|
61
|
+
|
62
|
+
raise msg
|
63
|
+
end
|
64
|
+
|
65
|
+
Config.new(name: name, aws_region: aws_region, data: data)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
module Cfer
|
6
|
+
module Auster
|
7
|
+
module Logging
|
8
|
+
def self.logger
|
9
|
+
@logger
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.logdev
|
13
|
+
@logdev
|
14
|
+
end
|
15
|
+
|
16
|
+
# rubocop:disable Style/AccessorMethodName
|
17
|
+
def self.set_logdev(logdev)
|
18
|
+
@logdev = logdev
|
19
|
+
@logger = Logger.new(@logdev)
|
20
|
+
end
|
21
|
+
|
22
|
+
set_logdev($stderr)
|
23
|
+
@logger.level = Logger::INFO
|
24
|
+
|
25
|
+
module Mixin
|
26
|
+
def logger
|
27
|
+
Cfer::Auster::Logging.logger
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cfer
|
4
|
+
module Auster
|
5
|
+
class ParamValidator
|
6
|
+
def initialize(&validator)
|
7
|
+
raise "validator must be a Proc." unless validator.is_a?(Proc)
|
8
|
+
raise "validator must be arity 2." unless validator.arity == 2
|
9
|
+
|
10
|
+
@validator = validator
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate(parameters)
|
14
|
+
raise "parameters must be a Hash." unless parameters.is_a?(Hash)
|
15
|
+
|
16
|
+
errors = []
|
17
|
+
@validator.call(parameters, errors)
|
18
|
+
errors
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|