terraspace 0.2.2 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +34 -13
  4. data/lib/templates/base/git_hook/hook.sh +1 -1
  5. data/lib/templates/base/project/.gitignore +1 -0
  6. data/lib/templates/base/project/README.md +17 -0
  7. data/lib/terraspace.rb +4 -0
  8. data/lib/terraspace/all/base.rb +8 -0
  9. data/lib/terraspace/all/grapher.rb +129 -0
  10. data/lib/terraspace/all/preview.rb +43 -0
  11. data/lib/terraspace/all/runner.rb +169 -0
  12. data/lib/terraspace/all/summary.rb +99 -0
  13. data/lib/terraspace/app.rb +32 -9
  14. data/lib/terraspace/booter.rb +9 -0
  15. data/lib/terraspace/builder.rb +67 -20
  16. data/lib/terraspace/cli.rb +39 -12
  17. data/lib/terraspace/cli/all.rb +63 -0
  18. data/lib/terraspace/cli/build/placeholder.rb +2 -5
  19. data/lib/terraspace/cli/bundle.rb +1 -1
  20. data/lib/terraspace/cli/check_setup.rb +17 -5
  21. data/lib/terraspace/cli/cloud.rb +19 -3
  22. data/lib/terraspace/cli/cloud/runs.rb +24 -0
  23. data/lib/terraspace/cli/commander.rb +1 -8
  24. data/lib/terraspace/cli/down.rb +20 -0
  25. data/lib/terraspace/cli/help/cloud/runs/list.md +36 -0
  26. data/lib/terraspace/cli/help/cloud/runs/prune.md +25 -0
  27. data/lib/terraspace/cli/help/cloud/sync.md +19 -0
  28. data/lib/terraspace/cli/help/log.md +42 -0
  29. data/lib/terraspace/cli/init.rb +35 -7
  30. data/lib/terraspace/cli/list.rb +14 -1
  31. data/lib/terraspace/cli/log.rb +112 -0
  32. data/lib/terraspace/cli/log/concern.rb +24 -0
  33. data/lib/terraspace/cli/logs.rb +15 -0
  34. data/lib/terraspace/cli/logs/tasks.rb +32 -0
  35. data/lib/terraspace/cli/new/git_hook.rb +1 -1
  36. data/lib/terraspace/cli/tfc_concern.rb +14 -0
  37. data/lib/terraspace/cli/up.rb +32 -0
  38. data/lib/terraspace/compiler/builder.rb +3 -3
  39. data/lib/terraspace/compiler/cleaner.rb +1 -1
  40. data/lib/terraspace/compiler/cleaner/backend_change.rb +21 -7
  41. data/lib/terraspace/compiler/dirs_concern.rb +47 -0
  42. data/lib/terraspace/compiler/dsl/syntax/helpers/common.rb +26 -1
  43. data/lib/terraspace/core.rb +11 -2
  44. data/lib/terraspace/dependency/graph.rb +139 -0
  45. data/lib/terraspace/dependency/node.rb +38 -0
  46. data/lib/terraspace/dependency/registry.rb +11 -0
  47. data/lib/terraspace/logger.rb +6 -18
  48. data/lib/terraspace/logger/formatter.rb +13 -0
  49. data/lib/terraspace/mod.rb +7 -1
  50. data/lib/terraspace/seeder/where.rb +6 -2
  51. data/lib/terraspace/shell.rb +79 -0
  52. data/lib/terraspace/terraform/api.rb +7 -45
  53. data/lib/terraspace/terraform/api/base.rb +7 -0
  54. data/lib/terraspace/terraform/api/client.rb +23 -3
  55. data/lib/terraspace/terraform/api/http.rb +14 -34
  56. data/lib/terraspace/terraform/api/http/concern.rb +10 -0
  57. data/lib/terraspace/terraform/api/runs.rb +28 -0
  58. data/lib/terraspace/terraform/api/token.rb +65 -0
  59. data/lib/terraspace/terraform/api/var.rb +20 -6
  60. data/lib/terraspace/terraform/api/vars.rb +2 -1
  61. data/lib/terraspace/terraform/api/workspace.rb +98 -0
  62. data/lib/terraspace/terraform/args/default.rb +48 -21
  63. data/lib/terraspace/terraform/cloud/runs.rb +13 -0
  64. data/lib/terraspace/terraform/cloud/runs/base.rb +33 -0
  65. data/lib/terraspace/terraform/cloud/runs/item_presenter.rb +37 -0
  66. data/lib/terraspace/terraform/cloud/runs/lister.rb +22 -0
  67. data/lib/terraspace/terraform/cloud/runs/pruner.rb +109 -0
  68. data/lib/terraspace/terraform/cloud/sync.rb +41 -0
  69. data/lib/terraspace/terraform/cloud/syncer.rb +52 -0
  70. data/lib/terraspace/terraform/cloud/workspace.rb +10 -21
  71. data/lib/terraspace/terraform/hooks/builder.rb +1 -1
  72. data/lib/terraspace/terraform/remote_state/fetcher.rb +122 -0
  73. data/lib/terraspace/terraform/remote_state/marker/output.rb +39 -0
  74. data/lib/terraspace/terraform/remote_state/marker/pretty_tracer.rb +37 -0
  75. data/lib/terraspace/terraform/remote_state/output_proxy.rb +29 -0
  76. data/lib/terraspace/terraform/runner.rb +24 -14
  77. data/lib/terraspace/util.rb +1 -5
  78. data/lib/terraspace/util/pretty.rb +18 -0
  79. data/lib/terraspace/version.rb +1 -1
  80. data/spec/fixtures/fetcher/c1.json +37 -0
  81. data/spec/fixtures/parser/cache_dirs/all/01-test.auto.tfvars +5 -0
  82. data/spec/fixtures/parser/cache_dirs/depends_on/01-test.auto.tfvars +2 -0
  83. data/spec/fixtures/parser/cache_dirs/output/01-test.auto.tfvars +2 -0
  84. data/spec/fixtures/summary/down.log +12 -0
  85. data/spec/fixtures/summary/output.log +5 -0
  86. data/spec/fixtures/summary/plan/error.log +20 -0
  87. data/spec/fixtures/summary/plan/success.log +17 -0
  88. data/spec/fixtures/summary/show.log +22 -0
  89. data/spec/fixtures/summary/up/error.log +13 -0
  90. data/spec/fixtures/summary/up/success.log +63 -0
  91. data/spec/fixtures/summary/validate/error.log +13 -0
  92. data/spec/fixtures/summary/validate/success.log +5 -0
  93. data/spec/terraspace/all/grapher_spec.rb +38 -0
  94. data/spec/terraspace/all/runner_spec.rb +48 -0
  95. data/spec/terraspace/all/summary_spec.rb +93 -0
  96. data/spec/terraspace/dependency/graph_spec.rb +162 -0
  97. data/spec/terraspace/seeder_spec.rb +0 -1
  98. data/spec/terraspace/terraform/remote_state/fetcher_spec.rb +52 -0
  99. data/terraspace.gemspec +5 -1
  100. metadata +137 -5
  101. data/lib/terraspace/terraform/cloud.rb +0 -25
  102. data/lib/terraspace/util/sh.rb +0 -19
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb1f738f3830420427da3a1ac7ba6b2bcf946da8cce8348d28ba67f7eb692cf3
4
- data.tar.gz: c88c1ce153617f68acc92d89b8fc1d82e193fa2271d701b658737069511a0c2c
3
+ metadata.gz: 471e59d4e8478c78dd4de1190397e89548e68646fa6fcec02748442ecf9b282e
4
+ data.tar.gz: 65250fbef38cef2731688e9c75877747a2a567ecd0d9ba40ce30b2c86c5c73e6
5
5
  SHA512:
