terraspace 0.3.4 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/README.md +56 -29
  4. data/lib/templates/base/project/README.md +1 -1
  5. data/lib/terraspace/all/preview.rb +1 -1
  6. data/lib/terraspace/all/runner.rb +1 -0
  7. data/lib/terraspace/all/summary.rb +8 -1
  8. data/lib/terraspace/app.rb +9 -5
  9. data/lib/terraspace/builder.rb +10 -6
  10. data/lib/terraspace/cli.rb +11 -16
  11. data/lib/terraspace/cli/all.rb +6 -0
  12. data/lib/terraspace/cli/bundle.rb +2 -1
  13. data/lib/terraspace/cli/clean.rb +18 -6
  14. data/lib/terraspace/cli/clean/all.rb +18 -0
  15. data/lib/terraspace/cli/clean/base.rb +15 -0
  16. data/lib/terraspace/cli/clean/cache.rb +25 -0
  17. data/lib/terraspace/cli/{logs/tasks.rb → clean/logs.rb} +16 -5
  18. data/lib/terraspace/cli/help/all/init.md +33 -0
  19. data/lib/terraspace/cli/help/clean/all.md +10 -0
  20. data/lib/terraspace/cli/help/clean/cache.md +12 -0
  21. data/lib/terraspace/cli/help/clean/logs.md +17 -0
  22. data/lib/terraspace/cli/help/logs.md +48 -0
  23. data/lib/terraspace/cli/init.rb +3 -7
  24. data/lib/terraspace/cli/list.rb +2 -1
  25. data/lib/terraspace/cli/logs.rb +106 -9
  26. data/lib/terraspace/cli/{log → logs}/concern.rb +2 -1
  27. data/lib/terraspace/cli/new/helper.rb +9 -2
  28. data/lib/terraspace/compiler/dependencies/helpers.rb +34 -0
  29. data/lib/terraspace/compiler/dsl/syntax/helpers/common.rb +0 -26
  30. data/lib/terraspace/compiler/dsl/syntax/tfvar.rb +1 -0
  31. data/lib/terraspace/compiler/erb/context.rb +1 -1
  32. data/lib/terraspace/compiler/erb/helpers.rb +6 -0
  33. data/lib/terraspace/dependency/helper/base.rb +7 -0
  34. data/lib/terraspace/dependency/helper/depends_on.rb +12 -0
  35. data/lib/terraspace/dependency/helper/output.rb +11 -0
  36. data/lib/terraspace/hooks/builder.rb +52 -0
  37. data/lib/terraspace/hooks/concern.rb +9 -0
  38. data/lib/terraspace/{terraform/hooks → hooks}/dsl.rb +3 -2
  39. data/lib/terraspace/hooks/runner.rb +23 -0
  40. data/lib/terraspace/mod.rb +11 -2
  41. data/lib/terraspace/plugin/summary/interface.rb +3 -1
  42. data/lib/terraspace/shell.rb +15 -10
  43. data/lib/terraspace/terraform/args/custom.rb +1 -1
  44. data/lib/terraspace/terraform/args/default.rb +9 -19
  45. data/lib/terraspace/terraform/remote_state/fetcher.rb +13 -4
  46. data/lib/terraspace/terraform/remote_state/marker/output.rb +3 -1
  47. data/lib/terraspace/terraform/remote_state/output_proxy.rb +18 -14
  48. data/lib/terraspace/terraform/remote_state/unresolved.rb +40 -0
  49. data/lib/terraspace/terraform/runner.rb +3 -8
  50. data/lib/terraspace/version.rb +1 -1
  51. data/spec/fixtures/dependencies/app/stacks/a1/tfvars/dev.tfvars +1 -0
  52. data/spec/fixtures/fetcher/c1.json +4 -0
  53. data/spec/terraspace/compiler/erb/render_spec.rb +15 -0
  54. data/spec/terraspace/dependency/helper/depends_on_spec.rb +27 -0
  55. data/spec/terraspace/dependency/helper/output_spec.rb +29 -0
  56. data/spec/terraspace/{terraform/hooks → hooks}/builder_spec.rb +4 -5
  57. data/spec/terraspace/terraform/remote_state/fetcher_spec.rb +108 -27
  58. data/spec/terraspace/terraform/remote_state/marker/output_spec.rb +36 -0
  59. data/spec/terraspace/terraform/remote_state/output_proxy_spec.rb +69 -0
  60. data/terraspace.gemspec +1 -1
  61. metadata +38 -13
  62. data/lib/terraspace/cli/help/clean.md +0 -5
  63. data/lib/terraspace/cli/help/log.md +0 -48
  64. data/lib/terraspace/cli/log.rb +0 -112
  65. data/lib/terraspace/terraform/hooks/builder.rb +0 -40
