terraspace 1.0.6 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/lib/templates/base/project/.gitignore +1 -0
  4. data/lib/templates/base/project/config/app.rb +1 -1
  5. data/lib/terraspace/all/grapher.rb +11 -3
  6. data/lib/terraspace/all/runner.rb +17 -10
  7. data/lib/terraspace/app.rb +1 -0
  8. data/lib/terraspace/builder.rb +11 -33
  9. data/lib/terraspace/cli/all.rb +2 -0
  10. data/lib/terraspace/cli/build/placeholder.rb +3 -1
  11. data/lib/terraspace/cli/help/all/plan.md +11 -1
  12. data/lib/terraspace/cli/help/all/up.md +10 -0
  13. data/lib/terraspace/cli/help/plan.md +9 -1
  14. data/lib/terraspace/cli/help/up.md +9 -1
  15. data/lib/terraspace/cli/init.rb +1 -1
  16. data/lib/terraspace/cli.rb +3 -2
  17. data/lib/terraspace/compiler/dirs_concern.rb +6 -1
  18. data/lib/terraspace/compiler/{builder → perform}/skip.rb +1 -1
  19. data/lib/terraspace/compiler/{builder.rb → perform.rb} +21 -20
  20. data/lib/terraspace/compiler/select.rb +2 -7
  21. data/lib/terraspace/compiler/strategy/tfvar.rb +2 -1
  22. data/lib/terraspace/dependency/resolver.rb +19 -0
  23. data/lib/terraspace/terraform/args/expand.rb +25 -0
  24. data/lib/terraspace/terraform/args/thor.rb +14 -11
  25. data/lib/terraspace/terraform/ihooks/after/plan.rb +2 -2
  26. data/lib/terraspace/terraform/ihooks/base.rb +5 -0
  27. data/lib/terraspace/terraform/ihooks/before/plan.rb +3 -5
  28. data/lib/terraspace/terraform/remote_state/fetcher.rb +10 -3
  29. data/lib/terraspace/terraform/remote_state/marker/output.rb +1 -1
  30. data/lib/terraspace/version.rb +1 -1
  31. data/spec/terraspace/all/runner_spec.rb +1 -0
  32. data/terraspace.gemspec +1 -1
  33. metadata +7 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57c0c4f1c1fce5c1dd8fcd7447f5bc5a82e17d66e5353f4d092d492c137021e5
4
- data.tar.gz: 81a4ba08410e898a1dd057625ca556392e746c4b48a3453874472c60424c3949
3
+ metadata.gz: c6c8809128dbbbf51d0f654c92784a879c88cab49d31e15e2a1d827c4b40d051
4
+ data.tar.gz: c557bd4692fddd9a1bf859a986566780cb6ccd6024dcdb3e7979138a42f1e00e
5
5
  SHA512:
