terraspace 1.1.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/.cody/all/{role.rb → iam_role.rb} +0 -0
  3. data/.cody/all/project.rb +6 -2
  4. data/.cody/aws/bin/build.sh +5 -0
  5. data/.cody/aws/{role.rb → iam_role.rb} +0 -0
  6. data/.cody/aws/project.rb +6 -2
  7. data/.cody/azurerm/bin/build.sh +5 -0
  8. data/.cody/azurerm/{role.rb → iam_role.rb} +0 -0
  9. data/.cody/azurerm/project.rb +5 -3
  10. data/.cody/google/bin/build.sh +5 -0
  11. data/.cody/google/{role.rb → iam_role.rb} +0 -0
  12. data/.cody/google/project.rb +5 -3
  13. data/.cody/none/bin/build.sh +5 -0
  14. data/.cody/none/{role.rb → iam_role.rb} +0 -0
  15. data/.cody/none/project.rb +6 -2
  16. data/.cody/shared/script/install/terraform.sh +2 -1
  17. data/.cody/shared/script/update/gemfile.sh +2 -0
  18. data/.cody/unit/project.rb +2 -2
  19. data/.gitignore +2 -0
  20. data/.pipedream/pipeline.rb +3 -3
  21. data/CHANGELOG.md +24 -0
  22. data/exe/terraspace +0 -7
  23. data/lib/templates/base/project/config/app.rb +3 -0
  24. data/lib/templates/plugin/ci/%gem_name%.gemspec.tt +32 -0
  25. data/lib/templates/plugin/ci/.gitignore +12 -0
  26. data/lib/templates/plugin/{.rspec → ci/.rspec} +0 -0
  27. data/lib/templates/plugin/ci/.rubocop.yml +13 -0
  28. data/lib/templates/plugin/ci/CHANGELOG.md.tt +5 -0
  29. data/lib/templates/plugin/ci/Gemfile +10 -0
  30. data/lib/templates/plugin/ci/LICENSE.txt +21 -0
  31. data/lib/templates/plugin/ci/README.md.tt +19 -0
  32. data/lib/templates/plugin/ci/Rakefile +12 -0
  33. data/lib/templates/plugin/ci/lib/%gem_name%/autoloader.rb.tt +23 -0
  34. data/lib/templates/plugin/ci/lib/%gem_name%/interface.rb.tt +15 -0
  35. data/lib/templates/plugin/ci/lib/%gem_name%/pr.rb.tt +15 -0
  36. data/lib/templates/plugin/ci/lib/%gem_name%/vars.rb.tt +26 -0
  37. data/lib/templates/plugin/ci/lib/%gem_name%/version.rb.tt +5 -0
  38. data/lib/templates/plugin/ci/lib/%gem_name%.rb.tt +17 -0
  39. data/lib/templates/plugin/ci/spec/%gem_name%_spec.rb.tt +7 -0
  40. data/lib/templates/plugin/ci/spec/spec_helper.rb.tt +15 -0
  41. data/lib/templates/plugin/{.gitignore → core/.gitignore} +0 -0
  42. data/lib/templates/plugin/core/.rspec +3 -0
  43. data/lib/templates/plugin/{CHANGELOG.md → core/CHANGELOG.md} +0 -0
  44. data/lib/templates/plugin/{Gemfile → core/Gemfile} +0 -0
  45. data/lib/templates/plugin/{LICENSE.txt → core/LICENSE.txt} +0 -0
  46. data/lib/templates/plugin/{README.md.tt → core/README.md.tt} +0 -0
  47. data/lib/templates/plugin/{Rakefile → core/Rakefile} +0 -0
  48. data/lib/templates/plugin/{bin → core/bin}/console.tt +0 -0
  49. data/lib/templates/plugin/{bin → core/bin}/setup +0 -0
  50. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/module/main.tf +0 -0
  51. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/module/outputs.tf +0 -0
  52. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/module/variables.tf +0 -0
  53. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/project/config/terraform/backend.tf.tt +0 -0
  54. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/project/config/terraform/provider.tf +0 -0
  55. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/stack/main.tf +0 -0
  56. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/stack/outputs.tf +0 -0
  57. data/lib/templates/plugin/{lib → core/lib}/templates/hcl/stack/variables.tf +0 -0
  58. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/module/main.rb +0 -0
  59. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/module/outputs.rb +0 -0
  60. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/module/variables.rb +0 -0
  61. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/project/config/terraform/backend.rb.tt +0 -0
  62. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/project/config/terraform/provider.rb +0 -0
  63. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/stack/main.rb +0 -0
  64. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/stack/outputs.rb +0 -0
  65. data/lib/templates/plugin/{lib → core/lib}/templates/ruby/stack/variables.rb +0 -0
  66. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/.rspec +0 -0
  67. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/Gemfile +0 -0
  68. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/spec/fixtures/stack/main.tf +0 -0
  69. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/spec/fixtures/stack/outputs.tf +0 -0
  70. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/spec/fixtures/stack/variables.tf +0 -0
  71. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/spec/main_spec.rb +0 -0
  72. data/lib/templates/plugin/{lib → core/lib}/templates/test/rspec/module/test/spec/spec_helper.rb +0 -0
  73. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/autoloader.rb.tt +0 -0
  74. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/clients.rb.tt +0 -0
  75. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/interfaces/backend.rb.tt +0 -0
  76. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/interfaces/config.rb.tt +0 -0
  77. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/interfaces/expander.rb.tt +0 -0
  78. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/interfaces/layer.rb.tt +0 -0
  79. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%/version.rb.tt +0 -0
  80. data/lib/templates/plugin/{lib → core/lib}/terraspace_plugin_%name%.rb.tt +0 -0
  81. data/lib/templates/plugin/{spec → core/spec}/spec_helper.rb.tt +0 -0
  82. data/lib/templates/plugin/{spec → core/spec}/terraspace_provider_%name%_spec.rb.tt +0 -0
  83. data/lib/templates/plugin/{terraspace_plugin_%name%.gemspec.tt → core/terraspace_plugin_%name%.gemspec.tt} +0 -0
  84. data/lib/terraspace/all/runner.rb +2 -0
  85. data/lib/terraspace/all/summary.rb +2 -0
  86. data/lib/terraspace/app.rb +23 -4
  87. data/lib/terraspace/builder.rb +1 -1
  88. data/lib/terraspace/cli/concerns/plan_path.rb +8 -0
  89. data/lib/terraspace/cli/down.rb +4 -0
  90. data/lib/terraspace/cli/init.rb +1 -1
  91. data/lib/terraspace/cli/new/ci.rb +121 -0
  92. data/lib/terraspace/cli/new/example.rb +1 -1
  93. data/lib/terraspace/cli/new/helpers/plugin_gem.rb +1 -1
  94. data/lib/terraspace/cli/new/plugin/ci.rb +46 -0
  95. data/lib/terraspace/cli/new/plugin/core.rb +26 -0
  96. data/lib/terraspace/cli/new/plugin/helper.rb +4 -1
  97. data/lib/terraspace/cli/new/plugin.rb +8 -15
  98. data/lib/terraspace/cli/new/test.rb +1 -1
  99. data/lib/terraspace/cli/new.rb +17 -13
  100. data/lib/terraspace/cli/plan.rb +13 -0
  101. data/lib/terraspace/cli/setup/check.rb +8 -0
  102. data/lib/terraspace/cli/up.rb +8 -12
  103. data/lib/terraspace/cli.rb +2 -2
  104. data/lib/terraspace/cloud/api/cani.rb +30 -0
  105. data/lib/terraspace/cloud/api/concern/errors.rb +12 -0
  106. data/lib/terraspace/cloud/api/concern/record.rb +18 -0
  107. data/lib/terraspace/cloud/api/concern.rb +38 -0
  108. data/lib/terraspace/cloud/api/http_methods.rb +116 -0
  109. data/lib/terraspace/cloud/api/validate.rb +24 -0
  110. data/lib/terraspace/cloud/api.rb +33 -0
  111. data/lib/terraspace/cloud/base.rb +97 -0
  112. data/lib/terraspace/cloud/ci/generic.rb +25 -0
  113. data/lib/terraspace/cloud/ci/manual.rb +81 -0
  114. data/lib/terraspace/cloud/ci/vcs/base.rb +36 -0
  115. data/lib/terraspace/cloud/ci/vcs/bitbucket.rb +11 -0
  116. data/lib/terraspace/cloud/ci/vcs/github.rb +11 -0
  117. data/lib/terraspace/cloud/ci/vcs/gitlab.rb +11 -0
  118. data/lib/terraspace/cloud/ci/vcs.rb +18 -0
  119. data/lib/terraspace/cloud/ci.rb +56 -0
  120. data/lib/terraspace/cloud/context.rb +14 -0
  121. data/lib/terraspace/cloud/folder/base.rb +17 -0
  122. data/lib/terraspace/cloud/folder/package.rb +33 -0
  123. data/lib/terraspace/cloud/folder/tidy.rb +54 -0
  124. data/lib/terraspace/cloud/folder/uploader.rb +37 -0
  125. data/lib/terraspace/cloud/folder.rb +11 -0
  126. data/lib/terraspace/cloud/plan.rb +47 -0
  127. data/lib/terraspace/cloud/update.rb +37 -0
  128. data/lib/terraspace/command.rb +16 -1
  129. data/lib/terraspace/compiler/dsl/syntax/mod.rb +2 -2
  130. data/lib/terraspace/compiler/dsl/syntax/tfvar.rb +1 -1
  131. data/lib/terraspace/compiler/expander/backend.rb +1 -1
  132. data/lib/terraspace/compiler/expander.rb +1 -1
  133. data/lib/terraspace/compiler/strategy/tfvar/layer.rb +56 -29
  134. data/lib/terraspace/core.rb +36 -3
  135. data/lib/terraspace/ext/core/module.rb +9 -4
  136. data/lib/terraspace/hooks/builder.rb +1 -1
  137. data/lib/terraspace/logger.rb +32 -5
  138. data/lib/terraspace/mod.rb +21 -9
  139. data/lib/terraspace/plugin/expander/interface.rb +15 -11
  140. data/lib/terraspace/plugin.rb +14 -5
  141. data/lib/terraspace/shell.rb +7 -2
  142. data/lib/terraspace/terraform/args/thor.rb +8 -2
  143. data/lib/terraspace/terraform/ihooks/after/apply.rb +8 -0
  144. data/lib/terraspace/terraform/ihooks/after/destroy.rb +8 -0
  145. data/lib/terraspace/terraform/ihooks/after/plan.rb +31 -2
  146. data/lib/terraspace/terraform/ihooks/base.rb +7 -3
  147. data/lib/terraspace/terraform/ihooks/before/apply.rb +8 -0
  148. data/lib/terraspace/terraform/ihooks/before/destroy.rb +8 -0
  149. data/lib/terraspace/terraform/ihooks/before/plan.rb +11 -3
  150. data/lib/terraspace/terraform/runner.rb +19 -5
  151. data/lib/terraspace/version.rb +1 -1
  152. data/lib/terraspace.rb +2 -0
  153. data/terraspace.gemspec +1 -0
  154. metadata +116 -52
  155. data/.pipedream/schedule.rb +0 -3