@@ -0,0 +1,25 @@
1
+ class Terraspace::CLI::Clean
2
+ class Cache < Base
3
+ def run
4
+ Terraspace.check_project!
5
+ paths = [Terraspace.cache_root, Terraspace.tmp_root]
6
+ are_you_sure?(paths)
7
+ paths.each do |path|
8
+ FileUtils.rm_rf(path)
9
+ puts "Removed #{pretty(path)}"
10
+ end
11
+ end
12
+
13
+ def are_you_sure?(paths)
14
+ pretty_paths = paths.map { |p| " #{pretty(p)}" }.join("\n")
15
+ message = <<~EOL.chomp
16
+ Will remove these folders and all their files:
17
+
18
+ #{pretty_paths}
19
+
20
+ Are you sure?
21
+ EOL
22
+ sure?(message) # from Util::Sure
23
+ end
24
+ end
25
+ end
@@ -1,11 +1,13 @@
1
- class Terraspace::CLI::Logs
2
- class Tasks
3
- def initialize(options={})
4
- @options = options
1
+ class Terraspace::CLI::Clean
2
+ class Logs < Base
3
+ def run
4
+ action = @options[:truncate] ? "truncate" : "remove"
5
+ are_you_sure?(action)
6
+ @options[:truncate] ? truncate : remove
7
+ logger.info "Logs #{action}d" # IE: Logs truncated or Logs removed
5
8
  end
6
9
 
7
10
  def truncate
8
- puts "Truncating log files in #{pretty_log_root}/" unless @options[:mute]
9
11
  log_files.each do |path|
10
12
  File.open(path, "w").close # truncates files
11
13
  end
@@ -28,5 +30,14 @@ class Terraspace::CLI::Logs
28
30
  def log_root
29
31
  Terraspace.config.log.root
30
32
  end
33
+
34
+ def are_you_sure?(action)
35
+ message = <<~EOL.chomp
36
+ Will #{action} all the log files in #{pretty_log_root}/ folder
37
+ Are you sure?
38
+ EOL
39
+ sure?(message) # from Util::Sure
40
+ end
31
41
  end
32
42
  end
