terraspace 1.1.6 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/.cody/all/bin/build.sh +33 -0
  3. data/.cody/all/buildspec.yml +10 -0
  4. data/.cody/{aws/role.rb → all/iam_role.rb} +0 -0
  5. data/.cody/all/project.rb +6 -0
  6. data/.cody/aws/bin/build.sh +5 -0
  7. data/.cody/aws/iam_role.rb +6 -0
  8. data/.cody/aws/project.rb +6 -2
  9. data/.cody/azurerm/bin/build.sh +5 -0
  10. data/.cody/azurerm/{role.rb → iam_role.rb} +0 -0
  11. data/.cody/azurerm/project.rb +5 -3
  12. data/.cody/google/bin/build.sh +5 -0
  13. data/.cody/google/{role.rb → iam_role.rb} +0 -0
  14. data/.cody/google/project.rb +5 -3
  15. data/.cody/none/bin/build.sh +5 -0
  16. data/.cody/none/{role.rb → iam_role.rb} +0 -0
  17. data/.cody/none/project.rb +6 -2
  18. data/.cody/shared/script/install/terraform.sh +2 -1
  19. data/.cody/shared/script/update/gemfile.sh +2 -0
  20. data/.cody/unit/project.rb +2 -2
  21. data/.gitignore +2 -0
  22. data/.pipedream/pipeline.rb +4 -3
  23. data/CHANGELOG.md +30 -0
  24. data/exe/terraspace +0 -7
  25. data/lib/templates/base/project/config/app.rb +3 -0
  26. data/lib/templates/plugin/ci/%gem_name%.gemspec.tt +32 -0
  27. data/lib/templates/plugin/ci/.gitignore +12 -0
  28. data/lib/templates/plugin/{.rspec → ci/.rspec} +0 -0
  29. data/lib/templates/plugin/ci/.rubocop.yml +13 -0
  30. data/lib/templates/plugin/ci/CHANGELOG.md.tt +5 -0
  31. data/lib/templates/plugin/ci/Gemfile +10 -0
  32. data/lib/templates/plugin/ci/LICENSE.txt +21 -0
  33. data/lib/templates/plugin/ci/README.md.tt +19 -0
  34. data/lib/templates/plugin/ci/Rakefile +12 -0
  35. data/lib/templates/plugin/ci/lib/%gem_name%/autoloader.rb.tt +23 -0
  36. data/lib/templates/plugin/ci/lib/%gem_name%/interface.rb.tt +15 -0
  37. data/lib/templates/plugin/ci/lib/%gem_name%/pr.rb.tt +15 -0
  38. data/lib/templates/plugin/ci/lib/%gem_name%/vars.rb.tt +26 -0
  39. data/lib/templates/plugin/ci/lib/%gem_name%/version.rb.tt +5 -0
  40. data/lib/templates/plugin/ci/lib/%gem_name%.rb.tt +17 -0
  41. data/lib/templates/plugin/ci/spec/%gem_name%_spec.rb.tt +7 -0
  42. data/lib/templates/plugin/ci/spec/spec_helper.rb.tt +15 -0
  43. data/lib/templates/plugin/{.gitignore → core/.gitignore} +0 -0
  44. data/lib/templates/plugin/core/.rspec +3 -0
  45. data/lib/templates/plugin/{CHANGELOG.md → core/CHANGELOG.md} +0 -0
  46. data/lib/templates/plugin/{Gemfile → core/Gemfile} +0 -0
  47. data/lib/templates/plugin/{LICENSE.txt → core/LICENSE.txt} +0 -0
  48. data/lib/templates/plugin/{README.md.tt → core/README.md.tt} +0 -0
  49. data/lib/templates/plugin/{Rakefile → core/Rakefile} +0 -0
  50. data/lib/templates/plugin/{bin → core/bin}/console.tt +0 -0
  51. data/lib/templates/plugin/{bin → core/bin}/setup +0 -0
  52. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/module/main.tf +0 -0
  53. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/module/outputs.tf +0 -0
  54. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/module/variables.tf +0 -0
  55. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/project/config/terraform/backend.tf.tt +0 -0
  56. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/project/config/terraform/provider.tf +0 -0
  57. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/stack/main.tf +0 -0
  58. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/stack/outputs.tf +0 -0
  59. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/stack/variables.tf +0 -0
  60. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/module/main.rb +0 -0
  61. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/module/outputs.rb +0 -0
  62. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/module/variables.rb +0 -0
  63. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/project/config/terraform/backend.rb.tt +0 -0
  64. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/project/config/terraform/provider.rb +0 -0
  65. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/stack/main.rb +0 -0
  66. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/stack/outputs.rb +0 -0
  67. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/stack/variables.rb +0 -0
  68. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/.rspec +0 -0
  69. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/Gemfile +0 -0
  70. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/spec/fixtures/stack/main.tf +0 -0
  71. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/spec/fixtures/stack/outputs.tf +0 -0
  72. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/spec/fixtures/stack/variables.tf +0 -0
  73. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/spec/main_spec.rb +0 -0
  74. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/spec/spec_helper.rb +0 -0
  75. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/autoloader.rb.tt +0 -0
  76. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/clients.rb.tt +0 -0
  77. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/interfaces/backend.rb.tt +0 -0
  78. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/interfaces/config.rb.tt +0 -0
  79. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/interfaces/expander.rb.tt +0 -0
  80. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/interfaces/layer.rb.tt +0 -0
  81. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/version.rb.tt +0 -0
  82. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%.rb.tt +0 -0
  83. data/lib/templates/plugin/{spec → core/spec}/spec_helper.rb.tt +0 -0
  84. data/lib/templates/plugin/{spec → core/spec}/terraspace_provider_%name%_spec.rb.tt +0 -0
  85. data/lib/templates/plugin/{terraspace_plugin_%name%.gemspec.tt → core/terraspace_plugin_%name%.gemspec.tt} +0 -0
  86. data/lib/terraspace/all/runner.rb +4 -1
  87. data/lib/terraspace/all/summary.rb +2 -0
  88. data/lib/terraspace/app.rb +23 -4
  89. data/lib/terraspace/builder/children.rb +6 -7
  90. data/lib/terraspace/builder.rb +8 -2
  91. data/lib/terraspace/cli/concerns/plan_path.rb +8 -0
  92. data/lib/terraspace/cli/down.rb +4 -0
  93. data/lib/terraspace/cli/init.rb +1 -1
  94. data/lib/terraspace/cli/new/ci.rb +121 -0
  95. data/lib/terraspace/cli/new/example.rb +1 -1
  96. data/lib/terraspace/cli/new/helpers/plugin_gem.rb +1 -1
  97. data/lib/terraspace/cli/new/plugin/ci.rb +46 -0
  98. data/lib/terraspace/cli/new/plugin/core.rb +26 -0
  99. data/lib/terraspace/cli/new/plugin/helper.rb +4 -1
  100. data/lib/terraspace/cli/new/plugin.rb +8 -15
  101. data/lib/terraspace/cli/new/test.rb +1 -1
  102. data/lib/terraspace/cli/new.rb +17 -13
  103. data/lib/terraspace/cli/plan.rb +13 -0
  104. data/lib/terraspace/cli/setup/check.rb +8 -0
  105. data/lib/terraspace/cli/up.rb +8 -12
  106. data/lib/terraspace/cli.rb +2 -2
  107. data/lib/terraspace/cloud/api/cani.rb +30 -0
  108. data/lib/terraspace/cloud/api/concern/errors.rb +12 -0
  109. data/lib/terraspace/cloud/api/concern/record.rb +18 -0
  110. data/lib/terraspace/cloud/api/concern.rb +38 -0
  111. data/lib/terraspace/cloud/api/http_methods.rb +116 -0
  112. data/lib/terraspace/cloud/api/validate.rb +24 -0
  113. data/lib/terraspace/cloud/api.rb +33 -0
  114. data/lib/terraspace/cloud/base.rb +97 -0
  115. data/lib/terraspace/cloud/ci/generic.rb +25 -0
  116. data/lib/terraspace/cloud/ci/manual.rb +81 -0
  117. data/lib/terraspace/cloud/ci/vcs/base.rb +36 -0
  118. data/lib/terraspace/cloud/ci/vcs/bitbucket.rb +11 -0
  119. data/lib/terraspace/cloud/ci/vcs/github.rb +11 -0
  120. data/lib/terraspace/cloud/ci/vcs/gitlab.rb +11 -0
  121. data/lib/terraspace/cloud/ci/vcs.rb +18 -0
  122. data/lib/terraspace/cloud/ci.rb +56 -0
  123. data/lib/terraspace/cloud/context.rb +14 -0
  124. data/lib/terraspace/cloud/folder/base.rb +17 -0
  125. data/lib/terraspace/cloud/folder/package.rb +33 -0
  126. data/lib/terraspace/cloud/folder/tidy.rb +54 -0
  127. data/lib/terraspace/cloud/folder/uploader.rb +37 -0
  128. data/lib/terraspace/cloud/folder.rb +11 -0
  129. data/lib/terraspace/cloud/plan.rb +47 -0
  130. data/lib/terraspace/cloud/update.rb +37 -0
  131. data/lib/terraspace/command.rb +16 -1
  132. data/lib/terraspace/compiler/dsl/syntax/mod.rb +2 -2
  133. data/lib/terraspace/compiler/dsl/syntax/tfvar.rb +1 -1
  134. data/lib/terraspace/compiler/expander/backend.rb +1 -1
  135. data/lib/terraspace/compiler/expander.rb +1 -1
  136. data/lib/terraspace/compiler/strategy/tfvar/layer.rb +56 -29
  137. data/lib/terraspace/core.rb +36 -3
  138. data/lib/terraspace/ext/core/module.rb +9 -4
  139. data/lib/terraspace/hooks/builder.rb +1 -1
  140. data/lib/terraspace/logger.rb +32 -5
  141. data/lib/terraspace/mod.rb +21 -9
  142. data/lib/terraspace/plugin/expander/interface.rb +19 -11
  143. data/lib/terraspace/plugin.rb +14 -5
  144. data/lib/terraspace/shell.rb +7 -2
  145. data/lib/terraspace/terraform/args/thor.rb +8 -2
  146. data/lib/terraspace/terraform/ihooks/after/apply.rb +8 -0
  147. data/lib/terraspace/terraform/ihooks/after/destroy.rb +8 -0
  148. data/lib/terraspace/terraform/ihooks/after/plan.rb +31 -2
  149. data/lib/terraspace/terraform/ihooks/base.rb +7 -3
  150. data/lib/terraspace/terraform/ihooks/before/apply.rb +8 -0
  151. data/lib/terraspace/terraform/ihooks/before/destroy.rb +8 -0
  152. data/lib/terraspace/terraform/ihooks/before/plan.rb +11 -3
  153. data/lib/terraspace/terraform/runner.rb +19 -5
  154. data/lib/terraspace/version.rb +1 -1
  155. data/lib/terraspace.rb +2 -0
  156. data/terraspace.gemspec +1 -0
  157. metadata +119 -51
  158. data/.pipedream/schedule.rb +0 -3