@@ -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
@@ -58,7 +58,7 @@ module Terraspace::Plugin::Expander
58
58
  #
59
59
  # cache_dir:
60
60
  #
61
- # :CACHE_ROOT/:REGION/:ENV/:BUILD_DIR/
61
+ # :REGION/:ENV/:BUILD_DIR/
62
62
  #
63
63
  # s3 backend key:
64
64
  #
@@ -69,21 +69,24 @@ module Terraspace::Plugin::Expander
69
69
  # :MOD_NAME-:ENV-:REGION-:INSTANCE
70
70
  #
71
71
  def strip(string)
72
- string.sub(/^-+/,'').sub(/-+$/,'') # remove leading and trailing -
73
- .sub(%r{/+$},'') # only remove trailing / or else /home/ec2-user => home/ec2-user
72
+ string.sub(%r{/+$},'') # only remove trailing / or else /home/ec2-user => home/ec2-user
74
73
  .sub(/:\/\//, 'TMP_KEEP_HTTP') # so we can keep ://. IE: https:// or http://
75
74
  .gsub(%r{/+},'/') # remove double slashes are more. IE: // -> / Useful of region is '' in generic expander
76
75
  .sub('TMP_KEEP_HTTP', '://') # restore :// IE: https:// or http://
76
+ .sub(/^-+/,'').sub(/-+$/,'') # remove leading and trailing -
77
77
  end
78
78
 
79
79
  def var_value(unexpanded)
80
- name = unexpanded.sub(':','').downcase
81
- if respond_to?(name)
82
- value = send(name).to_s
80
+ name = unexpanded.sub(':','')
81
+ downcase = name.downcase
82
+ if respond_to?(downcase)
83
+ value = send(downcase).to_s
84
+ elsif !ENV[name].blank?
85
+ return ENV[name]
83
86
  else
84
87
  return unexpanded
85
88
  end
86
- if name == "namespace" && Terraspace.config.layering.enable_names.expansion
89
+ if downcase == "namespace" && Terraspace.config.layering.enable_names.expansion
87
90
  value = friendly_name(value)
88
91
  end
89
92
  value
@@ -93,9 +96,10 @@ module Terraspace::Plugin::Expander
93
96
  @mod.name
94
97
  end
95
98
 
96
- def env
97
- Terraspace.env
98
- end
99
+ def app; Terraspace.app ; end
100
+ def role; Terraspace.role ; end
101
+ def env; Terraspace.env ; end
102
+ def extra; Terraspace.extra ; end
99
103
 
100
104
  def type_instance
101
105
  [type, instance].reject { |s| s.blank? }.join('-')
@@ -111,7 +115,7 @@ module Terraspace::Plugin::Expander
111
115
  end
112
116
 
113
117
  # So default config works:
114
- # config.cache_dir = ":CACHE_ROOT/:REGION/:ENV/:BUILD_DIR"
118
+ # config.cache_dir = ":REGION/:ENV/:BUILD_DIR"
115
119
  # For when folks configure it with the http backend for non-cloud providers
116
120
  # The double slash // will be replace with a single slash in expander/interface.rb
117
121
  def region
@@ -7,7 +7,7 @@ module Terraspace
7
7
  # Example meta:
8
8
  #
9
9
  # {
10
- # "aws => {root: "/path", backend: "s3"}
10
+ # "aws" => {root: "/path", backend: "s3"}
11
11
  # "google" => {root: "/path", backend: "gcs"},
12
12
  # }
13
13
  #
@@ -16,6 +16,10 @@ module Terraspace
16
16
  @@meta
17
17
  end
18
18
 
19
+ def register(plugin, data)
20
+ @@meta[plugin] = data
21
+ end
22
+
19
23
  def config_classes
20
24
  @@meta.map { |plugin, data| data[:config_class] }.compact
21
25
  end
@@ -50,10 +54,6 @@ module Terraspace
50
54
  end
51
55
  end
52
56
 
53
- def register(plugin, data)
54
- @@meta[plugin] = data
55
- end
56
-
57
57
  # Example return:
58
58
  #
59
59
  # TerraspacePluginAws::Interfaces::Backend
@@ -72,6 +72,15 @@ module Terraspace
72
72
  end
73
73
  memoize :find_with
74
74
 
75
+ # Returns simple String: aws
76
+ def autodetect
77
+ plugins = meta.keys
78
+ precedence = %w[aws azurerm google]
79
+ precedence.find do |p|
80
+ plugins.include?(p)
81
+ end
82
+ end
83
+
75
84
  extend self
76
85
  end
77
86
  end
@@ -34,6 +34,11 @@ module Terraspace
34
34
 
35
35
  def popen3(env)
36
36
  Open3.popen3(env, @command, chdir: @mod.cache_dir) do |stdin, stdout, stderr, wait_thread|
37
+ # interesting. simply handling the trap and doing nothing works
38
+ # Think its because ctrl-c is sent to both processes.
39
+ # 1. we do nothing in here in the parent process
40
+ # 2. in the child process the ctrl-c gets sent directly to the terraform command
41
+ Signal.trap("INT") { }
37
42
  handle_streams(stdin, stdout, stderr)
38
43
  status = wait_thread.value.exitstatus
39
44
  exit_status(status)
@@ -112,6 +117,7 @@ module Terraspace
112
117
  end
113
118
  if matched
114
119
  answer = $stdin.gets
120
+ logger.stdin_capture(answer.strip)
115
121
  stdin.write_nonblock(answer)
116
122
  end
117
123
  end
@@ -139,8 +145,7 @@ module Terraspace
139
145
  if @error && @error.known?
140
146
  raise @error.instance
141
147
  elsif exit_on_fail
142
- logger.error "Error running command: #{@command}".color(:red)
143
- exit status
148
+ raise ShellError.new("Error running command: #{@command}")
144
149
  end
145
150
  end
146
151
  end