43
+
@@ -0,0 +1,33 @@
1
+ ## Example
2
+
3
+ $ terraspace all init
4
+ Building one stack to build all stacks
5
+ Building .terraspace-cache/us-west-2/dev/stacks/c1
6
+ Downloading tfstate files for dependencies defined in tfvars...
7
+ Built in .terraspace-cache/us-west-2/dev/stacks/c1
8
+ Running:
9
+ terraspace init c1 # batch 1
10
+ terraspace init b1 # batch 2
11
+ terraspace init b2 # batch 2
12
+ terraspace init a1 # batch 3
13
+ Batch Run 1:
14
+ Running: terraspace init c1 Logs: log/init/c1.log
15
+ terraspace init c1: Terraform has been successfully initialized!
16
+ Batch Run 2:
17
+ Running: terraspace init b1 Logs: log/init/b1.log
18
+ Running: terraspace init b2 Logs: log/init/b2.log
19
+ terraspace init b1: Terraform has been successfully initialized!
20
+ terraspace init b2: Terraform has been successfully initialized!
21
+ Batch Run 3:
22
+ Running: terraspace init a1 Logs: log/init/a1.log
23
+ terraspace init a1: Terraform has been successfully initialized!
24
+ Time took: 6s
25
+ $
26
+
27
+ If Terraform is having trouble initializing, clearing the cache may help:
28
+
29
+ $ terraspace clean cache -y
30
+ Removed .terraspace-cache
31
+ Removed /tmp/terraspace
32
+
33
+ Also consider disabling the [terraform.plugin_cache.enabled](https://terraspace.cloud/docs/config/reference/).
@@ -0,0 +1,10 @@
1
+ ## Examples
2
+
3
+ $ terraspace clean all
4
+ Will remove Terraspace cache and logs.
5
+ Are you sure? (y/N) y
6
+ Removed .terraspace-cache
7
+ Removed /tmp/terraspace
8
+ Removing all files in log/
9
+ Logs removed
10
+ $
@@ -0,0 +1,12 @@
1
+ ## Example
2
+
3
+ $ terraspace clean cache
4
+ Will remove these folders and all their files:
5
+
6
+ .terraspace-cache
7
+ /tmp/terraspace
8
+
9
+ Are you sure? (y/N)
10
+ Removed .terraspace-cache
11
+ Removed /tmp/terraspace
12
+ $
@@ -0,0 +1,17 @@
1
+ ## Examples
2
+
3
+ Remove logs completely:
4
+
5
+ $ terraspace clean logs
6
+ Will remove all the log files in log/ folder
7
+ Are you sure? (y/N) y
8
+ Removing all files in log/
9
+ Logs removed
10
+
11
+ Truncate logs. IE: Keeps the files but removes contents and zero bytes the files.
12
+
13
+ $ terraspace clean logs --truncate
14
+ Will truncate all the log files in log/ folder
15
+ Are you sure? (y/N) y
16
+ Logs truncated
17
+ $
@@ -0,0 +1,48 @@
1
+ The log commands will filter out the logs for the last ran terraspace command. It does this by filtering for the last found PID in the log files.
2
+
3
+ ## Quick Start
4
+
5
+ Follow all the logs as you're running `terraspace all up`:
6
+
7
+ terraspace logs -f
8
+
9
+ Note, Terraspace automatically checks every second for new logs and adds them to be followed.
10
+
11
+ ## View Logs
12
+
13
+ View last 10 lines of each log file.
14
+
15
+ terraspace logs up network # view up log on a specific stack
16
+ terraspace logs up # view all up logs
17
+ terraspace logs down # view all down logs
18
+ terraspace logs # view all logs: up, down, etc
19
+
20
+ By default, the logs command shows the last 10 lines for each log file. You can use the `-n` option to adjust this.
21
+
22
+ terraspace logs -n 2 # view last 2 lines of all logs: up, down, etc
23
+
24
+ To show all logs, use the `-a` option.
25
+
26
+ terraspace logs up -a
27
+
28
+ Note, if both an action and stack is specified, then it defaults to showing all logs. If you want not to show all logs, use `--no-all`.
29
+
30
+ ## Tail Logs
31
+
32
+ To tail logs, use the `-f` option.
33
+
34
+ terraspace logs up network -f # view up log on a specific stack
35
+ terraspace logs up -f # view all up logs
36
+ terraspace logs down -f # view all down logs
37
+ terraspace logs -f # view all logs: up, down, etc
38
+
39
+ ## Timestamps
40
+
41
+ The timestamps are shown by default when you are looking for multiple files. When you specify both the action and stack for a single log file, the timestamps are not shown.
42
+
43
+ terraspace logs up # timestamps will be shown in this case
44
+ terraspace logs up network # timestamps not be shown in this case
45
+
46
+ To show timestamps:
47
+
48
+ terraspace logs up network --timestamps
@@ -67,7 +67,7 @@ class Terraspace::CLI
67
67
  mode = ENV['TS_INIT_MODE'] || Terraspace.config.init.mode
68
68
  case mode.to_sym
69
69
  when :auto
70
- !already_initialized?
70
+ !already_init?
71
71
  when :always
72
72
  true
73
73
  when :never
@@ -80,16 +80,12 @@ class Terraspace::CLI
80
80
  # Traverse symlink dirs also: linux_amd64 is a symlink
81
81
  # plugins/registry.terraform.io/hashicorp/google/3.39.0/linux_amd64/terraform-provider-google_v3.39.0_x5
82
82
  #
83
- # Check modules/modules.json also because during the tfvars dependency pass main.tf modules are not built.
84
- # So init happens again during the second pass.
85
- #
86
- def already_initialized?
83
+ def already_init?
87
84
  terraform = "#{@mod.cache_dir}/.terraform"
88
85
  provider = Dir.glob("#{terraform}/**{,/*/**}/*").find do |path|
89
86
  path.include?("terraform-provider-")
90
87
  end
91
- modules = File.exist?("#{terraform}/modules/modules.json")
92
- !!(provider && modules)
88
+ !!provider
93
89
  end
94
90
  end
95
91
  end
@@ -6,7 +6,8 @@ class Terraspace::CLI
6
6
  end
7
7
 
8
8
  def run
9
- Dir.glob("{app,vendor}/{modules,stacks}/*").sort.each do |path|
9
+ dirs = Dir.glob("{app,vendor}/{modules,stacks}/*").select { |p| File.directory?(p) }
10
+ dirs.sort.each do |path|
10
11
  if @type_dir
11
12
  puts path if path.include?("/#{@type_dir}/")
12
13
  else
@@ -1,15 +1,112 @@
1
+ require "eventmachine"
2
+ require "eventmachine-tail"
3
+
1
4
  class Terraspace::CLI
2
- class Logs < Terraspace::Command
3
- desc "truncate", "Truncates logs. IE: Keeps the files but removes contents and zero bytes the files."
4
- long_desc Help.text("logs/truncate")
5
- def truncate
6
- Tasks.new(options).truncate
5
+ class Logs < Base
6
+ include Concern
7
+
8
+ def initialize(options={})
9
+ super
10
+ @action, @stack = options[:action], options[:stack]
11
+ @action ||= '**'
12
+ @stack ||= '*'
13
+ end
14
+
15
+ def run
16
+ check_logs!
17
+ if @options[:follow]
18
+ follow_logs
19
+ else
20
+ all_log_paths.each { |path| show_log(path) }
21
+ end
22
+ end
23
+
24
+ def follow_logs
25
+ glob_path = "#{Terraspace.log_root}/#{@action}/#{@stack}.log"
26
+ Dir.glob(glob_path).each do |path|
27
+ puts "Following #{pretty(path)}".color(:purple)
28
+ end
29
+ EventMachine.run do
30
+ interval = Integer(ENV['TS_LOG_GLOB_INTERNAL'] || 1)
31
+ EventMachine::FileGlobWatchTail.new(glob_path, nil, interval) do |filetail, line|
32
+ puts line # always show timestamp in follow mode
33
+ end
34
+ end
35
+ end
36
+
37
+ def show_log(path)
38
+ report_log(path)
39
+ lines = readlines(path)
40
+ lines = apply_limit(lines)
41
+ lines.each do |line|
42
+ puts format(line)
43
+ end
44
+ end
45
+
46
+ def report_log(path)
47
+ pretty_path = pretty(path)
48
+ if File.exist?(path)
49
+ puts "Showing: #{pretty_path}".color(:purple)
50
+ end
51
+ end
52
+
53
+ def format(line)
54
+ if timestamps
55
+ line
56
+ else
57
+ line.sub(/.*\]: /,'')
58
+ end
59
+ end
60
+
61
+ def all_log_paths
62
+ Dir.glob("#{Terraspace.log_root}/#{@action}/#{@stack}.log")
7
63
  end