6
- metadata.gz: e629795fd75ad23d343ec28390e76756650f11740c6b380ad3df87cb216f7a998acfea282ebb1a1399343c3db2b5abe452705bb82fd61e76a8cd1a2ca258300b
7
- data.tar.gz: 7104217cb56ee3ba2317d4d2abe0d4f760fe35de359903349a97bef4c2de3c92aaff3e0605789fd9e970f8a841a2203dedc9d740d9bddf127d2555a3f3818f30
6
+ metadata.gz: 8b28d7d1b8565fa0f646fda57163b6d21486db7e7f704ebe657a219335e66504206769e487cec9dc3f30da06ac793e28d81b1709ebd4c32a39a70a5d8668be05
7
+ data.tar.gz: 1a09efc8d460c9267371aca9dbb130e4dbb1c9268ea870e19d6a9734aa48e0134b3cee09aedfe894acfd2c7e956a1fed4bf7b38cad9cf9b13b935802bd2fa4a7
data/CHANGELOG.md CHANGED
@@ -3,6 +3,11 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  This project *loosely tries* to adhere to [Semantic Versioning](http://semver.org/), even before v1.0.
5
5
 
6
+ ## [1.1.0] - 2022-01-30
7
+ - [#196](https://github.com/boltops-tools/terraspace/pull/196) terraspace all: build modules in batches and only each specific stack
8
+ - [#197](https://github.com/boltops-tools/terraspace/pull/197) all plan --output and all up --plan options
9
+ - simplify starter config/app.rb
10
+
6
11
  ## [1.0.6] - 2022-01-24
7
12
  - [#195](https://github.com/boltops-tools/terraspace/pull/195) improve autodetection for plugin expander for backend like remote
8
13
 
@@ -5,6 +5,7 @@ terraform.tfvars
5
5
  *.tfstate*
6
6
 
7
7
  .terraspace-cache
8
+ *.plan
8
9
 
9
10
  # OS X files
10
11
  .history
@@ -1,4 +1,4 @@
1
+ # Docs: https://terraspace.cloud/docs/config/reference/
1
2
  Terraspace.configure do |config|
2
3
  config.logger.level = :info
3
- config.test_framework = "rspec"
4
4
  end
@@ -3,14 +3,13 @@ require "tty-tree"
3
3
 
4
4
  module Terraspace::All
5
5
  class Grapher < Base
6
+ include Terraspace::Compiler::DirsConcern
6
7
  include Terraspace::Util::Logging
7
8
 
8
9
  def run
9
10
  check_graphviz!
10
11
  logger.info "Building graph..."
11
- builder = Terraspace::Builder.new(@options.merge(mod: "placeholder", quiet: true, draw_full_graph: draw_full_graph))
12
- builder.run
13
- graph = builder.graph
12
+ graph = build_graph
14
13
  if @options[:format] == "text"
15
14
  text(graph.top_nodes)
16
15
  else
@@ -18,6 +17,15 @@ module Terraspace::All
18
17
  end
19
18
  end
20
19
 
20
+ def build_graph
21
+ resolver = Terraspace::Dependency::Resolver.new(@options.merge(quiet: true, draw_full_graph: draw_full_graph))
22
+ resolver.resolve
23
+ dependencies = Terraspace::Dependency::Registry.data # populated after build_unresolved
24
+ graph = Terraspace::Dependency::Graph.new(stack_names, dependencies, @options)
25
+ graph.build
26
+ graph
27
+ end
28
+
21
29
  def text(nodes)
22
30
  Rainbow.enabled = false unless @options[:full]
23
31
  data = build_tree_data(nodes)
@@ -1,6 +1,7 @@
1
1
  module Terraspace::All
2
2
  class Runner < Base
3
3
  include Terraspace::Util
4
+ extend Memoist
4
5
 
5
6
  def initialize(command, options={})
6
7
  @command, @options = command, options
@@ -21,32 +22,27 @@ module Terraspace::All
21
22
  end
22
23
 
23
24
  def build_batches
24
- @batches = run_builder(quiet: false)
25
+ @batches = Terraspace::Dependency::Resolver.new(@options).resolve
25
26
  @batches.reverse! if @command == "down"
26
27
  @batches
27
28
  end
28
29
 
29
30
  def deploy_batches
30
31
  truncate_logs if ENV['TS_TRUNCATE_LOGS']
32
+ build_modules
31
33
  @batches.each_with_index do |batch,i|
32
34
  logger.info "Batch Run #{i+1}:"
33
- run_builder unless i == 0 # already handled by build_batches the first time
34
35
  deploy_batch(batch)
35
36
  end
36
37
  end
37
38
 
38
- # Should run after each batch run. run_builder also calls replace_outputs.
39
- # Important: rebuild from source so placeholders are in place.
40
- def run_builder(quiet: true)
41
- Terraspace::Builder.new(@options.merge(mod: "placeholder", quiet: quiet)).run
42
- end
43
-
44
39
  def deploy_batch(batch)
45
40
  @pids = {} # stores child processes pids. map of pid to mod_name, reset this list on each batch run
46
41
  concurrency = Terraspace.config.all.concurrency
47
42
  batch.sort_by(&:name).each_slice(concurrency) do |slice|
48
43
  slice.each do |node|
49
44
  pid = fork do
45
+ build_stack(node.name)
50
46
  run_terraspace(node.name)
51
47
  end
52
48
  @pids[pid] = node.name # store mod_name mapping
@@ -57,6 +53,16 @@ module Terraspace::All
57
53
  report_errors # reports finall errors and possibly exit
58
54
  end
59
55
 
56
+ def build_modules
57
+ builder = Terraspace::Builder.new(@options.merge(mod: "placeholder", quiet: true, clean: true))
58
+ builder.build(modules: true, stack: false)
59
+ end
60
+
61
+ def build_stack(name)
62
+ builder = Terraspace::Builder.new(@options.merge(mod: name, quiet: true, clean: false))
63
+ builder.build(modules: false, stack: true)
64
+ end
65
+
60
66
  def wait_for_child_proccess
61
67
  @errors = [] # stores child processes pids that errored
62
68
  @pids.each do |pid, _|
@@ -70,7 +76,7 @@ module Terraspace::All
70
76
  @errors.each do |pid|
71
77
  mod_name = @pids[pid]
72
78
  terraspace_command = terraspace_command(mod_name)
73
- logger.error "Error running: #{terraspace_command}. Check logs and fix the error.".color(:red)
79
+ logger.error "Error running: #{terraspace_command}. Fix the error above or check logs for the error.".color(:red)
74
80
  end
75
81
  unless @errors.empty?
76
82
  exit 2 if exit_on_fail?
@@ -97,7 +103,8 @@ module Terraspace::All
97
103
  log_path: log_path(mod_name),
98
104
  terraspace_command: terraspace_command(mod_name),
99
105
  }
100
- Summary.new(data).run
106
+ # Its possible for log file to not get created if RemoteState::Fetcher#validate! fails
107
+ Summary.new(data).run if File.exist?(data[:log_path])
101
108
  end
102
109
  end
103
110
 
@@ -17,6 +17,7 @@ module Terraspace
17
17
  config.all.concurrency = 5
18
18
  config.all.exit_on_fail = ActiveSupport::OrderedOptions.new
19
19
  config.all.exit_on_fail.down = true
20
+ config.all.exit_on_fail.plan = true
20
21
  config.all.exit_on_fail.up = true
21
22
 
22
23
  config.allow = ActiveSupport::OrderedOptions.new
@@ -4,64 +4,42 @@ module Terraspace
4
4
  include Compiler::DirsConcern
5
5
  include Hooks::Concern
6
6
 
7
- attr_reader :graph
8
-
9
7
  def run
10
8
  return if @options[:build] == false
11
9
  Terraspace::CLI::Setup::Check.check!
10
+ check_allow!
12
11
  @mod.root_module = true
13
12
  clean
13
+ build
14
+ end
15
+
16
+ def build(modules: true, stack: true)
14
17
  build_dir = Util.pretty_path(@mod.cache_dir)
15
18
  placeholder_stack_message
16
19
  logger.info "Building #{build_dir}" unless @options[:quiet] # from terraspace all
17
-
18
- batches = nil
19
20
  FileUtils.mkdir_p(@mod.cache_dir) # so terraspace before build hooks work
20
21
  run_hooks("terraspace.rb", "build") do
21
- check_allow!
22
- build_unresolved
23
- batches = build_batches
24
- build_all
22
+ build_dir("modules") if modules
23
+ build_root_module if stack
25
24
  logger.info "Built in #{build_dir}" unless @options[:quiet] # from terraspace all
26
25
  end
27
- batches
28
26
  end
29
27
 
30
28
  def check_allow!
31
29
  Allow.new(@mod).check!
32
30
  end
33
31
 
34
- # Builds dependency graph and returns the batches to run
35
- def build_batches
36
- dependencies = Terraspace::Dependency::Registry.data # populated after build_unresolved
37
- @graph = Terraspace::Dependency::Graph.new(stack_names, dependencies, @options)
38
- @graph.build
39
- end
40
-
41
- def build_all
42
- # At this point dependencies have been resolved.
43
- Terraspace::Terraform::RemoteState::Fetcher.flush!
44
- @resolved = true
45
- build_unresolved
46
- end
47
-
48
- def build_unresolved
49
- build_dir("modules")
50
- build_dir("stacks")
51
- build_root_module
52
- end
53
-
54
32
  def build_root_module
55
- @mod.resolved = @resolved
56
- Compiler::Builder.new(@mod).build
33
+ @mod.resolved = true
34
+ Compiler::Perform.new(@mod).compile
57
35
  end
58
36
 
59
37
  def build_dir(type_dir)
60
38
  with_each_mod(type_dir) do |mod|
61
- mod.resolved = @resolved
39
+ mod.resolved = true
62
40
  is_root_module = mod.cache_dir == @mod.cache_dir
63
41
  next if is_root_module # handled by build_root_module
64
- Compiler::Builder.new(mod).build
42
+ Compiler::Perform.new(mod).compile
65
43
  end
66
44
  end
67
45
 
@@ -38,6 +38,7 @@ class Terraspace::CLI
38
38
 
39
39
  desc "plan", "Run plan for all or multiple stacks."
40
40
  long_desc Help.text("all/plan")
41
+ option :out, aliases: :o, desc: "Output path. Can be a pattern like :MOD_NAME.plan"
41
42
  def plan(*stacks)
42
43
  Terraspace::All::Runner.new("plan", @options.merge(stacks: stacks)).run
43
44
  end
@@ -56,6 +57,7 @@ class Terraspace::CLI
56
57
 
57
58
  desc "up", "Deploy all or multiple stacks."
58
59
  long_desc Help.text("all/up")
60
+ option :plan, desc: "Plan path. Can be a pattern like :MOD_NAME.plan"
59
61
  def up(*stacks)
60
62
  Terraspace::All::Runner.new("up", @options.merge(stacks: stacks)).run
61
63
  end
@@ -3,6 +3,7 @@
3
3
  # It's useful for the summary command.
4
4
  module Terraspace::CLI::Build
5
5
  class Placeholder
6
+ include Terraspace::Compiler::DirsConcern
6
7
  include Terraspace::Util::Logging
7
8
 
8
9
  def initialize(options={})
@@ -28,7 +29,8 @@ module Terraspace::CLI::Build
28
29
  def find_stack
29
30
  stack_paths = Dir.glob("{app,vendor}/stacks/*")
30
31
  stack_paths.select! do |path|
31
- select = Terraspace::Compiler::Select.new(path)
32
+ stack_name = extract_stack_name(path)
33
+ select = Terraspace::Compiler::Select.new(stack_name)
32
34
  select.selected?
33
35
  end
34
36
  mod_path = stack_paths.last
@@ -22,4 +22,14 @@
22
22
  terraspace plan a1: Plan: 2 to add, 0 to change, 0 to destroy.
23
23
  terraspace plan a1: Changes to Outputs:
24
24
  Time took: 11s
25
- $
25
+ $
26
+
27
+ ## Using Plan Outputs
28
+
29
+ Using plan output path. You can specify an output path for the plan that contains pattern for expansion. Example:
30
+
31
+ $ terraspace all plan --out ":MOD_NAME.plan"
32
+
33
+ You can then use this later in terraspace up:
34
+
35
+ $ terraspace all up --plan ":MOD_NAME.plan"
@@ -25,3 +25,13 @@ Once you confirm, Terraspace deploys the batches in parallel. Essentially, Terra
25
25
  Time took: 25s
26
26
 
27
27
  Terraspace provides a reduced-noise summary of the runs. The full logs are also written for further inspection and debugging. The [terraspace log](https://terraspace.cloud/reference/terraspace-log/) command is useful for viewing the logs.
28
+
29
+ ## Using Plans
30
+
31
+ Using plan output path. You can specify an output path for the plan that contains pattern for expansion. Example:
32
+
33
+ $ terraspace all plan --out ":MOD_NAME.plan"
34
+
35
+ You can then use this later in terraspace up:
36
+
37
+ $ terraspace all up --plan ":MOD_NAME.plan"
@@ -26,4 +26,12 @@
26
26
  can't guarantee that exactly these actions will be performed if
27
27
  "terraform apply" is subsequently run.
28
28
 
29
- $
29
+ $
30
+
31
+ Using plan output path. You can specify an output path for the plan. Example:
32
+
33
+ $ terraspace plan demo --out "my.plan"
34
+
35
+ You can then use this later in terraspace up:
36
+
37
+ $ terraspace up demo --plan "my.plan"
@@ -27,4 +27,12 @@
27
27
 
28
28
  bucket_name = bucket-trusty-marmoset
29
29
  Time took: 39s
30
- $
30
+ $
31
+
32
+ Using plan output path. You can specify an output path for the plan. Example:
33
+
34
+ $ terraspace plan demo --out "my.plan"
35
+
36
+ You can then use this later in terraspace up:
37
+
38
+ $ terraspace up demo --plan "my.plan"
@@ -52,7 +52,7 @@ class Terraspace::CLI
52
52
  return if meta['Dir'] == '.' # root is already built
53
53
 
54
54
  remote_mod = Terraspace::Mod::Remote.new(meta, @mod)
55
- Terraspace::Compiler::Builder.new(remote_mod).build
55
+ Terraspace::Compiler::Perform.new(remote_mod).build
56
56
  end
57
57
 
58
58
  def auto?
@@ -9,7 +9,7 @@ module Terraspace
9
9
  option :yes, aliases: :y, type: :boolean, desc: "-auto-approve the terraform apply"
10
10
  }
11
11
  out_option = Proc.new {
12
- option :out, aliases: :o, desc: "Write the output to path"
12
+ option :out, aliases: :o, desc: "Output path. Can be a pattern like :MOD_NAME.plan"
13
13
  }
14
14
  input_option = Proc.new {
15
15
  option :input, type: :boolean, desc: "Ask for input for variables if not directly set."
@@ -55,6 +55,7 @@ module Terraspace
55
55
  option :quiet, type: :boolean, desc: "quiet output"
56
56
  instance_option.call
57
57
  yes_option.call
58
+ option :clean, type: :boolean, default: nil, desc: "Whether or not clean out .terraspace-cache folder first", hide: true
58
59
  def build(mod="placeholder")
59
60
  Terraspace::Builder.new(options.merge(mod: mod)).run # building any stack builds them all
60
61
  end
@@ -190,7 +191,7 @@ module Terraspace
190
191
  desc "show STACK", "Run show."
191
192
  long_desc Help.text(:show)
192
193
  instance_option.call
193
- option :plan, desc: "path to created.plan"
194
+ option :plan, desc: "Plan path. Can be a pattern like :MOD_NAME.plan"
194
195
  def show(mod, *args)
195
196
  Commander.new("show", options.merge(mod: mod, args: args)).run
196
197
  end
@@ -37,10 +37,15 @@ module Terraspace::Compiler
37
37
  # path /home/ec2-user/environment/downloads/infra/app/stacks/demo
38
38
  def select_stack?(type_dir, path)
39
39
  return true unless type_dir == "stacks"
40
- select = Terraspace::Compiler::Select.new(path)
40
+ stack_name = extract_stack_name(path)
41
+ select = Terraspace::Compiler::Select.new(stack_name)
41
42
  select.selected?
42
43
  end
43
44
 
45
+ def extract_stack_name(path)
46
+ path.sub(%r{.*(app|vendor)/stacks/}, '')
47
+ end
48
+
44
49
  def local_paths(type_dir)
45
50
  dirs("app/#{type_dir}/*") + dirs("vendor/#{type_dir}/*")
46
51
  end
@@ -1,4 +1,4 @@
1
- class Terraspace::Compiler::Builder
1
+ class Terraspace::Compiler::Perform
2
2
  class Skip
3
3
  def initialize(mod, src_path)
4
4
  @mod, @src_path = mod, src_path
@@ -1,5 +1,5 @@
1
1
  module Terraspace::Compiler
2
- class Builder
2
+ class Perform
3
3
  include CommandsConcern
4
4
  include Basename
5
5
 
@@ -7,44 +7,45 @@ module Terraspace::Compiler
7
7
  @mod = mod
8
8
  end
9
9
 
10
- def build
11
- build_config
12
- build_module if @mod.resolved
13
- build_tfvars unless command_is?(:seed) # avoid dependencies being built and erroring when backend bucket doesnt exist
10
+ def compile
11
+ compile_config
12
+ compile_module if @mod.resolved
13
+ compile_tfvars
14
14
  end
15
15
 
16
- # build common config files: provider and backend for the root module
17
- def build_config
18
- return unless build?
19
- build_config_terraform
16
+ # compile common config files: provider and backend for the root module
17
+ def compile_config
18
+ return unless compile?
19
+ compile_config_terraform
20
20
  end
21
21
 
22
- def build_module
22
+ def compile_module
23
23
  with_mod_file do |src_path|
24
- build_mod_file(src_path)
24
+ compile_mod_file(src_path)
25
25
  end
26
26
  end
27
27
 
28
- def build_tfvars
29
- return unless build?
30
- Strategy::Tfvar.new(@mod).run # writer within Strategy to control file ordering
28
+ def compile_tfvars(write: true)
29
+ return unless compile?
30
+ return if command_is?(:seed) # avoid dependencies being built and erroring when backend bucket doesnt exist
31
+ Strategy::Tfvar.new(@mod).run(write: write) # writer within Strategy to control file ordering
31
32
  end
32
33
 
33
34
  private
34
- def build?
35
+ def compile?
35
36
  @mod.type == "stack" || @mod.root_module?
36
37
  end
37
38
 
38
- def build_config_terraform
39
+ def compile_config_terraform
39
40
  expr = "#{Terraspace.root}/config/terraform/**/*"
40
41
  search(expr).each do |path|
41
42
  next unless File.file?(path)
42
43
  next if path.include?('config/terraform/tfvars')
43
- build_config_file(basename(path))
44
+ compile_config_file(basename(path))
44
45
  end
45
46
  end
46
47
 
47
- def build_config_file(file)
48
+ def compile_config_file(file)
48
49
  existing = search("#{@mod.root}/#{file}").first
49
50
  return if existing && existing.ends_with?(".tf") # do not overwrite existing backend.tf, provider.tf, etc
50
51
 
@@ -52,10 +53,10 @@ module Terraspace::Compiler
52
53
  src_path = search("#{@mod.root}/#{basename(file)}").first # existing source. IE: backend.rb in module folder
53
54
  end
54
55
  src_path ||= search("#{Terraspace.root}/config/terraform/#{file}").first
55
- build_mod_file(src_path) if src_path
56
+ compile_mod_file(src_path) if src_path
56
57
  end
57
58
 
58
- def build_mod_file(src_path)
59
+ def compile_mod_file(src_path)
59
60
  content = Strategy::Mod.new(@mod, src_path).run
60
61
  Writer.new(@mod, src_path: src_path).write(content)
61
62
  end
@@ -3,9 +3,8 @@ module Terraspace::Compiler
3
3
  include Terraspace::App::CallableOption::Concern
4
4
  include Terraspace::Util::Logging
5
5
 
6
- def initialize(path)
7
- @path = path
8
- @stack_name = extract_stack_name(path)
6
+ def initialize(stack_name)
7
+ @stack_name = stack_name
9
8
  end
10
9
 
11
10
  def selected?
@@ -61,10 +60,6 @@ module Terraspace::Compiler
61
60
  Terraspace.config
62
61
  end
63
62
 
64
- def extract_stack_name(path)
65
- path.sub(%r{.*(app|vendor)/stacks/}, '')
66
- end
67
-
68
63
  @@ignore_stacks_deprecation_warning = nil
69
64
  def ignore_stacks_deprecation_warning
70
65
  return unless config.all.ignore_stacks
@@ -5,7 +5,7 @@ module Terraspace::Compiler::Strategy
5
5
  @order = 0
6
6
  end
7
7
 
8
- def run
8
+ def run(write: true)
9
9
  layer_paths.each do |layer_path|
10
10
  ext = File.extname(layer_path).sub('.','')
11
11
  klass = strategy_class(ext)
@@ -14,6 +14,7 @@ module Terraspace::Compiler::Strategy
14
14
  strategy = klass.new(@mod, layer_path)
15
15
  content = strategy.run
16
16
 
17
+ next unless write
17
18
  dest_name = ordered_name(layer_path)
18
19
  writer = Terraspace::Compiler::Writer.new(@mod, dest_name: dest_name)
19
20
  writer.write(content)
@@ -0,0 +1,19 @@
1
+ module Terraspace::Dependency
2
+ class Resolver
3
+ include Terraspace::Compiler::DirsConcern
4
+
5
+ def initialize(options={})
6
+ @options = options
7
+ end
8
+
9
+ def resolve
10
+ with_each_mod("stacks") do |mod|
11
+ Terraspace::Compiler::Perform.new(mod).compile_tfvars(write: false)
12
+ end
13
+
14
+ dependencies = Terraspace::Dependency::Registry.data # populated dependencies resolved
15
+ @graph = Terraspace::Dependency::Graph.new(stack_names, dependencies, @options)
16
+ @graph.build # Returns batches to run
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ module Terraspace::Terraform::Args
2
+ class Expand
3
+ class << self
4
+ def option_method(*names)
5
+ names.compact.each do |name|
6
+ option_method_each(name)
7
+ end
8
+ end
9
+
10
+ def option_method_each(name)
11
+ define_method name do
12
+ return unless @options[name]
13
+ expander = Terraspace::Compiler::Expander.autodetect(@mod)
14
+ expander.expansion(@options[name]) # pattern is a String that contains placeholders for substitutions
15
+ end
16
+ end
17
+ end
18
+
19
+ def initialize(mod, options={})
20
+ @mod, @options = mod, options
21
+ end
22
+
23
+ option_method :plan, :out
24
+ end
25
+ end
@@ -2,6 +2,8 @@ require "tempfile"
2
2
 
3
3
  module Terraspace::Terraform::Args
4
4
  class Thor
5
+ extend Memoist
6
+
5
7
  def initialize(mod, name, options={})
6
8
  @mod, @name, @options = mod, name.underscore, options
7
9
  @quiet = @options[:quiet].nil? ? true : @options[:quiet]
@@ -48,7 +50,7 @@ module Terraspace::Terraform::Args
48
50
  args << input_option
49
51
 
50
52
  # must be at the end
51
- plan = @options[:plan]
53
+ plan = expand.plan
52
54
  if plan
53
55
  copy_to_cache(plan)
54
56
  args << " #{plan}"
@@ -81,7 +83,7 @@ module Terraspace::Terraform::Args
81
83
 
82
84
  def output_args
83
85
  args = []
84
- args << "> #{expanded_out}" if @options[:out]
86
+ args << "> #{expand.out}" if expand.out
85
87
  args
86
88
  end
87
89
 
@@ -89,26 +91,22 @@ module Terraspace::Terraform::Args
89
91
  args = []
90
92
  args << input_option
91
93
  args << "-destroy" if @options[:destroy]
92
- args << "-out #{expanded_out}" if @options[:out]
93
- # Note: based on the @options[:out] will run an internal hook to copy plan
94
+ args << "-out #{expand.out}" if expand.out
95
+ # Note: based on the expand.out will run an internal hook to copy plan
94
96
  # file back up to the root project folder for use. Think this is convenient and expected behavior.
95
97
  args
96
98
  end
97
99
 
98
100
  def show_args
99
101
  args = []
100
- plan = @options[:plan]
102
+ plan = expand.plan
101
103
  if plan
102
- copy_to_cache(@options[:plan])
103
- args << " #{@options[:plan]}" # terraform show /path/to/plan
104
+ copy_to_cache(expand.plan)
105
+ args << " #{expand.plan}" # terraform show /path/to/plan
104
106
  end
105
107
  args
106
108
  end
107
109
 
108
- def expanded_out
109
- @options[:out]
110
- end
111
-
112
110
  def destroy_args
113
111
  auto_approve_arg
114
112
  end
@@ -147,5 +145,10 @@ module Terraspace::Terraform::Args
147
145
  FileUtils.mkdir_p(File.dirname(dest))
148
146
  FileUtils.cp(src, dest) unless same_file?(src, dest)
149
147
  end
148
+
149
+ def expand
150
+ Terraspace::Terraform::Args::Expand.new(@mod, @options)
151
+ end
152
+ memoize :expand
150
153
  end
151
154
  end
@@ -1,8 +1,8 @@
1
1
  module Terraspace::Terraform::Ihooks::After
2
2
  class Plan < Terraspace::Terraform::Ihooks::Base
3
3
  def run
4
- return if !@options[:out] || @options[:copy_to_root] == false
5
- copy_to_root(@options[:out])
4
+ return if !out_option || @options[:copy_to_root] == false
5
+ copy_to_root(out_option)
6
6
  end
7
7
 
8
8
  def copy_to_root(file)
@@ -4,5 +4,10 @@ module Terraspace::Terraform::Ihooks
4
4
  @name = name
5
5
  super(options)
6
6
  end
7
+
8
+ def out_option
9
+ expand = Terraspace::Terraform::Args::Expand.new(@mod, @options)
10
+ expand.out
11
+ end
7
12
  end
8
13
  end
@@ -1,12 +1,10 @@
1
1
  module Terraspace::Terraform::Ihooks::Before
2
2
  class Plan < Terraspace::Terraform::Ihooks::Base
3
3
  def run
4
- out = @options[:out]
5
- return unless out
6
- return if out =~ %r{^/} # not need to create parent dir for copy with absolute path
4
+ return unless out_option
5
+ return if out_option =~ %r{^/} # not need to create parent dir for copy with absolute path
7
6
 
8
- out = @options[:out]
9
- name = out.sub("#{Terraspace.root}/",'')
7
+ name = out_option.sub("#{Terraspace.root}/",'')
10
8
  dest = "#{@mod.cache_dir}/#{name}"
11
9
  FileUtils.mkdir_p(File.dirname(dest))
12
10
  end
@@ -2,6 +2,7 @@ module Terraspace::Terraform::RemoteState
2
2
  class Fetcher
3
3
  extend Memoist
4
4
  include Terraspace::Compiler::CommandsConcern
5
+ include Terraspace::Compiler::DirsConcern
5
6
  include Terraspace::Util::Logging
6
7
 
7
8
  def initialize(parent, identifier, options={})
@@ -130,9 +131,15 @@ module Terraspace::Terraform::RemoteState
130
131
 
131
132
  # Note we already validate mod exist at the terraform_output helper. This is just in case that logic changes.
132
133
  def validate!
133
- return if @child.exist?
134
- logger.error "ERROR: stack #{@child.name} not found".color(:red)
135
- exit 1
134
+ unless @child.exist?
135
+ logger.error "ERROR: stack #{@child.name} not found".color(:red)
136
+ exit 1
137
+ end
138
+ select = Terraspace::Compiler::Select.new(@child.name)
139
+ unless select.selected?
140
+ logger.error "ERROR: stack #{@child.name} is configured to not be included. IE: config.all.include_stacks or config.all.exclude_stacks".color(:red)
141
+ exit 1
142
+ end
136
143
  end
137
144
 
138
145
  # Using debug level because all the tfvar files always get evaluated.
@@ -25,7 +25,7 @@ module Terraspace::Terraform::RemoteState::Marker
25
25
  end
26
26
 
27
27
  def warning
28
- logger.warn "WARN: The #{@child_name} stack does not exist".color(:yellow)
28
+ logger.warn "WARN: The #{@child_name} stack does not exist or is configured to be not included. IE: config.all.include_stacks or config.all.exclude_stacks".color(:yellow)
29
29
  caller_line = caller.find { |l| l.include?('.tfvars') }
30
30
  return unless caller_line # specs dont have a tfvars file
31
31
  source_code = PrettyTracer.new(caller_line).source_code
@@ -1,3 +1,3 @@
1
1
  module Terraspace
2
- VERSION = "1.0.6"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -1,6 +1,7 @@
1
1
  describe Terraspace::All::Runner do
2
2
  let(:runner) do
3
3
  runner = described_class.new(command: "up", yes: true)
4
+ allow(runner).to receive(:build_modules)
4
5
  allow(runner).to receive(:build_batches).and_return(batches)
5
6
  allow(runner).to receive(:preview)
6
7
  # Just test to the point of the run_builder and deploy_batch
data/terraspace.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Tung Nguyen"]
10
10
  spec.email = ["tung@boltops.com"]
11
11
  spec.summary = "Terraspace: The Terraspace Framework"
12
- spec.homepage = "https://github.com/boltops-tools/terraspace"
12
+ spec.homepage = "https://terraspace.cloud"
13
13
  spec.license = "Apache-2.0"
14
14
 
15
15
  spec.files = File.directory?('.git') ? `git ls-files`.split($/) : Dir.glob("**/*")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terraspace
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.6
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-24 00:00:00.000000000 Z
11
+ date: 2022-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -602,8 +602,6 @@ files:
602
602
  - lib/terraspace/cli/up.rb
603
603
  - lib/terraspace/command.rb
604
604
  - lib/terraspace/compiler/basename.rb
605
- - lib/terraspace/compiler/builder.rb
606
- - lib/terraspace/compiler/builder/skip.rb
607
605
  - lib/terraspace/compiler/cleaner.rb
608
606
  - lib/terraspace/compiler/cleaner/backend_change.rb
609
607
  - lib/terraspace/compiler/commands_concern.rb
@@ -633,6 +631,8 @@ files:
633
631
  - lib/terraspace/compiler/expander.rb
634
632
  - lib/terraspace/compiler/expander/backend.rb
635
633
  - lib/terraspace/compiler/helper_extender.rb
634
+ - lib/terraspace/compiler/perform.rb
635
+ - lib/terraspace/compiler/perform/skip.rb
636
636
  - lib/terraspace/compiler/select.rb
637
637
  - lib/terraspace/compiler/strategy/abstract_base.rb
638
638
  - lib/terraspace/compiler/strategy/mod.rb
@@ -655,6 +655,7 @@ files:
655
655
  - lib/terraspace/dependency/helper/output.rb
656
656
  - lib/terraspace/dependency/node.rb
657
657
  - lib/terraspace/dependency/registry.rb
658
+ - lib/terraspace/dependency/resolver.rb
658
659
  - lib/terraspace/dotenv.rb
659
660
  - lib/terraspace/ext.rb
660
661
  - lib/terraspace/ext/bundler.rb
@@ -703,6 +704,7 @@ files:
703
704
  - lib/terraspace/terraform/api/workspace.rb
704
705
  - lib/terraspace/terraform/args/custom.rb
705
706
  - lib/terraspace/terraform/args/dsl.rb
707
+ - lib/terraspace/terraform/args/expand.rb
706
708
  - lib/terraspace/terraform/args/pass.rb
707
709
  - lib/terraspace/terraform/args/shorthands.rb
708
710
  - lib/terraspace/terraform/args/thor.rb
@@ -864,7 +866,7 @@ files:
864
866
  - spec/terraspace/terraform/remote_state/marker/output_spec.rb
865
867
  - spec/terraspace/terraform/remote_state/output_proxy_spec.rb
866
868
  - terraspace.gemspec
867
- homepage: https://github.com/boltops-tools/terraspace
869
+ homepage: https://terraspace.cloud
868
870
  licenses:
869
871
  - Apache-2.0
870
872
  metadata: {}