6
- metadata.gz: '097c4a70ac24ecfdf0e6a87b844688adf82bc6f41699a903877af054136a00bf9614ace775cb2c431dbbafc92b981218ece247e691b4db079191b080133032eb'
7
- data.tar.gz: fb0e6162ebb0f48f3e46f441163d94cf93e0186a6a32917071f9cbf5b1f27cec679ac582c39f35a9e61000373ba1bbd074797d7fdf941a2887b6d36463dc2e5d
6
+ metadata.gz: 400b78be5d433399119713533a7b56fd7b143b253cae7e96d344273d74784ba9fe877a2a92293f237cec9f0642694256bd04c0b0ad0bef73050d205603884f92
7
+ data.tar.gz: eac5b2042f655964d08454d7b5ea3ba40012145fed1c560bf116757c91857479ef7430cd1b7a2be661e828dbaf12a7cd0323014fb847a13299ef8fdc01cc8125
@@ -3,6 +3,37 @@
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
+ ## [0.3.2]
7
+ * #40 fix backend auto creation
8
+
9
+ ## [0.3.1]
10
+ * #39 fix backend auto creation
11
+
12
+ ## [0.3.0]
13
+ * All commands: Dependency graph calculated and deployed in proper order
14
+ * All commands: `terraspace all up`, `terraspace all build`, `terraspace all down`, etc. `terraspace all -h`
15
+ * Terraspace log: view and tail log files
16
+ * Terraspace logs management commands: `terraspace logs truncate` and `terraspace logs remove`
17
+ * TFC/TFE: Improve support. `config.cloud.vars`, `config.cloud.workspace.attrs`
18
+ * TFC commands: terraspace cloud runs list, terraspace cloud runs prune
19
+ * TFC VCS also sync as part of deploy. Also separate `terraspace cloud sync` command
20
+ * Logger improvements: configurable formatter, log to stderr by default
21
+ * Rename: `cloud.relative_root` to `cloud.working_dir_prefix`
22
+ * Run a plan to capture the diff as part of `-y` option. IE: `terraspace up demo -y`
23
+ * Run plan -destroy as part of down -y
24
+ * Rename update cli to up. Only support the shorthand.
25
+ * Config options: config.all.concurrency, config.all.exit_on_fail, etc
26
+ * TFC: cloud.auto_sync option
27
+ * Improve terraform version check
28
+ * Terraform init: auto mode will retry initializing up to 3 times
29
+ * Terraspace seed: fix instance option
30
+
31
+ ## [0.2.4]
32
+ * fix version check for some versions of terraform
33
+
34
+ ## [0.2.3]
35
+ * #37 config.clean_cache option
36
+
6
37
  ## [0.2.2]
