terraspace 0.6.23 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.cody/README.md +2 -0
- data/.cody/azurerm/bin/az/configure.sh +5 -1
- data/.cody/azurerm/project.rb +4 -0
- data/.cody/azurerm/role.rb +0 -1
- data/.cody/google/bin/gcloud/configure.sh +2 -1
- data/.cody/google/project.rb +4 -0
- data/.cody/google/role.rb +0 -1
- data/.cody/none/bin/build.sh +15 -0
- data/.cody/none/buildspec.yml +10 -0
- data/.cody/none/project.rb +2 -0
- data/.cody/none/role.rb +4 -0
- data/.cody/shared/script/install/terraform.sh +14 -6
- data/.cody/unit/buildspec.yml +1 -0
- data/.pipedream/pipeline.rb +1 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile +6 -0
- data/README.md +1 -1
- data/lib/templates/examples/hcl/module/main.tf +3 -0
- data/lib/templates/examples/hcl/module/outputs.tf +4 -0
- data/lib/templates/examples/hcl/module/variables.tf +5 -0
- data/lib/templates/examples/hcl/stack/main.tf +4 -0
- data/lib/templates/examples/hcl/stack/outputs.tf +4 -0
- data/lib/templates/examples/hcl/stack/variables.tf +5 -0
- data/lib/templates/examples/ruby/module/main.rb +3 -0
- data/lib/templates/examples/ruby/module/outputs.rb +4 -0
- data/lib/templates/examples/ruby/module/variables.rb +5 -0
- data/lib/templates/examples/ruby/stack/main.rb +4 -0
- data/lib/templates/examples/ruby/stack/outputs.rb +4 -0
- data/lib/templates/examples/ruby/stack/variables.rb +5 -0
- data/lib/templates/hcl/project/config/terraform/backend.tf.tt +5 -13
- data/lib/templates/hcl/project/config/terraform/provider.tf +4 -14
- data/lib/terraspace/autodetect.rb +15 -0
- data/lib/terraspace/autoloader.rb +6 -1
- data/lib/terraspace/builder.rb +1 -9
- data/lib/terraspace/bundle.rb +54 -0
- data/lib/terraspace/cli/concern.rb +113 -0
- data/lib/terraspace/cli/help/new/example.md +7 -1
- data/lib/terraspace/cli/help.rb +7 -8
- data/lib/terraspace/cli/new/example.rb +36 -0
- data/lib/terraspace/cli/new/helpers/plugin_gem.rb +2 -14
- data/lib/terraspace/cli/new/helpers.rb +1 -1
- data/lib/terraspace/cli/new/module.rb +1 -1
- data/lib/terraspace/cli/new/plugin/helper.rb +1 -1
- data/lib/terraspace/cli/new/plugin.rb +1 -1
- data/lib/terraspace/cli/new/project.rb +11 -17
- data/lib/terraspace/cli/new/sequence.rb +1 -1
- data/lib/terraspace/cli/new/source/core.rb +7 -6
- data/lib/terraspace/cli/new/source/plugin.rb +2 -2
- data/lib/terraspace/cli/new/stack.rb +3 -2
- data/lib/terraspace/cli/new/test.rb +12 -2
- data/lib/terraspace/cli/new.rb +4 -0
- data/lib/terraspace/cli/{check_setup.rb → setup/check.rb} +5 -3
- data/lib/terraspace/cli/setup.rb +9 -0
- data/lib/terraspace/cli/tfc_concern.rb +1 -1
- data/lib/terraspace/cli.rb +34 -27
- data/lib/terraspace/command.rb +3 -2
- data/lib/terraspace/compiler/commands_concern.rb +0 -8
- data/lib/terraspace/compiler/expander/backend.rb +44 -0
- data/lib/terraspace/compiler/expander.rb +11 -19
- data/lib/terraspace/core.rb +10 -0
- data/lib/terraspace/plugin/expander/friendly.rb +1 -0
- data/lib/terraspace/plugin/expander/interface.rb +13 -2
- data/lib/terraspace/terraform/api/client.rb +1 -1
- data/lib/terraspace/terraform/args/custom.rb +11 -6
- data/lib/terraspace/terraform/args/pass.rb +110 -0
- data/lib/terraspace/terraform/args/{default.rb → thor.rb} +1 -3
- data/lib/terraspace/{compiler → terraform/runner}/backend/parser.rb +1 -1
- data/lib/terraspace/{compiler → terraform/runner}/backend.rb +14 -1
- data/lib/terraspace/terraform/runner/retryer.rb +1 -1
- data/lib/terraspace/terraform/runner.rb +25 -15
- data/lib/terraspace/terraform/tfc/sync.rb +1 -1
- data/lib/terraspace/version.rb +1 -1
- data/spec/terraspace/terraform/args/custom_spec.rb +4 -4
- data/spec/terraspace/terraform/args/pass_spec.rb +101 -0
- data/terraspace.gemspec +2 -4
- metadata +46 -50
@@ -0,0 +1,36 @@
|
|
1
|
+
class Terraspace::CLI::New
|
2
|
+
class Example < Thor::Group
|
3
|
+
include Thor::Actions
|
4
|
+
|
5
|
+
# only stack name is configurable
|
6
|
+
argument :name, default: "demo"
|
7
|
+
|
8
|
+
def self.options
|
9
|
+
# default is nil for autodetection
|
10
|
+
[
|
11
|
+
[:force, aliases: %w[f], type: :boolean, desc: "Force overwrite"],
|
12
|
+
[:lang, default: "hcl", desc: "Language to use: HCL/ERB or Ruby DSL"],
|
13
|
+
[:plugin, aliases: %w[p], default: nil, type: :string],
|
14
|
+
]
|
15
|
+
end
|
16
|
+
options.each { |args| class_option(*args) }
|
17
|
+
|
18
|
+
def create
|
19
|
+
Module.start(["example", "--examples"] + cli_args)
|
20
|
+
Stack.start([name, "--examples"] + cli_args)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def cli_args
|
25
|
+
plugin = @options[:plugin] || Terraspace::Autodetect.new.plugin
|
26
|
+
args = if plugin
|
27
|
+
['--plugin', plugin]
|
28
|
+
else
|
29
|
+
['--plugin', 'none'] # override default of aws
|
30
|
+
end
|
31
|
+
args << "--force" if @options[:force]
|
32
|
+
args += ["--lang", @options[:lang]] if @options[:lang]
|
33
|
+
args
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -5,20 +5,8 @@ module Terraspace::CLI::New::Helpers
|
|
5
5
|
if @options[:plugin_gem]
|
6
6
|
@options[:plugin_gem]
|
7
7
|
else
|
8
|
-
plugin = @options[:plugin] ||
|
9
|
-
"terraspace_plugin_#{plugin}"
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def autodetect_provider
|
14
|
-
providers = Terraspace::Plugin.meta.keys
|
15
|
-
if providers.size == 1
|
16
|
-
providers.first
|
17
|
-
else
|
18
|
-
precedence = %w[aws azurerm google]
|
19
|
-
precedence.find do |p|
|
20
|
-
providers.include?(p)
|
21
|
-
end
|
8
|
+
plugin = @options[:plugin] || Terraspace::Autodetect.new.plugin
|
9
|
+
"terraspace_plugin_#{plugin}" if plugin and plugin != "none"
|
22
10
|
end
|
23
11
|
end
|
24
12
|
end
|
@@ -5,7 +5,7 @@ class Terraspace::CLI::New
|
|
5
5
|
argument :name
|
6
6
|
|
7
7
|
def create_module
|
8
|
-
puts "=> Creating
|
8
|
+
puts "=> Creating new module called #{name}"
|
9
9
|
plugin_template_source(@options[:lang], "module") # IE: plugin_template_source("hcl", "module")
|
10
10
|
dest = "app/modules/#{name}"
|
11
11
|
dest = "#{@options[:project_name]}/#{dest}" if @options[:project_name]
|
@@ -6,7 +6,6 @@ class Terraspace::CLI::New
|
|
6
6
|
[:config, type: :boolean, default: true, desc: "Whether or not to generate config files."],
|
7
7
|
[:force, aliases: %w[y], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files."],
|
8
8
|
[:quiet, type: :boolean, desc: "Quiet output."],
|
9
|
-
[:test_structure, type: :boolean, desc: "Create project bootstrap test structure."],
|
10
9
|
]
|
11
10
|
end
|
12
11
|
|
@@ -20,7 +19,7 @@ class Terraspace::CLI::New
|
|
20
19
|
|
21
20
|
public
|
22
21
|
def creating_messaging
|
23
|
-
log "=> Creating new project called #{name}
|
22
|
+
log "=> Creating new project called #{name}"
|
24
23
|
end
|
25
24
|
|
26
25
|
def create_base
|
@@ -28,6 +27,16 @@ class Terraspace::CLI::New
|
|
28
27
|
directory ".", "#{name}"
|
29
28
|
end
|
30
29
|
|
30
|
+
def bundle_install
|
31
|
+
return if @options[:bundle] == false
|
32
|
+
log "=> Installing dependencies with: bundle install"
|
33
|
+
Bundler.with_unbundled_env do
|
34
|
+
bundle = "BUNDLE_IGNORE_CONFIG=1 bundle install"
|
35
|
+
bundle << " > /dev/null 2>&1" if @options[:quiet]
|
36
|
+
system(bundle, chdir: name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
31
40
|
# Will generate config folder from
|
32
41
|
#
|
33
42
|
# 1. terraspace code lang templates or
|
@@ -60,21 +69,6 @@ class Terraspace::CLI::New
|
|
60
69
|
Stack.start(component_args("demo", name))
|
61
70
|
end
|
62
71
|
|
63
|
-
def create_test
|
64
|
-
return unless @options[:test_structure]
|
65
|
-
Test::Bootstrap.start(["--dir", name])
|
66
|
-
end
|
67
|
-
|
68
|
-
def bundle_install
|
69
|
-
return if @options[:bundle] == false
|
70
|
-
log "=> Installing dependencies with: bundle install"
|
71
|
-
Bundler.with_unbundled_env do
|
72
|
-
bundle = "BUNDLE_IGNORE_CONFIG=1 bundle install"
|
73
|
-
bundle << " > /dev/null 2>&1" if @options[:quiet]
|
74
|
-
system(bundle, chdir: name)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
72
|
def welcome_message_examples
|
79
73
|
return unless options[:examples]
|
80
74
|
log <<~EOL
|
@@ -9,7 +9,8 @@ module Terraspace::CLI::New::Source
|
|
9
9
|
|
10
10
|
def set_core_source(template, type=nil)
|
11
11
|
template_name = template_name(template, type)
|
12
|
-
|
12
|
+
folder = @options[:examples] && type != "project" ? "examples/#{template_name}" : template_name
|
13
|
+
template_path = File.expand_path("../../../../templates/#{folder}", __dir__)
|
13
14
|
override_source_paths(template_path)
|
14
15
|
end
|
15
16
|
|
@@ -18,11 +19,14 @@ module Terraspace::CLI::New::Source
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def require_gem(name)
|
22
|
+
return unless name # can be passed in name=nil with rspec test harness
|
21
23
|
begin
|
24
|
+
# Need to clear gem paths since installing plugins like terraspace_plugin_aws as part of terraspace new project
|
25
|
+
Gem.clear_paths
|
22
26
|
require name # require plugin for the templates, this registers the plugin
|
23
27
|
rescue LoadError => e
|
24
28
|
puts "#{e.class}: #{e.message}".color(:red)
|
25
|
-
logger.error "ERROR: Unable to require plugin #{name}
|
29
|
+
logger.error "ERROR: Unable to require plugin #{name}".color(:red)
|
26
30
|
puts "Are you sure you the plugin exists and you specified the right plugin option."
|
27
31
|
puts "You specified --plugin #{@options[:plugin]}"
|
28
32
|
exit 1
|
@@ -32,10 +36,7 @@ module Terraspace::CLI::New::Source
|
|
32
36
|
def set_plugin_gem_source(template, type)
|
33
37
|
require_gem(plugin_gem_name)
|
34
38
|
plugin = Terraspace::Plugin.find_with(plugin: @options[:plugin])
|
35
|
-
unless plugin
|
36
|
-
puts "ERROR: Unable to a find plugin for #{@options[:plugin]}. Are you sure the gem for the plugin is correct?".color(:red)
|
37
|
-
exit 1
|
38
|
-
end
|
39
|
+
return unless plugin # can be passed in name=nil with rspec test harness
|
39
40
|
template_name = template_name(template, type)
|
40
41
|
template_path = File.expand_path("#{plugin.root}/lib/templates/#{template_name}")
|
41
42
|
override_source_paths(template_path)
|
@@ -9,12 +9,12 @@ module Terraspace::CLI::New::Source
|
|
9
9
|
# project always uses the examples from the provider gem for configs
|
10
10
|
# base always uses terraspace core templates
|
11
11
|
# examples option always use examples from provider gems
|
12
|
-
if (type == "project" || @options[:examples]) && template != "base"
|
12
|
+
if (type == "project" || @options[:examples]) && template != "base" &&
|
13
|
+
(@options[:plugin] != "none" && !@options[:plugin].nil?)
|
13
14
|
set_plugin_gem_source(template, type) # provider gems has examples
|
14
15
|
else
|
15
16
|
set_core_source(template, type) # terraspace core has empty starter files
|
16
17
|
end
|
17
18
|
end
|
18
|
-
|
19
19
|
end
|
20
20
|
end
|
@@ -2,10 +2,11 @@ class Terraspace::CLI::New
|
|
2
2
|
class Stack < Sequence
|
3
3
|
component_options.each { |args| class_option(*args) }
|
4
4
|
|
5
|
-
|
5
|
+
# default so terraspace new example works without a Thor warning
|
6
|
+
argument :name, default: "demo"
|
6
7
|
|
7
8
|
def create_stack
|
8
|
-
puts "=> Creating new stack called #{name}
|
9
|
+
puts "=> Creating new stack called #{name}"
|
9
10
|
plugin_template_source(@options[:lang], "stack") # IE: plugin_template_source("hcl", "stack")
|
10
11
|
dest = "app/stacks/#{name}"
|
11
12
|
dest = "#{@options[:project_name]}/#{dest}" if @options[:project_name]
|
@@ -3,13 +3,13 @@ class Terraspace::CLI::New
|
|
3
3
|
include Thor::Actions
|
4
4
|
include Terraspace::CLI::New::Helpers
|
5
5
|
|
6
|
-
argument :name
|
6
|
+
argument :name, required: false
|
7
7
|
|
8
8
|
def self.options
|
9
9
|
[
|
10
10
|
[:force, aliases: %w[y], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
|
11
11
|
[:test_name, desc: "Test name. Defaults to the project, module or stack name"],
|
12
|
-
[:type, default: "
|
12
|
+
[:type, default: "stack", desc: "project, stack or module"],
|
13
13
|
]
|
14
14
|
end
|
15
15
|
options.each { |args| class_option(*args) }
|
@@ -42,9 +42,19 @@ class Terraspace::CLI::New
|
|
42
42
|
public
|
43
43
|
|
44
44
|
def create
|
45
|
+
if type != 'project' && name.nil?
|
46
|
+
puts "ERROR: require NAME for type stack and module".color(:red)
|
47
|
+
exit 1
|
48
|
+
end
|
45
49
|
test_template_source(@options[:lang], type)
|
46
50
|
puts "=> Creating #{type} test: #{name}"
|
47
51
|
directory ".", dest
|
48
52
|
end
|
53
|
+
|
54
|
+
# Used in ERB template
|
55
|
+
# rspec-terraspace/lib/templates/stack/test/spec/fixtures/config/terraform/provider.tf.tt
|
56
|
+
def autodetect_provider
|
57
|
+
Terraspace::Autodetect.new.plugin
|
58
|
+
end
|
49
59
|
end
|
50
60
|
end
|
data/lib/terraspace/cli/new.rb
CHANGED
@@ -42,5 +42,9 @@ class Terraspace::CLI
|
|
42
42
|
long_desc Help.text("new/test")
|
43
43
|
Test.options.each { |args| option(*args) }
|
44
44
|
register(Test, "test", "test NAME", "Generates new test.")
|
45
|
+
|
46
|
+
long_desc Help.text("new/example")
|
47
|
+
Example.options.each { |args| option(*args) }
|
48
|
+
register(Example, "example", "example [NAME]", "Generates new example.")
|
45
49
|
end
|
46
50
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
class Terraspace::CLI
|
2
|
-
class
|
1
|
+
class Terraspace::CLI::Setup
|
2
|
+
class Check
|
3
3
|
extend Memoist
|
4
4
|
|
5
5
|
# Terraspace requires at least this version of terraform
|
@@ -83,7 +83,9 @@ class Terraspace::CLI
|
|
83
83
|
#
|
84
84
|
# Note: The -json option is only available in v0.13+
|
85
85
|
def terraform_version_message
|
86
|
-
`terraform --version
|
86
|
+
out = `terraform --version`
|
87
|
+
version = out.split("\n").find { |l| l =~ /^Terraform / }
|
88
|
+
version.strip if version
|
87
89
|
end
|
88
90
|
memoize :terraform_version_message
|
89
91
|
|
data/lib/terraspace/cli.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
module Terraspace
|
2
2
|
class CLI < Command
|
3
|
+
include Concern
|
4
|
+
|
3
5
|
class_option :verbose, type: :boolean
|
4
6
|
class_option :noop, type: :boolean
|
5
7
|
|
6
8
|
yes_option = Proc.new {
|
7
9
|
option :yes, aliases: :y, type: :boolean, desc: "-auto-approve the terraform apply"
|
8
10
|
}
|
9
|
-
format_option = Proc.new {
|
10
|
-
option :format, desc: "output formats: json, text"
|
11
|
-
}
|
12
11
|
out_option = Proc.new {
|
13
12
|
option :out, aliases: :o, desc: "Write the output to path"
|
14
13
|
}
|
@@ -43,6 +42,10 @@ module Terraspace
|
|
43
42
|
long_desc Help.text(:new)
|
44
43
|
subcommand "new", New
|
45
44
|
|
45
|
+
desc "setup SUBCOMMAND", "setup subcommands"
|
46
|
+
long_desc Help.text(:setup)
|
47
|
+
subcommand "setup", Setup
|
48
|
+
|
46
49
|
desc "tfc SUBCOMMAND", "tfc subcommands"
|
47
50
|
long_desc Help.text(:tfc)
|
48
51
|
subcommand "tfc", Tfc
|
@@ -62,17 +65,23 @@ module Terraspace
|
|
62
65
|
Bundle.new(options.merge(args: args)).run
|
63
66
|
end
|
64
67
|
|
65
|
-
desc "check_setup", "Check setup."
|
68
|
+
desc "check_setup", "Check setup.", hide: true
|
66
69
|
long_desc Help.text(:check_setup)
|
67
70
|
def check_setup
|
68
|
-
|
71
|
+
puts <<~EOL
|
72
|
+
DEPRECATED: The terraspace check_setup command is deprecated. Instead use:
|
73
|
+
|
74
|
+
terraspace setup check
|
75
|
+
|
76
|
+
EOL
|
77
|
+
Setup::Check.new(options).run
|
69
78
|
end
|
70
79
|
|
71
80
|
desc "console STACK", "Run console in built terraform project."
|
72
81
|
long_desc Help.text(:console)
|
73
82
|
instance_option.call
|
74
|
-
def console(mod)
|
75
|
-
Commander.new("console", options.merge(mod: mod, shell: "system")).run
|
83
|
+
def console(mod, *args)
|
84
|
+
Commander.new("console", options.merge(mod: mod, args: args, shell: "system")).run
|
76
85
|
end
|
77
86
|
|
78
87
|
desc "down STACK", "Destroy infrastructure stack."
|
@@ -81,8 +90,8 @@ module Terraspace
|
|
81
90
|
yes_option.call
|
82
91
|
reconfigure_option.call
|
83
92
|
option :destroy_workspace, type: :boolean, desc: "Also destroy the Cloud workspace. Only applies when using Terraform Cloud remote backend."
|
84
|
-
def down(mod)
|
85
|
-
Down.new(options.merge(mod: mod)).run
|
93
|
+
def down(mod, *args)
|
94
|
+
Down.new(options.merge(mod: mod, args: args)).run
|
86
95
|
end
|
87
96
|
|
88
97
|
desc "force_unlock", "Calls terrform force-unlock"
|
@@ -111,8 +120,8 @@ module Terraspace
|
|
111
120
|
desc "init STACK", "Run init in built terraform project."
|
112
121
|
long_desc Help.text(:init)
|
113
122
|
instance_option.call
|
114
|
-
def init(mod)
|
115
|
-
Commander.new("init", options.merge(mod: mod, quiet: false)).run
|
123
|
+
def init(mod, *args)
|
124
|
+
Commander.new("init", options.merge(mod: mod, args: args, quiet: false)).run
|
116
125
|
end
|
117
126
|
|
118
127
|
desc "list", "List stacks and modules."
|
@@ -141,22 +150,22 @@ module Terraspace
|
|
141
150
|
out_option.call
|
142
151
|
reconfigure_option.call
|
143
152
|
option :copy_to_root, type: :boolean, default: true, desc: "Copy plan file generated in the cache folder back to project root"
|
144
|
-
def plan(mod)
|
145
|
-
Commander.new("plan", options.merge(mod: mod)).run
|
153
|
+
def plan(mod, *args)
|
154
|
+
Commander.new("plan", options.merge(mod: mod, args: args)).run
|
146
155
|
end
|
147
156
|
|
148
157
|
desc "providers STACK", "Show providers."
|
149
158
|
long_desc Help.text(:providers)
|
150
159
|
instance_option.call
|
151
|
-
def providers(mod)
|
152
|
-
Commander.new("providers", options.merge(mod: mod)).run
|
160
|
+
def providers(mod, *args)
|
161
|
+
Commander.new("providers", options.merge(mod: mod, args: args)).run
|
153
162
|
end
|
154
163
|
|
155
164
|
desc "refresh STACK", "Run refresh."
|
156
165
|
long_desc Help.text(:refresh)
|
157
166
|
instance_option.call
|
158
|
-
def refresh(mod)
|
159
|
-
Commander.new("refresh", options.merge(mod: mod)).run
|
167
|
+
def refresh(mod, *args)
|
168
|
+
Commander.new("refresh", options.merge(mod: mod, args: args)).run
|
160
169
|
end
|
161
170
|
|
162
171
|
desc "seed STACK", "Build starer seed tfvars file."
|
@@ -182,9 +191,8 @@ module Terraspace
|
|
182
191
|
long_desc Help.text(:show)
|
183
192
|
instance_option.call
|
184
193
|
option :plan, desc: "path to created.plan"
|
185
|
-
|
186
|
-
|
187
|
-
Commander.new("show", options.merge(mod: mod)).run
|
194
|
+
def show(mod, *args)
|
195
|
+
Commander.new("show", options.merge(mod: mod, args: args)).run
|
188
196
|
end
|
189
197
|
|
190
198
|
desc "state SUBCOMMAND STACK", "Run state."
|
@@ -201,11 +209,10 @@ module Terraspace
|
|
201
209
|
|
202
210
|
desc "output STACK", "Run output."
|
203
211
|
long_desc Help.text(:output)
|
204
|
-
format_option.call
|
205
212
|
instance_option.call
|
206
213
|
out_option.call
|
207
|
-
def output(mod)
|
208
|
-
Commander.new("output", options.merge(mod: mod)).run
|
214
|
+
def output(mod, *args)
|
215
|
+
Commander.new("output", options.merge(mod: mod, args: args)).run
|
209
216
|
end
|
210
217
|
|
211
218
|
desc "up STACK", "Deploy infrastructure stack."
|
@@ -218,15 +225,15 @@ module Terraspace
|
|
218
225
|
reconfigure_option.call
|
219
226
|
option :plan, desc: "Execution plan that can be used to only execute a pre-determined set of actions."
|
220
227
|
option :var_files, type: :array, desc: "list of var files"
|
221
|
-
def up(mod)
|
222
|
-
Up.new(options.merge(mod: mod)).run
|
228
|
+
def up(mod, *args)
|
229
|
+
Up.new(options.merge(mod: mod, args: args)).run
|
223
230
|
end
|
224
231
|
|
225
232
|
desc "validate STACK", "Validate stack."
|
226
233
|
long_desc Help.text(:validate)
|
227
234
|
instance_option.call
|
228
|
-
def validate(mod)
|
229
|
-
Commander.new("validate", options.merge(mod: mod)).run
|
235
|
+
def validate(mod, *args)
|
236
|
+
Commander.new("validate", options.merge(mod: mod, args: args)).run
|
230
237
|
end
|
231
238
|
|
232
239
|
desc "completion *PARAMS", "Prints words for auto-completion."
|
data/lib/terraspace/command.rb
CHANGED
@@ -71,7 +71,7 @@ module Terraspace
|
|
71
71
|
version_manager = "rvm" if rvm?
|
72
72
|
version_manager = "rbenv" if rbenv?
|
73
73
|
if rbenv? || rvm?
|
74
|
-
puts <<~EOL.color(:yellow)
|
74
|
+
$stderr.puts <<~EOL.color(:yellow)
|
75
75
|
WARN: It looks like a standalone Terraspace install and #{version_manager} is also in use.
|
76
76
|
Different gems from the standalone Terraspace install and #{version_manager} can cause all kinds of trouble.
|
77
77
|
Please install Terraspace as a gem instead and remove the standalone Terraspace /opt/terraspace installation.
|
@@ -102,8 +102,9 @@ module Terraspace
|
|
102
102
|
return if subcommand?
|
103
103
|
return if command_name.nil?
|
104
104
|
return if help_flags.include?(Terraspace.argv.last) # IE: -h help
|
105
|
-
return if %w[-h -v check_setup completion completion_script help new test version].include?(command_name)
|
105
|
+
return if %w[-h -v --version check_setup completion completion_script help new setup test version].include?(command_name)
|
106
106
|
return if File.exist?("#{Terraspace.root}/config/app.rb")
|
107
|
+
return unless Terraspace.check_project
|
107
108
|
logger.error "ERROR: It doesnt look like this is a terraspace project. Are you sure you are in a terraspace project?".color(:red)
|
108
109
|
ENV['TS_TEST'] ? raise : exit(1)
|
109
110
|
end
|
@@ -1,13 +1,5 @@
|
|
1
1
|
module Terraspace::Compiler
|
2
2
|
module CommandsConcern
|
3
|
-
def requires_backend?
|
4
|
-
command_is?(requires_backend_commands)
|
5
|
-
end
|
6
|
-
|
7
|
-
def requires_backend_commands
|
8
|
-
%w[down init output plan providers refresh show up validate]
|
9
|
-
end
|
10
|
-
|
11
3
|
def command_is?(*commands)
|
12
4
|
commands.flatten!
|
13
5
|
commands.map!(&:to_s)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Simpler than Terraspace::Terraform::Runner::Backend::Parser because
|
2
|
+
# Terraspace::Compiler::Expander autodetect backend super early on.
|
3
|
+
# It's so early that don't want helper methods like <%= expansion(...) %> to be called.
|
4
|
+
# Calling the expansion helper itself results in Terraspace autodetecting a concrete
|
5
|
+
# Terraspace Plugin Expander, which creates an infinite loop.
|
6
|
+
# This simple detection class avoids calling ERB and avoids the infinite loop.
|
7
|
+
class Terraspace::Compiler::Expander
|
8
|
+
class Backend
|
9
|
+
extend Memoist
|
10
|
+
|
11
|
+
def initialize(mod)
|
12
|
+
@mod = mod
|
13
|
+
end
|
14
|
+
|
15
|
+
COMMENT = /^\s+#/
|
16
|
+
# Works for both backend.rb DSL and backend.tf ERB
|
17
|
+
def detect
|
18
|
+
return nil unless src_path # no backend file. returning nil means a local backend
|
19
|
+
lines = IO.readlines(src_path)
|
20
|
+
backend_line = lines.find { |l| l.include?("backend") && l !~ COMMENT }
|
21
|
+
md = backend_line.match(/['"](.*)['"]/)
|
22
|
+
md[1] if md
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
# Use original unrendered source as wont know the
|
27
|
+
# @mod.cache_dir = ":CACHE_ROOT/:REGION/:ENV/:BUILD_DIR"
|
28
|
+
# Until the concrete Terraspace Plugin Expander has been autodetected.
|
29
|
+
# Follow same precedence rules as rest of Terraspace.
|
30
|
+
def src_path
|
31
|
+
exprs = [
|
32
|
+
"app/stacks/#{@mod.build_dir}/backend.*",
|
33
|
+
"app/modules/#{@mod.build_dir}/backend.*",
|
34
|
+
"vendor/stacks/#{@mod.build_dir}/backend.*",
|
35
|
+
"vendor/modules/#{@mod.build_dir}/backend.*",
|
36
|
+
"config/terraform/backend.*",
|
37
|
+
]
|
38
|
+
path = nil
|
39
|
+
exprs.find { |expr| path = Dir.glob(expr).first }
|
40
|
+
path
|
41
|
+
end
|
42
|
+
memoize :src_path
|
43
|
+
end
|
44
|
+
end
|
@@ -1,17 +1,21 @@
|
|
1
1
|
module Terraspace::Compiler
|
2
2
|
class Expander
|
3
|
+
extend Memoist
|
3
4
|
delegate :expand, :expansion, to: :expander
|
4
5
|
|
5
|
-
attr_reader :expander
|
6
6
|
def initialize(mod, name)
|
7
7
|
@mod, @name = mod, name
|
8
|
-
@expander = expander_class.new(@mod)
|
9
8
|
end
|
10
9
|
|
10
|
+
def expander
|
11
|
+
expander_class.new(@mod)
|
12
|
+
end
|
13
|
+
memoize :expander
|
14
|
+
|
11
15
|
def expander_class
|
12
16
|
# IE: TerraspacePluginAws::Interfaces::Expander
|
13
17
|
klass_name = Terraspace::Plugin.klass("Expander", backend: @name)
|
14
|
-
klass_name.constantize
|
18
|
+
klass_name ? klass_name.constantize : Terraspace::Plugin::Expander::Generic
|
15
19
|
rescue NameError
|
16
20
|
Terraspace::Plugin::Expander::Generic
|
17
21
|
end
|
@@ -20,27 +24,15 @@ module Terraspace::Compiler
|
|
20
24
|
extend Memoist
|
21
25
|
|
22
26
|
def autodetect(mod, opts={})
|
23
|
-
backend = opts[:backend]
|
24
|
-
unless backend
|
25
|
-
plugin = find_plugin
|
26
|
-
backend = plugin[:backend]
|
27
|
-
end
|
27
|
+
backend = opts[:backend] || find_backend(mod)
|
28
28
|
new(mod, backend)
|
29
29
|
end
|
30
30
|
memoize :autodetect
|
31
31
|
|
32
|
-
def
|
33
|
-
|
34
|
-
if plugins.size == 1
|
35
|
-
plugins.first[1]
|
36
|
-
else
|
37
|
-
precedence = %w[aws azurerm google]
|
38
|
-
plugin = precedence.find do |provider|
|
39
|
-
plugins[provider]
|
40
|
-
end
|
41
|
-
plugins[plugin]
|
42
|
-
end
|
32
|
+
def find_backend(mod)
|
33
|
+
Backend.new(mod).detect
|
43
34
|
end
|
35
|
+
memoize :find_backend
|
44
36
|
end
|
45
37
|
end
|
46
38
|
end
|
data/lib/terraspace/core.rb
CHANGED
@@ -70,12 +70,15 @@ module Terraspace::Plugin::Expander
|
|
70
70
|
#
|
71
71
|
def strip(string)
|
72
72
|
string.sub(/^-+/,'').sub(/-+$/,'') # remove leading and trailing -
|
73
|
-
.sub(%r{/+$},'')
|
73
|
+
.sub(%r{/+$},'') # only remove trailing / or else /home/ec2-user => home/ec2-user
|
74
|
+
.sub(/:\/\//, 'TMP_KEEP_HTTP') # so we can keep ://. IE: https:// or http://
|
75
|
+
.gsub(%r{/+},'/') # remove double slashes are more. IE: // -> / Useful of region is '' in generic expander
|
76
|
+
.sub('TMP_KEEP_HTTP', '://') # restore :// IE: https:// or http://
|
74
77
|
end
|
75
78
|
|
76
79
|
def var_value(name)
|
77
80
|
name = name.sub(':','').downcase
|
78
|
-
value = send(name)
|
81
|
+
value = send(name).to_s
|
79
82
|
if name == "namespace" && Terraspace.config.layering.enable_names.expansion
|
80
83
|
value = friendly_name(value)
|
81
84
|
end
|
@@ -102,5 +105,13 @@ module Terraspace::Plugin::Expander
|
|
102
105
|
def cache_root
|
103
106
|
Terraspace.cache_root
|
104
107
|
end
|
108
|
+
|
109
|
+
# So default config works:
|
110
|
+
# config.cache_dir = ":CACHE_ROOT/:REGION/:ENV/:BUILD_DIR"
|
111
|
+
# For when folks configure it with the http backend for non-cloud providers
|
112
|
+
# The double slash // will be replace with a single slash in expander/interface.rb
|
113
|
+
def region
|
114
|
+
''
|
115
|
+
end
|
105
116
|
end
|
106
117
|
end
|