8
64
 
9
- desc "remove", "Removes logs"
10
- long_desc Help.text("logs/remove")
11
- def remove
12
- Tasks.new(options).remove
65
+ def check_logs!
66
+ return unless all_log_paths.empty?
67
+ puts "WARN: No logs found".color(:yellow)
68
+ end
69
+
70
+ # Only need to check if both action and stack are provided. Otherwise the Dir.globs are used to discover the files
71
+ def check_log!
72
+ return unless single_log?
73
+ path = "#{Terraspace.log_root}/#{@action}/#{@stack}.log"
74
+ return if File.exist?(path)
75
+ puts "ERROR: Log file was not found: #{pretty(path)}".color(:red)
76
+ exit 1
77
+ end
78
+
79
+ def single_log?
80
+ @action != '**' && @stack != '*'
81
+ end
82
+
83
+ def apply_limit(lines)
84
+ return lines if all
85
+ left = limit * -1
86
+ lines[left..-1] || []
87
+ end
88
+
89
+ def all
90
+ if single_log?
91
+ @options[:all].nil? ? true : @options[:all]
92
+ else # multiple
93
+ @options[:all].nil? ? false : @options[:all]
94
+ end
95
+ end
96
+
97
+ def limit
98
+ @options[:limit].nil? ? 10 : @options[:limit]
99
+ end
100
+
101
+ def timestamps
102
+ if single_log?
103
+ @options[:timestamps].nil? ? false : @options[:timestamps]
104
+ else
105
+ @options[:timestamps].nil? ? true : @options[:timestamps]
106
+ end
107
+ end
108
+ def pretty(path)
109
+ Terraspace::Util.pretty_path(path)
13
110
  end
14
111
  end
15
112
  end
