terraspace 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +30 -0
- data/README.md +57 -30
- data/lib/templates/base/arg/terraform.rb.tt +3 -0
- data/lib/templates/base/helper/%name%_helper.rb.tt +2 -0
- data/lib/templates/base/hook/%kind%.rb.tt +7 -0
- data/lib/terraspace.rb +7 -4
- data/lib/terraspace/all/base.rb +0 -1
- data/lib/terraspace/app.rb +1 -0
- data/lib/terraspace/autoloader.rb +20 -3
- data/lib/terraspace/cli.rb +2 -0
- data/lib/terraspace/cli/all.rb +1 -1
- data/lib/terraspace/cli/bundle.rb +0 -1
- data/lib/terraspace/cli/clean/base.rb +0 -1
- data/lib/terraspace/cli/clean/cache.rb +0 -1
- data/lib/terraspace/cli/cloud.rb +2 -0
- data/lib/terraspace/cli/help/all/init.md +33 -0
- data/lib/terraspace/cli/help/logs.md +4 -4
- data/lib/terraspace/cli/help/new/arg.md +19 -0
- data/lib/terraspace/cli/help/new/helper.md +39 -0
- data/lib/terraspace/cli/help/new/hook.md +25 -0
- data/lib/terraspace/cli/help/new/test.md +34 -0
- data/lib/terraspace/cli/info.rb +12 -0
- data/lib/terraspace/cli/list.rb +2 -1
- data/lib/terraspace/cli/logs/concern.rb +1 -0
- data/lib/terraspace/cli/new.rb +22 -16
- data/lib/terraspace/cli/new/arg.rb +62 -0
- data/lib/terraspace/cli/new/helper.rb +44 -12
- data/lib/terraspace/cli/new/helpers.rb +22 -0
- data/lib/terraspace/cli/new/helpers/plugin_gem.rb +25 -0
- data/lib/terraspace/cli/new/hook.rb +70 -0
- data/lib/terraspace/cli/new/module.rb +0 -11
- data/lib/terraspace/cli/new/plugin.rb +4 -4
- data/lib/terraspace/cli/new/plugin/helper.rb +1 -1
- data/lib/terraspace/cli/new/project.rb +16 -7
- data/lib/terraspace/cli/new/sequence.rb +3 -10
- data/lib/terraspace/cli/new/source/core.rb +1 -1
- data/lib/terraspace/cli/new/stack.rb +1 -12
- data/lib/terraspace/cli/new/test.rb +50 -0
- data/lib/terraspace/cli/summary.rb +0 -1
- data/lib/terraspace/command.rb +16 -0
- data/lib/terraspace/compiler/builder.rb +2 -0
- data/lib/terraspace/compiler/dsl/mod.rb +2 -0
- data/lib/terraspace/compiler/dsl/syntax/mod.rb +2 -0
- data/lib/terraspace/compiler/dsl/syntax/mod/backend.rb +1 -1
- data/lib/terraspace/compiler/erb/context.rb +2 -0
- data/lib/terraspace/compiler/helper_extender.rb +27 -0
- data/lib/terraspace/core.rb +0 -6
- data/lib/terraspace/ext/core/module.rb +16 -0
- data/lib/terraspace/hooks/builder.rb +6 -7
- data/lib/terraspace/hooks/concern.rb +2 -2
- data/lib/terraspace/logger.rb +6 -0
- data/lib/terraspace/mod.rb +0 -1
- data/lib/terraspace/plugin.rb +8 -4
- data/lib/terraspace/plugin/config/interface.rb +2 -2
- data/lib/terraspace/plugin/helper/interface.rb +31 -0
- data/lib/terraspace/shell.rb +5 -33
- data/lib/terraspace/shell/error.rb +46 -0
- data/lib/terraspace/terraform/args/custom.rb +2 -3
- data/lib/terraspace/terraform/args/default.rb +9 -19
- data/lib/terraspace/terraform/runner.rb +6 -14
- data/lib/terraspace/terraform/runner/retryer.rb +69 -0
- data/lib/terraspace/version.rb +1 -1
- data/spec/terraspace/terraform/args/custom_spec.rb +6 -4
- data/terraspace.gemspec +4 -4
- metadata +30 -21
- data/lib/terraspace/cli/help/new/bootstrap_test.md +0 -8
- data/lib/terraspace/cli/help/new/module_test.md +0 -12
- data/lib/terraspace/cli/help/new/project_test.md +0 -8
- data/lib/terraspace/cli/new/helper/plugin_gem.rb +0 -12
- data/lib/terraspace/cli/new/test/base.rb +0 -17
- data/lib/terraspace/cli/new/test/bootstrap.rb +0 -18
- data/lib/terraspace/cli/new/test/module.rb +0 -15
- data/lib/terraspace/cli/new/test/project.rb +0 -15
@@ -4,16 +4,23 @@ class Terraspace::CLI::New
|
|
4
4
|
[
|
5
5
|
[:bundle, type: :boolean, default: true, desc: "Runs bundle install on the project"],
|
6
6
|
[:config, type: :boolean, default: true, desc: "Whether or not to generate config files."],
|
7
|
-
[:force, type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files."],
|
8
|
-
[:
|
7
|
+
[:force, aliases: %w[y], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files."],
|
8
|
+
[:quiet, type: :boolean, desc: "Quiet output."],
|
9
|
+
[:test_structure, type: :boolean, desc: "Create project bootstrap test structure."],
|
9
10
|
]
|
10
11
|
end
|
11
12
|
|
12
13
|
base_options.each { |args| class_option(*args) }
|
13
14
|
project_options.each { |args| class_option(*args) }
|
14
15
|
|
16
|
+
private
|
17
|
+
def log(msg)
|
18
|
+
logger.info(msg) unless @options[:quiet]
|
19
|
+
end
|
20
|
+
|
21
|
+
public
|
15
22
|
def creating_messaging
|
16
|
-
|
23
|
+
log "=> Creating new project called #{name}."
|
17
24
|
end
|
18
25
|
|
19
26
|
def create_base
|
@@ -60,15 +67,17 @@ class Terraspace::CLI::New
|
|
60
67
|
|
61
68
|
def bundle_install
|
62
69
|
return if @options[:bundle] == false
|
63
|
-
|
70
|
+
log "=> Installing dependencies with: bundle install"
|
64
71
|
Bundler.with_unbundled_env do
|
65
|
-
|
72
|
+
bundle = "BUNDLE_IGNORE_CONFIG=1 bundle install"
|
73
|
+
bundle << " > /dev/null 2>&1" if @options[:quiet]
|
74
|
+
system(bundle, chdir: name)
|
66
75
|
end
|
67
76
|
end
|
68
77
|
|
69
78
|
def welcome_message_examples
|
70
79
|
return unless options[:examples]
|
71
|
-
|
80
|
+
log <<~EOL
|
72
81
|
#{"="*64}
|
73
82
|
Congrats! You have successfully created a terraspace project.
|
74
83
|
Check out the created files. Adjust to the examples and then deploy with:
|
@@ -83,7 +92,7 @@ class Terraspace::CLI::New
|
|
83
92
|
|
84
93
|
def welcome_message_no_examples
|
85
94
|
return if options[:examples]
|
86
|
-
|
95
|
+
log <<~EOL
|
87
96
|
#{"="*64}
|
88
97
|
Congrats! You have successfully created a terraspace project.
|
89
98
|
Check out the created files.
|
@@ -3,12 +3,13 @@ require 'thor'
|
|
3
3
|
class Terraspace::CLI::New
|
4
4
|
class Sequence < Thor::Group
|
5
5
|
include Thor::Actions
|
6
|
-
include
|
6
|
+
include Terraspace::Util::Logging
|
7
|
+
include Helpers
|
7
8
|
|
8
9
|
def self.base_options
|
9
10
|
[
|
10
11
|
[:examples, type: :boolean, default: false, desc: "Also generate examples"],
|
11
|
-
[:force, type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
|
12
|
+
[:force, aliases: %w[y], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
|
12
13
|
[:lang, default: "hcl", desc: "Language to use: HCL/ERB or Ruby DSL"],
|
13
14
|
[:plugin, aliases: %w[p], default: "aws", desc: "Cloud Plugin. Supports: aws, google"],
|
14
15
|
[:test, type: :boolean, desc: "Whether or not to generate tests"],
|
@@ -47,13 +48,5 @@ class Terraspace::CLI::New
|
|
47
48
|
source = Source::Plugin.new(self, @options)
|
48
49
|
source.set_source_paths(template, type)
|
49
50
|
end
|
50
|
-
|
51
|
-
# A generator script hook to allow for further customizations
|
52
|
-
# The dest folder like app/modules/demo is provided as a first argument to the command.
|
53
|
-
def run_script(script, dest)
|
54
|
-
command = "#{script} #{dest}"
|
55
|
-
puts "Running: #{command}" unless ENV['TS_GENERATOR_MUTE']
|
56
|
-
system(command)
|
57
|
-
end
|
58
51
|
end
|
59
52
|
end
|
@@ -5,22 +5,11 @@ class Terraspace::CLI::New
|
|
5
5
|
argument :name
|
6
6
|
|
7
7
|
def create_stack
|
8
|
-
plugin_template_source(@options[:lang], "stack") # IE: plugin_template_source("hcl", "stack")
|
9
|
-
|
10
8
|
puts "=> Creating new stack called #{name}."
|
9
|
+
plugin_template_source(@options[:lang], "stack") # IE: plugin_template_source("hcl", "stack")
|
11
10
|
dest = "app/stacks/#{name}"
|
12
11
|
dest = "#{@options[:project_name]}/#{dest}" if @options[:project_name]
|
13
12
|
directory ".", dest
|
14
13
|
end
|
15
|
-
|
16
|
-
def create_test
|
17
|
-
Test::Project.start(component_args(name, @options[:project_name]))
|
18
|
-
end
|
19
|
-
|
20
|
-
def run_generator_hook_script
|
21
|
-
script = ENV['TS_GENERATOR_STACK']
|
22
|
-
return unless script
|
23
|
-
run_script(script, "app/stacks/#{name}")
|
24
|
-
end
|
25
14
|
end
|
26
15
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Terraspace::CLI::New
|
2
|
+
class Test < Thor::Group
|
3
|
+
include Thor::Actions
|
4
|
+
include Terraspace::CLI::New::Helpers
|
5
|
+
|
6
|
+
argument :name
|
7
|
+
|
8
|
+
def self.options
|
9
|
+
[
|
10
|
+
[:force, aliases: %w[y], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
|
11
|
+
[:test_name, desc: "Test name. Defaults to the project, module or stack name"],
|
12
|
+
[:type, default: "project", desc: "project, stack or module"],
|
13
|
+
]
|
14
|
+
end
|
15
|
+
options.each { |args| class_option(*args) }
|
16
|
+
|
17
|
+
private
|
18
|
+
def type
|
19
|
+
valid_types = %w[project stack module]
|
20
|
+
type = @options[:type]
|
21
|
+
valid_types.include?(type) ? type : "project" # fallback to project if user provides invalid type
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_name
|
25
|
+
options[:test_name] || name
|
26
|
+
end
|
27
|
+
|
28
|
+
def dest
|
29
|
+
map = {
|
30
|
+
project: ".", # Terraspace.root
|
31
|
+
stack: "app/stacks/#{name}",
|
32
|
+
module: "app/modules/#{name}",
|
33
|
+
}
|
34
|
+
map[type.to_sym]
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_template_source(template, type)
|
38
|
+
source = Terraspace::CLI::New::Source::Test.new(self, @options)
|
39
|
+
source.set_source_paths(template, type)
|
40
|
+
end
|
41
|
+
|
42
|
+
public
|
43
|
+
|
44
|
+
def create
|
45
|
+
test_template_source(@options[:lang], type)
|
46
|
+
puts "=> Creating #{type} test: #{name}"
|
47
|
+
directory ".", dest
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/terraspace/command.rb
CHANGED
@@ -27,7 +27,11 @@ Thor::Util.singleton_class.prepend(ThorPrepend::Util)
|
|
27
27
|
module Terraspace
|
28
28
|
class Command < Thor
|
29
29
|
class << self
|
30
|
+
include Terraspace::Util::Logging
|
31
|
+
|
30
32
|
def dispatch(m, args, options, config)
|
33
|
+
check_project!(args.first)
|
34
|
+
|
31
35
|
# Allow calling for help via:
|
32
36
|
# terraspace command help
|
33
37
|
# terraspace command -h
|
@@ -54,6 +58,18 @@ module Terraspace
|
|
54
58
|
super
|
55
59
|
end
|
56
60
|
|
61
|
+
def check_project!(command_name)
|
62
|
+
return if subcommand?
|
63
|
+
return if %w[-h -v completion completion_script help new test version].include?(command_name)
|
64
|
+
return if File.exist?("#{Terraspace.root}/config/app.rb")
|
65
|
+
logger.error "ERROR: It doesnt look like this is a terraspace project. Are you sure you are in a terraspace project?".color(:red)
|
66
|
+
ENV['TS_TEST'] ? raise : exit(1)
|
67
|
+
end
|
68
|
+
|
69
|
+
def subcommand?
|
70
|
+
!!caller.detect { |l| l.include?('in subcommand') }
|
71
|
+
end
|
72
|
+
|
57
73
|
# Override command_help to include the description at the top of the
|
58
74
|
# long_description.
|
59
75
|
def command_help(shell, command_name)
|
@@ -73,6 +73,8 @@ module Terraspace::Compiler
|
|
73
73
|
def skip?(src_path)
|
74
74
|
return true unless File.file?(src_path)
|
75
75
|
# certain folders will be skipped
|
76
|
+
src_path.include?("#{@mod.root}/config/args") ||
|
77
|
+
src_path.include?("#{@mod.root}/config/hooks") ||
|
76
78
|
src_path.include?("#{@mod.root}/test")
|
77
79
|
end
|
78
80
|
end
|
@@ -11,7 +11,7 @@ module Terraspace::Compiler::Dsl::Syntax::Mod
|
|
11
11
|
Terraspace::Compiler::Expander.new(@mod, backend_name).expand(props)
|
12
12
|
end
|
13
13
|
|
14
|
-
# Can set opts to explicitly use an
|
14
|
+
# Can set opts to explicitly use an specific backend. Example:
|
15
15
|
#
|
16
16
|
# opts = {backend: s3}
|
17
17
|
#
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module Terraspace::Compiler::Erb
|
2
2
|
class Context
|
3
3
|
include Helpers
|
4
|
+
include Terraspace::Compiler::HelperExtender
|
4
5
|
|
5
6
|
attr_reader :mod, :options
|
6
7
|
def initialize(mod)
|
7
8
|
@mod = mod
|
8
9
|
@options = mod.options # so user has access to cli options
|
10
|
+
extend_module_level_helpers
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Terraspace::Compiler
|
2
|
+
module HelperExtender
|
3
|
+
private
|
4
|
+
def extend_module_level_helpers
|
5
|
+
full_dir = "#{@mod.root}/config/helpers"
|
6
|
+
Dir.glob("#{full_dir}/**/*").each do |path|
|
7
|
+
regexp = Regexp.new(".*/helpers/")
|
8
|
+
klass = path.sub(regexp, '').sub('.rb','').camelize
|
9
|
+
klass = "#{mod_namespace}::#{klass}"
|
10
|
+
require path # able to use require instead of load since each helper has unique namespace
|
11
|
+
send :extend, klass.constantize
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# IE: mod_namespace = Terraspace::Module::Demo
|
16
|
+
# Use separate namespaces scope with module name so custom helper methods from different modules are isolated.
|
17
|
+
def mod_namespace
|
18
|
+
mod_name = @mod.name.camelize
|
19
|
+
ns = "Terraspace::#{@mod.type.camelize}".constantize # IE: Terraspace::Module or Terraspace::Stack
|
20
|
+
if ns.const_defined?(mod_name.to_sym)
|
21
|
+
"#{ns}::#{mod_name}".constantize
|
22
|
+
else
|
23
|
+
ns.const_set(mod_name, Module.new)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/terraspace/core.rb
CHANGED
@@ -51,11 +51,5 @@ module Terraspace
|
|
51
51
|
def logger=(v)
|
52
52
|
@@logger = v
|
53
53
|
end
|
54
|
-
|
55
|
-
def check_project!
|
56
|
-
return if File.exist?("#{Terraspace.root}/config/app.rb")
|
57
|
-
logger.error "ERROR: It doesnt look like this is a terraspace project. Are you sure you are in a terraspace project?".color(:red)
|
58
|
-
ENV['TS_TEST'] ? raise : exit(1)
|
59
|
-
end
|
60
54
|
end
|
61
55
|
end
|
@@ -16,4 +16,20 @@ class Module
|
|
16
16
|
include klass.constantize
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
def include_project_level_helpers
|
21
|
+
full_dir = "#{Terraspace.root}/config/helpers"
|
22
|
+
Dir.glob("#{full_dir}/**/*").each do |path|
|
23
|
+
regexp = Regexp.new(".*/config/helpers/")
|
24
|
+
klass = path.sub(regexp, '').sub('.rb','').camelize
|
25
|
+
klass = "Terraspace::Project::#{klass}"
|
26
|
+
include klass.constantize
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def include_plugin_helpers
|
31
|
+
Terraspace::Plugin.helper_classes.each do |klass|
|
32
|
+
include klass # IE: TerraspacePluginAws::Interfaces::Helper
|
33
|
+
end
|
34
|
+
end
|
19
35
|
end
|
@@ -7,14 +7,14 @@ module Terraspace::Hooks
|
|
7
7
|
|
8
8
|
# IE: dsl_file: config/hooks/terraform.rb
|
9
9
|
attr_accessor :name
|
10
|
-
def initialize(mod,
|
11
|
-
@mod, @
|
10
|
+
def initialize(mod, file, name)
|
11
|
+
@mod, @file, @name = mod, file, name
|
12
12
|
@hooks = {before: {}, after: {}}
|
13
13
|
end
|
14
14
|
|
15
15
|
def build
|
16
|
-
|
17
|
-
evaluate_file(@
|
16
|
+
evaluate_file("#{Terraspace.root}/config/hooks/#{@file}")
|
17
|
+
evaluate_file("#{@mod.root}/config/hooks/#{@file}")
|
18
18
|
@hooks.deep_stringify_keys!
|
19
19
|
end
|
20
20
|
memoize :build
|
@@ -37,11 +37,10 @@ module Terraspace::Hooks
|
|
37
37
|
def run_hook(type, hook)
|
38
38
|
return unless run?(hook)
|
39
39
|
|
40
|
-
command = File.basename(@
|
40
|
+
command = File.basename(@file).sub('.rb','') # IE: terraform or terraspace
|
41
41
|
id = "#{command} #{type} #{@name}"
|
42
42
|
label = " label: #{hook["label"]}" if hook["label"]
|
43
|
-
logger.info "Running #{id} hook.#{label}"
|
44
|
-
logger.debug "Hook options: #{hook}"
|
43
|
+
logger.info "Hook: Running #{id} hook.#{label}".color(:cyan)
|
45
44
|
Runner.new(@mod, hook).run
|
46
45
|
end
|
47
46
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Terraspace::Hooks
|
2
2
|
module Concern
|
3
|
-
def run_hooks(
|
4
|
-
hooks = Builder.new(@mod,
|
3
|
+
def run_hooks(file, name, &block)
|
4
|
+
hooks = Builder.new(@mod, file, name)
|
5
5
|
hooks.build # build hooks
|
6
6
|
hooks.run_hooks(&block)
|
7
7
|
end
|
data/lib/terraspace/logger.rb
CHANGED
@@ -2,6 +2,12 @@ require 'logger'
|
|
2
2
|
|
3
3
|
module Terraspace
|
4
4
|
class Logger < ::Logger
|
5
|
+
def initialize(*args)
|
6
|
+
super
|
7
|
+
self.formatter = Formatter.new
|
8
|
+
self.level = :info
|
9
|
+
end
|
10
|
+
|
5
11
|
def format_message(severity, datetime, progname, msg)
|
6
12
|
line = if @logdev.dev == $stdout || @logdev.dev == $stderr
|
7
13
|
msg # super simple format if stdout
|
data/lib/terraspace/mod.rb
CHANGED
data/lib/terraspace/plugin.rb
CHANGED
@@ -16,14 +16,18 @@ module Terraspace
|
|
16
16
|
@@meta
|
17
17
|
end
|
18
18
|
|
19
|
-
def layer_classes
|
20
|
-
@@meta.map { |plugin, data| data[:layer_class] }.compact
|
21
|
-
end
|
22
|
-
|
23
19
|
def config_classes
|
24
20
|
@@meta.map { |plugin, data| data[:config_class] }.compact
|
25
21
|
end
|
26
22
|
|
23
|
+
def helper_classes
|
24
|
+
@@meta.map { |plugin, data| data[:helper_class] }.compact
|
25
|
+
end
|
26
|
+
|
27
|
+
def layer_classes
|
28
|
+
@@meta.map { |plugin, data| data[:layer_class] }.compact
|
29
|
+
end
|
30
|
+
|
27
31
|
# The resource map can be used to customized the mapping from the resource "first word" to the plugin.
|
28
32
|
#
|
29
33
|
# resource map is in meta structure.
|
@@ -16,8 +16,8 @@ module Terraspace::Plugin::Config
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def load_project_config
|
19
|
-
|
20
|
-
evaluate_file(
|
19
|
+
evaluate_file("#{Terraspace.root}/config/plugins/#{provider}.rb")
|
20
|
+
evaluate_file("#{Terraspace.root}/config/plugins/#{provider}/#{Terraspace.env}.rb")
|
21
21
|
end
|
22
22
|
|
23
23
|
def configure
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Terraspace::Plugin::Helper
|
2
|
+
module Interface
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
# Useful for plugin helpers. Can check this only run logic after dependency resolution.
|
6
|
+
def resolved?
|
7
|
+
!!@mod.resolved
|
8
|
+
end
|
9
|
+
|
10
|
+
class_methods do
|
11
|
+
@@helper_cache = {}
|
12
|
+
# This method is useful to avoid double call of heavy processing logic for tfvars,
|
13
|
+
# since the tfvars files get evaluated twice.
|
14
|
+
# Note: Not setting any cache or doing any logic unless resolved.
|
15
|
+
def cache_helper(meth)
|
16
|
+
uncached_meth = "uncached_#{meth}"
|
17
|
+
alias_method(uncached_meth, meth)
|
18
|
+
define_method(meth) do |*args|
|
19
|
+
return unless resolved? # return nil in first unresolved pass
|
20
|
+
id = Marshal.dump([meth] + args)
|
21
|
+
exist = @@helper_cache.key?(id)
|
22
|
+
if exist
|
23
|
+
@@helper_cache[id]
|
24
|
+
else
|
25
|
+
@@helper_cache[id] = send(uncached_meth, *args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|