auster 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.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
|