@@ -0,0 +1,33 @@
1
+ require 'zip_folder'
2
+
3
+ class Terraspace::Cloud::Folder
4
+ class Package < Base
5
+ def build
6
+ copy
7
+ tidy
8
+ zip # returns zip path
9
+ end
10
+
11
+ def copy
12
+ FileUtils.rm_rf(artifacts_path)
13
+ FileUtils.mkdir_p(File.dirname(artifacts_path))
14
+
15
+ expr = "#{@mod.cache_dir}/.terraspace-cache/_cache2/#{@type}/*"
16
+ Dir.glob(expr).each do |src|
17
+ dest = "#{artifacts_path}/#{File.basename(src)}"
18
+ FileUtils.mkdir_p(File.dirname(dest))
19
+ FileUtils.cp(src, dest)
20
+ end
21
+ end
22
+
23
+ def tidy
24
+ Tidy.new(@options).cleanup
25
+ end
26
+
27
+ def zip
28
+ FileUtils.rm_f(zip_path)
29
+ ZipFolder.zip(artifacts_path, zip_path)
30
+ zip_path
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,54 @@
1
+ class Terraspace::Cloud::Folder
2
+ class Tidy < Base
3
+ def cleanup
4
+ removals.each do |removal|
5
+ removal = removal.sub(%r{^/},'') # remove leading slash
6
+ path = "#{artifacts_path}/#{removal}"
7
+ rm_rf(path)
8
+ end
9
+ end
10
+
11
+ def removals
12
+ removals = always_removals
13
+ removals += get_removals("#{artifacts_path}/.gitignore")
14
+ removals = removals.reject do |p|
15
+ tskeep.find do |keep|
16
+ p.include?(keep)
17
+ end
18
+ end
19
+ removals.uniq
20
+ end
21
+
22
+ def get_removals(file)
23
+ path = file
24
+ return [] unless File.exist?(path)
25
+
26
+ removal = File.read(path).split("\n")
27
+ removal.map {|i| i.strip}.reject {|i| i =~ /^#/ || i.empty?}
28
+ end
29
+
30
+ # We clean out ignored files pretty aggressively. So provide
31
+ # a way for users to keep files from being cleaned out.
32
+ def tskeep
33
+ always_keep = %w[]
34
+ path = "#{artifacts_path}/.tskeep"
35
+ return always_keep unless File.exist?(path)
36
+
37
+ keep = IO.readlines(path)
38
+ keep = keep.map {|i| i.strip}.reject { |i| i =~ /^#/ || i.empty? }
39
+ (always_keep + keep).uniq
40
+ end
41
+
42
+ def rm_rf(path)
43
+ exists = File.exist?("#{path}/.gitkeep") || File.exist?("#{path}/.keep")
44
+ return if exists
45
+
46
+ FileUtils.rm_rf(path)
47
+ end
48
+
49
+ # These directories will be removed regardless of dir level
50
+ def always_removals
51
+ %w[.git spec tmp]
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,37 @@
1
+ class Terraspace::Cloud::Folder
2
+ class Uploader < Base
3
+ attr_reader :record
4
+ def upload
5
+ @record = create_record # set @record for start_plan(uploader.record)
6
+ upload_project(@record['url'], zip_path)
7
+ end
8
+
9
+ def create_record
10
+ result = api.create_upload
11
+ if errors?(result)
12
+ error_message(result)
13
+ exit 1 # Consider: raise exception can rescue higher up
14
+ else
15
+ load_record(result)
16
+ end
17
+ end
18
+
19
+ def upload_project(url, path)
20
+ uri = URI.parse(url)
21
+ object_content = IO.read(path)
22
+ resp = Net::HTTP.start(uri.host) do |http|
23
+ http.send_request(
24
+ 'PUT',
25
+ uri.request_uri,
26
+ object_content,
27
+ 'content-type' => ''
28
+ )
29
+ end
30
+ unless resp.code =~ /^20/
31
+ puts "ERROR: Uploading code"
32
+ puts "resp.body #{resp.body}"
33
+ exit 1 # TODO: consider raising error
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ module Terraspace::Cloud
2
+ class Folder < Base
3
+ def upload_data
4
+ Package.new(@options).build
5
+ uploader = Uploader.new(@options)
6
+ uploader.upload
7
+ @upload = uploader.record
8
+ end
9
+ attr_reader :upload # upload record
10
+ end
11
+ end
@@ -0,0 +1,47 @@
1
+ module Terraspace::Cloud
2
+ class Plan < Base
3
+ include Terraspace::CLI::Concerns::PlanPath
4
+
5
+ def run
6
+ return unless record?
7
+
8
+ build
9
+ folder = Folder.new(@options.merge(type: "plan"))
10
+ upload = folder.upload_data # returns upload record
11
+ result = api.create_plan(
12
+ upload_id: upload['id'],
13
+ stack_uid: upload['stack_id'], # use stack_uid since stack_id is friendly url name
14
+ plan: stage_attrs,
15
+ )
16
+ url = terraspace_cloud_info(result)
17
+ pr_comment(url)
18
+ rescue Terraspace::NetworkError => e
19
+ logger.warn "WARN: #{e.class} #{e.message}"
20
+ logger.warn "WARN: Unable to save data to Terraspace cloud"
21
+ end
22
+
23
+ def cani?
24
+ api.create_plan(cani: 1)
25
+ end
26
+
27
+ def build
28
+ clean_cache2_stage
29
+ # .terraspace-cache/dev/stacks/demo
30
+ Dir.chdir(@mod.cache_dir) do
31
+ plan_dir = File.dirname(plan_path)
32
+
33
+ IO.write("#{plan_dir}/plan.log", Terraspace::Logger.logs)
34
+
35
+ return unless @success
36
+ return if File.empty?(plan_path)
37
+
38
+ out_option_root_path = "#{Terraspace.root}/#{plan_path}"
39
+ return unless File.exist?(out_option_root_path)
40
+ FileUtils.cp(out_option_root_path, plan_path)
41
+
42
+ json = plan_path.sub('.binary','.json')
43
+ sh "terraform show -json #{plan_path} > #{json}"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,37 @@
1
+ module Terraspace::Cloud
2
+ class Update < Base
3
+ def run
4
+ return unless record?
5
+
6
+ build
7
+ folder = Folder.new(@options.merge(type: @kind))
8
+ upload = folder.upload_data # returns upload record
9
+ result = api.create_update(
10
+ upload_id: upload['id'],
11
+ stack_uid: upload['stack_id'], # use stack_uid since stack_id is friendly url name
12
+ update: stage_attrs,
13
+ )
14
+ url = terraspace_cloud_info(result)
15
+ pr_comment(url)
16
+ end
17
+
18
+ def cani?
19
+ api.create_update(cani: 1)
20
+ end
21
+
22
+ def build
23
+ clean_cache2_stage
24
+ # .terraspace-cache/dev/stacks/demo
25
+ Dir.chdir(@mod.cache_dir) do
26
+ cache2_path = ".terraspace-cache/_cache2/#{@kind}"
27
+ FileUtils.mkdir_p(cache2_path)
28
+
29
+ IO.write("#{cache2_path}/#{@kind}.log", Terraspace::Logger.logs)
30
+ return unless @success
31
+
32
+ sh "terraform state pull > #{cache2_path}/state.json"
33
+ sh "terraform output -json > #{cache2_path}/output.json"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -29,10 +29,25 @@ module Terraspace
29
29
  class << self
30
30
  include Terraspace::Util::Logging
31
31
 
32
+ @@initial_dispatch_command = nil
33
+ def initial_dispatch_command
34
+ @@initial_dispatch_command
35
+ end
36
+
37
+ # use by test framework
38
+ def reset_dispatch_command
39
+ @@initial_dispatch_command = nil
40
+ end
41
+
32
42
  def dispatch(m, args, options, config)
33
43
  # Terraspace.argv provides consistency when terraspace is being called by rspec-terraspace test harness
34
44
  Terraspace.argv = args.clone # important to clone since Thor removes the first argv
35
45
 
46
+ unless @@initial_dispatch_command
47
+ @@initial_dispatch_command = "$ terraspace #{args.join(' ')}\n"
48
+ Terraspace::Logger.buffer << @@initial_dispatch_command
49
+ end
50
+
36
51
  check_standalone_install!
37
52
  check_project!(args.first)
38
53
 
@@ -105,7 +120,7 @@ module Terraspace
105
120
  return if %w[-h -v --version check_setup completion completion_script help new setup test version].include?(command_name)
106
121
  return if File.exist?("#{Terraspace.root}/config/app.rb")
107
122
  return unless Terraspace.check_project
108
- logger.error "ERROR: It doesnt look like this is a terraspace project. Are you sure you are in a terraspace project?".color(:red)
123
+ logger.error "ERROR: It doesn't look like this is a terraspace project. Are you sure you are in a terraspace project?".color(:red)
109
124
  ENV['TS_TEST'] ? raise : exit(1)
110
125
  end
111
126
 
@@ -1,8 +1,8 @@
1
1
  module Terraspace::Compiler::Dsl::Syntax
2
2
  module Mod
3
3
  include Terraspace::Util::Logging
4
- include_dir("mod")
5
- include_dir("helpers")
4
+ include_modules("mod")
5
+ include_modules("helpers")
6
6
  include_plugin_helpers
7
7
  include_project_level_helpers
8
8
  end
@@ -1,6 +1,6 @@
1
1
  module Terraspace::Compiler::Dsl::Syntax
2
2
  module Tfvar
3
- include_dir("tfvar")
3
+ include_modules("tfvar")
4
4
  include Terraspace::Compiler::Dependencies::Helpers
5
5
  end
6
6
  end
@@ -24,7 +24,7 @@ class Terraspace::Compiler::Expander
24
24
 
25
25
  private
26
26
  # Use original unrendered source as wont know the
27
- # @mod.cache_dir = ":CACHE_ROOT/:REGION/:ENV/:BUILD_DIR"
27
+ # @mod.cache_dir = ":REGION/:ENV/:BUILD_DIR"
28
28
  # Until the concrete Terraspace Plugin Expander has been autodetected.
29
29
  # Follow same precedence rules as rest of Terraspace.
30
30
  def src_path
@@ -44,7 +44,7 @@ module Terraspace::Compiler
44
44
 
45
45
  # autodetect by looking up loaded plugins
46
46
  def plugin_backend
47
- plugin = Terraspace::Autodetect.new.plugin # IE: aws, azurerm, google
47
+ plugin = Terraspace::Plugin.autodetect
48
48
  if plugin
49
49
  data = Terraspace::Plugin.meta[plugin]
50
50
  data[:backend] # IE: s3, azurerm, gcs
@@ -50,21 +50,35 @@ class Terraspace::Compiler::Strategy::Tfvar
50
50
  memoize :paths
51
51
 
52
52
  def full_paths(tfvars_dir)
53
- layer_paths = full_layering.map do |layer|
54
- [
55
- "#{tfvars_dir}/#{layer}.tfvars",
56
- "#{tfvars_dir}/#{layer}.rb",
57
- ]
58
- end.flatten
53
+ layers = full_layering(tfvars_dir, remove_first: true)
54
+ if Terraspace.role
55
+ layers += full_layering("#{tfvars_dir}/#{Terraspace.role}")
56
+ end
57
+ if Terraspace.app
58
+ layers += full_layering("#{tfvars_dir}/#{Terraspace.app}")
59
+ end
60
+ if Terraspace.app && Terraspace.role
61
+ layers += full_layering("#{tfvars_dir}/#{Terraspace.app}/#{Terraspace.role}")
62
+ end
63
+ layers
59
64
  end
60
65
 
61
- def full_layering
66
+ def full_layering(tfvars_dir, remove_first: false)
62
67
  # layers defined in Terraspace::Layering module
63
68
  all = layers.map { |layer| layer.sub(/\/$/,'') } # strip trailing slash
64
- all.inject([]) do |sum, layer|
69
+ all = all.inject([]) do |sum, layer|
65
70
  sum += layer_levels(layer) unless layer.nil?
66
71
  sum
67
72
  end
73
+ all.map! do |layer|
74
+ layer = layer.blank? ? layer : "/#{layer}"
75
+ [
76
+ "#{tfvars_dir}#{layer}.tfvars",
77
+ "#{tfvars_dir}#{layer}.rb",
78
+ ]
79
+ end.flatten!
80
+ all.shift if remove_first # IE: app/stacks/demo/tfvars.tfvars
81
+ all
68
82
  end
69
83
 
70
84
  # adds prefix and to each layer pair that has base and Terraspace.env. IE:
@@ -73,14 +87,17 @@ class Terraspace::Compiler::Strategy::Tfvar
73
87
  # "#{prefix}/#{Terraspace.env}"
74
88
  #
75
89
  def layer_levels(prefix=nil)
76
- levels = ["base", Terraspace.env, @mod.instance].reject(&:blank?) # layer levels. @mod.instance can be nil
77
- env_levels = levels.map { |l| "#{Terraspace.env}/#{l}" } # env folder also
78
- levels = levels + env_levels
90
+ if @mod.instance
91
+ logger.info "WARN: The instance option is deprecated. Instead use TS_EXTRA"
92
+ logger.info "See: http://terraspace.test/docs/layering/instance-option/"
93
+ end
94
+ extra = Terraspace.extra || @mod.instance
95
+ levels = ["base", Terraspace.env, extra].reject(&:blank?) # layer levels. @mod.instance can be nil
79
96
  levels.map! do |i|
80
97
  # base layer has prefix of '', reject with blank so it doesnt produce '//'
81
98
  [prefix, i].reject(&:blank?).join('/')
82
99
  end
83
- levels.unshift(prefix) unless prefix.blank? # IE: tfvars/us-west-2.tfvars
100
+ levels.unshift(prefix) if !prefix.nil?
84
101
  levels
85
102
  end
86
103
 
@@ -93,18 +110,22 @@ class Terraspace::Compiler::Strategy::Tfvar
93
110
  layers << layer.region
94
111
 
95
112
  namespace = friendly_name(layer.namespace)
96
-
97
- # namespace is a simple way keep different tfvars between different engineers on different accounts
98
- layers << namespace
99
- layers << "#{namespace}/#{layer.region}"
100
-
101
- # in case using multiple providers and one region
102
- layers << layer.provider
103
- layers << "#{layer.provider}/#{layer.region}" # also in case another provider has colliding regions
104
-
105
- # Most general layering
106
- layers << "#{layer.provider}/#{namespace}"
107
- layers << "#{layer.provider}/#{namespace}/#{layer.region}"
113
+ mode = Terraspace.config.layering.mode # simple, namespace, provider
114
+ if mode == "namespace" || mode == "provider"
115
+ # namespace is a simple way keep different tfvars between different engineers on different accounts
116
+ layers << namespace
117
+ layers << "#{namespace}/#{layer.region}"
118
+ end
119
+
120
+ if mode == "provider"
121
+ # in case using multiple providers and one region
122
+ layers << layer.provider
123
+ layers << "#{layer.provider}/#{layer.region}" # also in case another provider has colliding regions
124
+
125
+ # Most general layering
126
+ layers << "#{layer.provider}/#{namespace}"
127
+ layers << "#{layer.provider}/#{namespace}/#{layer.region}"
128
+ end
108
129
  end
109
130
  layers
110
131
  end
@@ -125,7 +146,7 @@ class Terraspace::Compiler::Strategy::Tfvar
125
146
  # should go.
126
147
  #
127
148
  def stack_tfvars_dir
128
- seed_dir = "#{Terraspace.root}/seed/tfvars/#{@mod.build_dir(disable_instance: true)}"
149
+ seed_dir = "#{Terraspace.root}/seed/tfvars/#{@mod.build_dir(disable_extra: true)}"
129
150
  mod_dir = "#{@mod.root}/tfvars"
130
151
 
131
152
  empty = Dir.glob("#{seed_dir}/*").empty?
@@ -137,14 +158,20 @@ class Terraspace::Compiler::Strategy::Tfvar
137
158
  return unless @mod.resolved
138
159
  return if @@shown_layers[@mod.name]
139
160
  logger.debug "Layers for #{@mod.name}:"
161
+ show = Terraspace.config.layering.show || ENV['TS_SHOW_ALL_LAYERS']
140
162
  paths.each do |path|
163
+ next if ARGV[0] == "all" # dont show layers with all command since fork happens after build
141
164
  next unless path.include?('.tfvars')
142
- show = File.exist?(path) || ENV['TS_SHOW_ALL_LAYERS']
143
- logger.debug " #{pretty_path(path)}" if show
165
+ if ENV['TS_SHOW_ALL_LAYERS']
166
+ message = " #{pretty_path(path)}"
167
+ message = "#{message} (found)".color(:yellow) if File.exist?(path)
168
+ logger.info message
169
+ elsif show
170
+ logger.info " #{pretty_path(path)}" if File.exist?(path)
171
+ end
144
172
  end
145
- logger.debug ""
173
+ # do not logger.info "" it creates a newline with all
146
174
  @@shown_layers[@mod.name] = true
147
175
  end
148
-
149
176
  end
150
177
  end
@@ -2,10 +2,21 @@ module Terraspace
2
2
  module Core
3
3
  extend Memoist
4
4
 
5
+ def app
6
+ ENV['TS_APP'] unless ENV['TS_APP'].blank?
7
+ end
8
+
9
+ def role
10
+ ENV['TS_ROLE'] unless ENV['TS_ROLE'].blank?
11
+ end
12
+
5
13
  def env
6
- ENV['TS_ENV'] || "dev"
14
+ ENV['TS_ENV'].blank? ? "dev" : ENV['TS_ENV']
15
+ end
16
+
17
+ def extra
18
+ ENV['TS_EXTRA'] unless ENV['TS_EXTRA'].blank?
7
19
  end
8
- memoize :env
9
20
 
10
21
  @@root = nil
11
22
  def root
@@ -15,7 +26,7 @@ module Terraspace
15
26
  cattr_writer :root
16
27
 
17
28
  def cache_root
18
- ENV['TS_CACHE_ROOT'] || config.build.cache_root || "#{root}/.terraspace-cache"
29
+ ENV['TS_CACHE_ROOT'] || "#{root}/.terraspace-cache"
19
30
  end
20
31
  memoize :cache_root
21
32
 
@@ -60,5 +71,27 @@ module Terraspace
60
71
  # So use Terraspace.argv instead of ARGV constant
61
72
  cattr_accessor :argv
62
73
  cattr_accessor :check_project, default: true
74
+
75
+ @@cloud_warning_shown = false
76
+ def cloud?
77
+ enabled = !!Terraspace.config.cloud.org
78
+ if ENV['TS_TOKEN'] && !enabled && !@@cloud_warning_shown
79
+ logger.warn <<~EOL.color(:yellow)
80
+ WARN: TS_TOKEN is set but config.cloud.org is not set.
81
+ See: http://terraspace.cloud/docs/cloud/setup/
82
+ EOL
83
+ @@cloud_warning_shown = true
84
+ end
85
+ enabled
86
+ end
87
+
88
+ @@buffer = []
89
+ def buffer
90
+ @@buffer
91
+ end
92
+
93
+ def command?(name)
94
+ ARGV[0] == name || ARGV[1] == name
95
+ end
63
96
  end
64
97
  end
@@ -10,17 +10,22 @@ class Module
10
10
  # windows: "C:/Ruby31-x64/lib/ruby/gems/3.1.0/gems/terraspace-1.1.1/lib/terraspace/builder.rb:34:in `build'"
11
11
  # linux: "/home/ec2-user/.rvm/gems/ruby-3.0.3/gems/terraspace-1.1.1/lib/terraspace/compiler/dsl/syntax/mod.rb:4:in `<module:Mod>'"
12
12
  #
13
- def include_dir(dir)
13
+ def include_modules(dir)
14
14
  caller_line = caller[0]
15
15
  parts = caller_line.split(':')
16
16
  calling_file = caller_line.match(/^[a-zA-Z]:/) ? parts[1] : parts[0]
17
17
  parent_dir = File.dirname(calling_file)
18
18
 
19
19
  full_dir = "#{parent_dir}/#{dir}"
20
- Dir.glob("#{full_dir}/**/*").each do |path|
20
+ paths = Dir.glob("#{full_dir}/**/*.rb")
21
+ if paths.empty?
22
+ raise "Empty include_modules full_dir: #{full_dir}"
23
+ end
24
+ paths.each do |path|
21
25
  regexp = Regexp.new(".*/lib/")
22
- klass = path.sub(regexp, '').sub('.rb','').camelize
23
- include klass.constantize
26
+ mod = path.sub(regexp, '').sub('.rb','').camelize
27
+ c = mod.constantize
28
+ include c if c.class == Module
24
29
  end
25
30
  end
26
31
 
@@ -40,7 +40,7 @@ module Terraspace::Hooks
40
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 "Hook: Running #{id} hook.#{label}".color(:cyan)
43
+ logger.info "Hook: Running #{id} hook.#{label}".color(:cyan) if Terraspace.config.hooks.show
44
44
  Runner.new(@mod, hook).run
45
45
  end
46
46
 
@@ -14,17 +14,44 @@ module Terraspace
14
14
  else
15
15
  super # use the configured formatter
16
16
  end
17
- line =~ /\n$/ ? line : "#{line}\n"
17
+ out = line =~ /\n$/ ? line : "#{line}\n"
18
+ @@buffer << out
19
+ out
18
20
  end
19
21
 
20
22
  # Used to allow terraform output to always go to stdout
21
23
  # Terraspace output goes to stderr by default
22
24
  # See: terraspace/shell.rb
23
25
  def stdout(msg, newline: true)
24
- if newline
25
- puts msg
26
- else
27
- print msg
26
+ out = newline ? "#{msg}\n" : msg
27
+ @@buffer << out
28
+ print out
29
+ end
30
+
31
+ def stdin_capture(text)
32
+ @@buffer << "#{text}\n"
33
+ @@stdin_capture = text
34
+ end
35
+
36
+ class << self
37
+ @@stdin_capture = ''
38
+ def stdin_capture
39
+ @@stdin_capture
40
+ end
41
+
42
+ @@buffer = []
43
+ def buffer
44
+ @@buffer
45
+ end
46
+
47
+ def logs
48
+ @@buffer.join('')
49
+ end
50
+
51
+ # for test framework
52
+ def clear
53
+ Terraspace::Command.reset_dispatch_command
54
+ @@buffer = []
28
55
  end
29
56
  end
30
57
  end
@@ -14,7 +14,7 @@ module Terraspace
14
14
  def initialize(name, options={})
15
15
  @name, @options = placeholder(name), options
16
16
  @consider_stacks = options[:consider_stacks].nil? ? true : options[:consider_stacks]
17
- @instance = options[:instance]
17
+ @extra = Terraspace.extra || options[:instance]
18
18
  @resolved = true # more common case
19
19
  end
20
20
 
@@ -72,7 +72,7 @@ module Terraspace
72
72
  # If the app/stacks/NAME has been removed in source code but stack still exist in the cloud.
73
73
  # allow user to delete by materializing an empty stack with the backend.tf
74
74
  # Note this does not seem to work for Terraform Cloud as terraform init doesnt seem to download the plugins
75
- # required. SIt only works for s3, azurerm, and gcs backends. On TFC, you can delete the stack via the GUI though.
75
+ # required. It only works for s3, azurerm, and gcs backends. On TFC, you can delete the stack via the GUI though.
76
76
  #
77
77
  # down - so user can delete stacks w/o needing to create an empty app/stacks/demo folder
78
78
  # null - for the terraspace summary command when there are zero stacks.
@@ -99,11 +99,11 @@ module Terraspace
99
99
  #
100
100
  # modules/vpc
101
101
  #
102
- def build_dir(disable_instance: false)
103
- if !@instance.nil? && type_dir == "stacks" && !disable_instance
102
+ def build_dir(disable_extra: false)
103
+ if !@extra.nil? && type_dir == "stacks" && !disable_extra
104
104
  # add _ in front so instance doesnt collide with other default stacks
105
105
  # never add for app/modules sources
106
- instance_name = [name, @instance].compact.join('.')
106
+ instance_name = [name, @extra].compact.join('.')
107
107
  else
108
108
  instance_name = name
109
109
  end
@@ -111,9 +111,9 @@ module Terraspace
111
111
  end
112
112
 
113
113
  # Full path with build_dir
114
- def cache_dir
114
+ def cache_dir(options={})
115
115
  # config.build.cache_dir is a String or object that respond_to call. IE:
116
- # :CACHE_ROOT/:REGION/:ENV/:BUILD_DIR
116
+ # :REGION/:ENV/:BUILD_DIR
117
117
  # CustomBuildDir.call
118
118
  # The call method should return a String pattern used for substitutions
119
119
  object = Terraspace.config.build.cache_dir
@@ -128,11 +128,23 @@ module Terraspace
128
128
  raise "ERROR: config.build.cache_dir is not a String or responds to the .call method."
129
129
  end
130
130
 
131
- expander = Terraspace::Compiler::Expander.autodetect(self)
132
- expander.expansion(pattern) # pattern is a String that contains placeholders for substitutions
131
+ path = expansion(pattern)
132
+ path = "#{Terraspace.cache_root}/#{path}"
133
+ path.gsub!(%r{/+},'/') # remove double slashes are more. IE: // -> / Useful since region is '' in generic expander
134
+ path
133
135
  end
134
136
  memoize :cache_dir
135
137
 
138
+ def expansion(pattern)
139
+ @expander ||= Terraspace::Compiler::Expander.autodetect(self)
140
+ @expander.expansion(pattern) # pattern is a String that contains placeholders for substitutions
141
+ end
142
+
143
+ def out_option
144
+ expand = Terraspace::Terraform::Args::Expand.new(self, @options)
145
+ expand.out
146
+ end
147
+
136
148
  def type
137
149
  root.include?("/stacks/") ? "stack" : "module"
138
150
  end