7
38
  * #36 cloud.relative_root setting
8
39
 
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <div align="center">
2
+ <a href="https://terraspace.cloud"><img src="https://img.boltops.com/boltops/logos/terraspace-dark-v2.png" /></a>
3
+ </div>
4
+
1
5
  # Terraspace
2
6
 
3
7
  [![Gem Version](https://badge.fury.io/rb/terraspace.png)](http://badge.fury.io/rb/terraspace)
@@ -8,6 +12,10 @@ The Terraform Framework.
8
12
 
9
13
  Official Docs Site: [terraspace.cloud](https://terraspace.cloud)
10
14
 
15
+ Introduction Video:
16
+
17
+ [![Watch the video](https://img.boltops.com/boltops/tools/terraspace/terraspace-youtube.png)](https://www.youtube.com/watch?v=O87t5q22YNc)
18
+
11
19
  ## Quick Start
12
20
 
13
21
  Here are commands to get started:
@@ -17,8 +25,8 @@ Here are commands to get started:
17
25
  terraspace up demo
18
26
  terraspace down demo
19
27
 
20
- * The `new` command generates a starter project.
21
- * The `up` command creates an s3 bucket.
28
+ * The `new` command generates a starter project.
29
+ * The `up` command creates an s3 bucket.
22
30
  * The `down` command cleans up and deletes the bucket.
23
31
 
24
32
  The default plugin is aws. Other plugins also supported are: google and azurerm.
@@ -28,10 +36,10 @@ The default plugin is aws. Other plugins also supported are: google and azurerm.
28
36
  Create infrastructure:
29
37
 
30
38
  $ terraspace up demo -y
31
- Building .terraspace-cache/dev/stacks/demo
32
- Current directory: .terraspace-cache/dev/stacks/demo
33
- => terraform init -get > /dev/null
34
- Built in .terraspace-cache/dev/stacks/demo
39
+ Building .terraspace-cache/us-west-2/dev/stacks/demo
40
+ Current directory: .terraspace-cache/us-west-2/dev/stacks/demo
41
+ => terraform init -get > /tmp/terraspace/out/terraform-init20200824-21379-bkfvnh.out
42
+ Built in .terraspace-cache/us-west-2/dev/stacks/demo
35
43
  => terraform apply -auto-approve
36
44
  random_pet.bucket: Creating...
37
45
  random_pet.bucket: Creation complete after 0s [id=amusing-mouse]
@@ -48,10 +56,10 @@ Create infrastructure:
48
56
  Destroy infrastructure:
49
57
 
50
58
  $ terraspace down demo -y
51
- Building .terraspace-cache/dev/stacks/demo
52
- Current directory: .terraspace-cache/dev/stacks/demo
53
- => terraform init -get > /dev/null
54
- Built in .terraspace-cache/dev/stacks/demo
59
+ Building .terraspace-cache/us-west-2/dev/stacks/demo
60
+ Current directory: .terraspace-cache/us-west-2/dev/stacks/demo
61
+ => terraform init -get > /tmp/terraspace/out/terraform-init20200824-21379-bkfvnh.out
62
+ Built in .terraspace-cache/us-west-2/dev/stacks/demo
55
63
  => terraform destroy -auto-approve
56
64
  random_pet.bucket: Refreshing state... [id=amusing-mouse]
57
65
  module.bucket.aws_s3_bucket.this: Refreshing state... [id=bucket-amusing-mouse]
@@ -63,13 +71,26 @@ Destroy infrastructure:
63
71
  Destroy complete! Resources: 2 destroyed.
64
72
  $
65
73
 
74
+ ## Deploy Multiple Stacks
75
+
76
+ To deploy all the infrastructure stacks:
77
+
78
+ terraspace all up
79
+
80
+ To choose multiple stacks to deploy
81
+
82
+ terraspace all up instance vpc
83
+
84
+ When you use the all command, the dependency graph is calculated and the stacks are deployed in the right order. To learn more: [Deploy Multiple Stacks](https://terraspace.cloud/docs/dependencies/deploy-all/).
85
+
66
86
  ## Features
67
87
 
68
88
  * [Config Structure](https://terraspace.cloud/docs/config/): A common config structure that gets materializes with the deployed module. Configs can be dynamically controlled to keep your code DRY. You can override the settings if needed, like for using existing backends. See: [Existing Backends](https://terraspace.cloud/docs/state/existing/).
69
89
  * [Generators](https://terraspace.cloud/docs/generators/): Built-in generators to quickly create the starter module. Focus on code instead of boilerplate structure.
70
- * [Tfvars](https://terraspace.cloud/docs/tfvars/): Use the same code with different tfvars to create multiple environments. Terraspace conventionally loads tfvars from the `tfvars` folder.
71
- * [Layering](https://terraspace.cloud/docs/tfvars/layering/): Rich layering support. This allows you to build different environments like dev and prod with the same code.
72
- * [Testing](https://terraspace.cloud/docs/testing/): A testing framework that allows you to create test harnesses, deploy real-resources, and have higher confidence that your code works.
90
+ * [Tfvars](https://terraspace.cloud/docs/tfvars/) & [Layering](https://terraspace.cloud/docs/tfvars/layering/): Use the same code with different tfvars to create multiple environments. Terraspace conventionally loads tfvars from the `tfvars` folder. Rich layering support allows you to build different environments like dev and prod with the same code. Examples are in [Full Layering](https://terraspace.cloud/docs/tfvars/full-layering/).
91
+ * [Deploy Multiple Stacks](https://terraspace.cloud/docs/dependencies/deploy-all/): The ability to deploy multiple stacks with a single command. Terraspace calculates the [dependency graph](https://terraspace.cloud/docs/dependencies/) and deploys stacks in the right order. You can also target specific stacks and deploy [subgraphs](https://terraspace.cloud/docs/dependencies/subgraphs/).
73
92
  * [Configurable CLI](https://terraspace.cloud/docs/cli/): Configurable [CLI Hooks](https://terraspace.cloud/docs/cli/hooks/) and [CLI Args](https://terraspace.cloud/docs/cli/args/) allow you to adjust the underlying terraform command.
93
+ * [Testing](https://terraspace.cloud/docs/testing/): A testing framework that allows you to create test harnesses, deploy real-resources, and have higher confidence that your code works.
94
+ * [Terraform Cloud and Terraform Enterprise Support](https://terraspace.cloud/docs/cloud/): TFC and TFE are both supported. Terraspace adds additional conveniences to make working with Terraform Cloud Workspaces easier.
74
95
 
75
96
  For more info: [terraspace.cloud](https://terraspace.cloud)
@@ -1,5 +1,5 @@
1
1
  #!/bin/bash -ex
2
- echo "Running: terraspace build placeholder"
2
+ echo "Running: terraspace build"
3
3
  <%= terraspace_build_commands %>
4
4
  git add .terraspace-cache
5
5
  git commit -m 'update .terraspace-cache' || true
@@ -23,6 +23,7 @@ terraform.tfvars
23
23
  /Gemfile.lock
24
24
  /InstalledFiles
25
25
  /lib/bundler/man
26
+ /log
26
27
  /output
27
28
  /pkg
28
29
  /rdoc
@@ -0,0 +1,17 @@
1
+ # Terraspace Project
2
+
3
+ This is a Terraspace project. It contains code to provision Cloud infrastructure built with [Terraform](https://www.terraform.io/) and the [Terraspace Framework](https://terraspace.cloud/).
4
+
5
+ ## Deploy
6
+
7
+ To deploy all the infrastructure stacks:
8
+
9
+ terraspace all up
10
+
11
+ To deploy individual stacks:
12
+
13
+ terraspace up demo # where demo is app/stacks/demo
14
+
15
+ ## Terrafile
16
+
17
+ To add more modules for use, you can add them to the [Terrafile](https://terraspace.cloud/docs/terrafile/).
@@ -1,16 +1,19 @@
1
1
  $stdout.sync = true unless ENV["TS_STDOUT_SYNC"] == "0"
2
2
 
3
3
  $:.unshift(File.expand_path("../", __FILE__))
4
+ require "active_support/concern"
4
5
  require "active_support/core_ext/class"
5
6
  require "active_support/core_ext/hash"
6
7
  require "active_support/core_ext/string"
7
8
  require "active_support/ordered_options"
9
+ require "deep_merge/rails_compat"
8
10
  require "dsl_evaluator"
9
11
  require "fileutils"
10
12
  require "json"
11
13
  require "memoist"
12
14
  require "rainbow/ext/string"
13
15
  require "render_me_pretty"
16
+ require "set"
14
17
  require "terraspace/ext"
15
18
  require "terraspace/version"
16
19
 
@@ -22,6 +25,7 @@ DslEvaluator.backtrace_reject = "lib/terraspace"
22
25
  module Terraspace
23
26
  extend Core # for Terraspace.root
24
27
  class Error < StandardError; end
28
+ class InitRequiredError < Error; end
25
29
  end
26
30
 
27
31
  Terraspace::Booter.boot
@@ -0,0 +1,8 @@
1
+ module Terraspace::All
2
+ class Base
3
+ def initialize(options={})
4
+ @options = options
5
+ Terraspace.check_project! unless ENV['TS_TEST']
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,129 @@
1
+ require "graph"
2
+ require "tty-tree"
3
+
4
+ module Terraspace::All
5
+ class Grapher < Base
6
+ include Terraspace::Util::Logging
7
+
8
+ def run
9
+ check_graphviz!
10
+ 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
14
+ if @options[:format] == "text"
15
+ text(graph.top_nodes)
16
+ else
17
+ draw(graph.nodes)
18
+ end
19
+ end
20
+
21
+ def text(nodes)
22
+ Rainbow.enabled = false unless @options[:full]
23
+ data = build_tree_data(nodes)
24
+ Rainbow.enabled = true unless @options[:full]
25
+ tree = TTY::Tree.new(data)
26
+ logger.info tree.render
27
+ end
28
+
29
+ def build_tree_data(nodes)
30
+ if nodes.size == 1
31
+ tree_data(nodes.first)
32
+ else
33
+ root = Terraspace::Dependency::Node.new('.')
34
+ nodes.each { |node| node.parent!(root) }
35
+ tree_data(root)
36
+ end
37
+ end
38
+
39
+ def tree_data(parent, data={})
40
+ parent_name = text_name(parent)
41
+ data[parent_name] ||= []
42
+ parent.children.each do |child|
43
+ child_name = text_name(child)
44
+ if child.children.empty? # leaf node
45
+ data[parent_name] << child_name
46
+ else
47
+ next_data = { child_name => [] }
48
+ data[parent_name] << tree_data(child, next_data)
49
+ end
50
+ end
51
+ data
52
+ end
53
+
54
+ def text_name(node)
55
+ node.highlighted? ? node.name.bright : node.name
56
+ end
57
+
58
+ def draw(nodes)
59
+ path, filename = nil, filename() # outside block to capture value
60
+ digraph do
61
+ node_attribs << color('"#b6d7a8"') << filled << fontcolor("white")
62
+ edge_attribs << color('"#999999"') << filled
63
+ nodes.each do |parent|
64
+ if parent.highlighted?
65
+ node(parent.name)
66
+ else
67
+ node(parent.name).attributes << color('"#A4C2F4"')
68
+ end
69
+ parent.children.each do |child|
70
+ edge(parent.name, child.name)
71
+ end
72
+ end
73
+ FileUtils.mkdir_p(File.dirname(filename))
74
+ save(filename, "png")
75
+ path = "#{filename}.png"
76
+ end
77
+
78
+ logger.info "Graph saved to #{Terraspace::Util.pretty_path(path)}"
79
+ open(path)
80
+ end
81
+
82
+ private
83
+ def draw_full_graph
84
+ if @options[:format] == "text"
85
+ @options[:full].nil? ? false : @options[:full]
86
+ else
87
+ @options[:full].nil? ? true : @options[:full]
88
+ end
89
+ end
90
+
91
+
92
+ def filename
93
+ name = "#{Terraspace.cache_root}/graph/dependencies" # dont include extension
94
+ unless ENV['TS_GRAPH_TS'] == '0'
95
+ @@timestamp ||= Time.now.utc.strftime("%Y%m%d%H%M%S")
96
+ name += "-#{@@timestamp}"
97
+ end
98
+ name
99
+ end
100
+
101
+ def open(path)
102
+ command = command("c9") || command("open")
103
+ system("#{command} #{path}") if command
104
+ end
105
+
106
+ def command(name)
107
+ name if system("type #{name} > /dev/null 2>&1") # c9 = cloud9, open = macosx
108
+ end
109
+
110
+ # Check if Graphiz is installed and prints a user friendly message if it is not installed.
111
+ def check_graphviz!
112
+ installed = system("type dot > /dev/null 2>&1") # dot is a command that is part of the graphviz package
113
+ return if installed
114
+ logger.error "ERROR: It appears that the Graphviz is not installed. Please install it to use the graph command.".color(:red)
115
+ if RUBY_PLATFORM =~ /darwin/
116
+ logger.error "You can install Graphviz with homebrew:"
117
+ logger.error " brew install graphviz"
118
+ end
119
+ logger.info <<~EOL
120
+ Also consider:
121
+
122
+ terraspace all graph --format text
123
+
124
+ Which will print out the graph in text form.
125
+ EOL
126
+ exit 1
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,43 @@
1
+ module Terraspace::All
2
+ class Preview
3
+ extend Memoist
4
+ include Terraspace::Util::Logging
5
+ include Terraspace::Compiler::DirsConcern
6
+
7
+ def initialize(command, batches, options={})
8
+ @command, @batches, @options = command, batches, options
9
+ end
10
+
11
+ def show
12
+ logger.info header
13
+ logger.info preview
14
+ end
15
+
16
+ def header
17
+ %w[up down].include?(@command) && !@options[:yes] ? "Will run:" : "Running:"
18
+ end
19
+
20
+ def preview
21
+ i = 0
22
+ @batches.map do |batch|
23
+ i += 1
24
+ batch.map do |stack|
25
+ command = " terraspace #{@command}"
26
+ ljust = command.size + max_name_size + 1
27
+ command = "#{command} #{stack.name}"
28
+ command.ljust(ljust, ' ') + " # batch #{i}"
29
+ end
30
+ end.join("\n")
31
+ end
32
+
33
+ def max_name_size
34
+ @batches.inject(0) do |max,batch|
35
+ batch.each do |node|
36
+ max = node.name.size if node.name.size > max
37
+ end
38
+ max
39
+ end
40
+ end
41
+ memoize :max_name_size
42
+ end
43
+ end
@@ -0,0 +1,169 @@
1
+ module Terraspace::All
2
+ class Runner < Base
3
+ include Terraspace::Util
4
+
5
+ def initialize(command, options={})
6
+ @command, @options = command, options
7
+ super(options)
8
+ end
9
+
10
+ def run
11
+ time_took do
12
+ @batches = build_batches
13
+ preview
14
+ are_you_sure?
15
+ deploy_batches
16
+ end
17
+ end
18
+
19
+ def preview
20
+ Preview.new(@command, @batches, @options).show
21
+ end
22
+
23
+ def build_batches
24
+ @batches = run_builder(quiet: false)
25
+ @batches.reverse! if @command == "down"
26
+ @batches
27
+ end
28
+
29
+ def deploy_batches
30
+ truncate_logs if ENV['TS_TRUNCATE_LOGS']
31
+ @batches.each_with_index do |batch,i|
32
+ logger.info "Batch Run #{i+1}:"
33
+ run_builder unless i == 0 # already handled by build_batches the first time
34
+ deploy_batch(batch)
35
+ end
36
+ end
37
+
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
+ def deploy_batch(batch)
45
+ @pids = {} # stores child processes pids. map of pid to mod_name, reset this list on each batch run
46
+ concurrency = Terraspace.config.all.concurrency
47
+ batch.sort_by(&:name).each_slice(concurrency) do |slice|
48
+ slice.each do |node|
49
+ pid = fork do
50
+ run_terraspace(node.name)
51
+ end
52
+ @pids[pid] = node.name # store mod_name mapping
53
+ end
54
+ end
55
+ wait_for_child_proccess
56
+ summarize # also reports lower-level error info
57
+ report_errors # reports finall errors and possibly exit
58
+ end
59
+
60
+ def wait_for_child_proccess
61
+ @errors = [] # stores child processes pids that errored
62
+ @pids.each do |pid, _|
63
+ pid, status = Process.wait2(pid)
64
+ success = status.exitstatus == 0
65
+ @errors << pid unless success
66
+ end
67
+ end
68
+
69
+ def report_errors
70
+ @errors.each do |pid|
71
+ mod_name = @pids[pid]
72
+ terraspace_command = terraspace_command(mod_name)
73
+ logger.error "Error running: #{terraspace_command}. Check logs and fix the error.".color(:red)
74
+ end
75
+ unless @errors.empty?
76
+ exit 2 if exit_on_fail?
77
+ end
78
+ end
79
+
80
+ # Precendence:
81
+ # 1. env var
82
+ # 2. cli
83
+ # 3. config/app.rb setting
84
+ def exit_on_fail?
85
+ return false if ENV['TS_EXIT_ON_FAIL'] == '0'
86
+ if @options[:exit_on_fail].nil?
87
+ Terraspace.config.all.exit_on_fail[@command]
88
+ else
89
+ @options[:exit_on_fail]
90
+ end
91
+ end
92
+
93
+ def summarize
94
+ @pids.each do |_, mod_name|
95
+ data = {
96
+ command: @command,
97
+ log_path: log_path(mod_name),
98
+ terraspace_command: terraspace_command(mod_name),
99
+ }
100
+ Summary.new(data).run
101
+ end
102
+ end
103
+
104
+ def run_terraspace(mod_name)
105
+ set_log_path!(mod_name)
106
+ name = command_map(@command)
107
+ o = @options.merge(mod: mod_name, yes: true, build: false, input: false)
108
+ case @command
109
+ when "up"
110
+ Terraspace::CLI::Up.new(o).run
111
+ when "down"
112
+ Terraspace::CLI::Down.new(o).run
113
+ else
114
+ Terraspace::CLI::Commander.new(name, o).run
115
+ end
116
+ end
117
+
118
+ def set_log_path!(mod_name)
119
+ command = terraspace_command(mod_name)
120
+ path = log_path(mod_name)
121
+ pretty_path = Terraspace::Util.pretty_path(path)
122
+ logger.info "Running: #{command.bright} Logs: #{pretty_path}"
123
+
124
+ FileUtils.mkdir_p(File.dirname(path))
125
+ logger = Terraspace::Logger.new(path)
126
+ logger.level = Terraspace.config.logger.level # match the level that user configured
127
+ logger.formatter = Terraspace.config.logger.formatter # match the level that user configured
128
+ logger.progname = command
129
+ Terraspace.logger = logger
130
+ end
131
+
132
+ def log_path(mod_name)
133
+ "#{Terraspace.config.log.root}/#{@command}/#{mod_name}.log"
134
+ end
135
+
136
+ def truncate_logs
137
+ logs = Terraspace::CLI::Log::Tasks.new(mute: true)
138
+ logs.truncate
139
+ end
140
+
141
+ def terraspace_command(name)
142
+ "terraspace #{@command} #{name}"
143
+ end
144
+
145
+ def command_map(name)
146
+ map = {
147
+ up: "apply",
148
+ down: "destroy",
149
+ }.stringify_keys
150
+ map[name] || name
151
+ end
152
+
153
+ def are_you_sure?
154
+ return true unless sure_command?
155
+ sure? # from Util
156
+ end
157
+
158
+ def sure_command?
159
+ %w[up down].include?(@command)
160
+ end
161
+
162
+ def time_took
163
+ t1 = Time.now
164
+ yield
165
+ t2 = Time.now
166
+ logger.info "Time took: #{pretty_time(t2-t1)}"
167
+ end
168
+ end
169
+ end