@@ -1,4 +1,4 @@
1
- class Terraspace::CLI::Log
1
+ class Terraspace::CLI::Logs
2
2
  module Concern
3
3
  # Filters for lines that belong to the last ran process pid
4
4
  def readlines(path)
@@ -17,6 +17,7 @@ class Terraspace::CLI::Log
17
17
 
18
18
  # [2020-09-06T21:58:25 #11313 terraspace up b1]:
19
19
  def pid(line)
20
+ return @options[:pid] if @options && @options[:pid] # Terraspace::All::Summary: doesnt have @options set
20
21
  md = line.match(/:\d{2} #(\d+) /)
21
22
  md[1] if md
22
23
  end
@@ -6,10 +6,17 @@ class Terraspace::CLI::New
6
6
  def build_gemfile(*list)
7
7
  lines = []
8
8
  list.each do |name|
9
- line = %Q|gem "#{name}"|
10
- lines << line
9
+ lines << gem_line(name)
11
10
  end
12
11
  lines.join("\n")
13
12
  end
13
+
14
+ def gem_line(name)
15
+ if name == "terraspace"
16
+ %Q|gem "#{name}", '~> #{Terraspace::VERSION}'|
17
+ else
18
+ %Q|gem "#{name}"|
19
+ end
20
+ end
14
21
  end
15
22
  end
@@ -0,0 +1,34 @@
1
+ module Terraspace::Compiler::Dependencies
2
+ # This is a separate module specifically because the DSL also has an output method.
3
+ # The module allows us to include dependency related methods only within tfvars context for the DSL.
4
+ #
5
+ # 1. Only include this module to DSL tfvars context.
6
+ # So the output method works in tfvars .rb files works.
7
+ # At the same time, the DSL usage of output also works for normal main.tf files.
8
+ # Passing specs prove this.
9
+ # 2. For ERB, there's currently only one ERB context. So this module is included in all contexts.
10
+ # The builder only processes dependencies from tfvars, so these helpers are only respected there.
11
+ #
12
+ # Where the module is included in the code:
13
+ #
14
+ # 1. lib/terraspace/compiler/dsl/syntax/tfvar.rb
15
+ # 2. lib/terraspace/compiler/erb/helpers.rb
16
+ #
17
+ module Helpers
18
+ def output(identifier, options={})
19
+ Terraspace::Dependency::Helper::Output.new(@mod, identifier, options).result
20
+ end
21
+ alias_method :terraform_output, :output # backwards compatibility
22
+
23
+ def depends_on(*child_names, **options)
24
+ child_names.flatten!
25
+ child_names.map do |child_name|
26
+ each_depends_on(child_name, options)
27
+ end.join("\n")
28
+ end
29
+
30
+ def each_depends_on(child_name, options={})
31
+ Terraspace::Dependency::Helper::DependsOn.new(@mod, child_name, options).result
32
+ end
33
+ end
34
+ end
@@ -1,8 +1,6 @@
1
1
  module Terraspace::Compiler::Dsl::Syntax::Helpers
2
2
  module Common
3
3
  extend Memoist
4
- Fetcher = Terraspace::Terraform::RemoteState::Fetcher
5
- Marker = Terraspace::Terraform::RemoteState::Marker
6
4
  Meta = Terraspace::Compiler::Dsl::Meta
7
5
 
8
6
  def var
@@ -25,29 +23,5 @@ module Terraspace::Compiler::Dsl::Syntax::Helpers
25
23
  command = ["terraspace"] + args
26
24
  command.join(separator)
27
25
  end
28
-
29
- def terraform_output(identifier, options={})
30
- if @mod.resolved # dependencies have been resolved
31
- Fetcher.new(@mod, identifier, options).output
32
- else
33
- Marker::Output.new(@mod, identifier, options).build
34
- end
35
- end
36
-
37
- def depends_on(*child_names, **options)
38
- child_names.flatten!
39
- child_names.map do |child_name|
40
- each_depends_on(child_name, options)
41
- end.join("\n")
42
- end
43
-
44
- def each_depends_on(child_name, options={})
45
- if @mod.resolved # dependencies have been resolved
46
- # Note: A generated line is not really needed. Dependencies are stored in memory. Added to assist users with debugging
47
- "# #{@mod.name} depends on #{child_name}"
48
- else
49
- Marker::Output.new(@mod, child_name, options).build
50
- end
51
- end
52
26
  end
53
27
  end