terraspace 0.6.4 → 0.6.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56392f488a05d42760586d379da36022589979733b654ea3f5f3d5fcb879de26
4
- data.tar.gz: 690b3d0fa3e192aee02fd34ae187ab4628caccd335a1733ce10040ff2ef55b32
3
+ metadata.gz: 0d88e968da03e187d230fad5e1785995ffa95415146a9a2b7cfe500c0e54473a
4
+ data.tar.gz: 49535538ad977e0d7b36620037a70478e6bfdf9c9bc607feff4576724fc351fd
5
5
  SHA512:
6
- metadata.gz: 4dcc7b6c1d1f2d21be6115d02cc34bbeb9f773e6ece16e8549ac3a8e6a4167fb6392a2917399399a0ba0ddde4ff880c9dff8de2ee5261def5250fc2525527ce1
7
- data.tar.gz: 476d2c10897c2915a3cd601b556e783a639cb29a97c11505b5e34f32d8079c311e5ad8f6efeeee6cf9a143a2f63b9b46b76a9b6741d78802fd53baf8de319b29
6
+ metadata.gz: 3d0a9476e10f46a504c555212aa538968045d27845eabcec4c43306a008422dce9b19c28b4d0ce16defce89bc78674c01098c08291af6e321f85f2385e6390f0
7
+ data.tar.gz: 27563820dafa65f85df07ce9e28430a75b7ffb6330b2440444b6a88803efa3f851d9524ae5c0417039a3d698a62896b2071fb84b8de30e93a42c03ad64941dbd
data/CHANGELOG.md CHANGED
@@ -3,6 +3,23 @@
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.6.9] - 2021-05-07
7
+ - [#112](https://github.com/boltops-tools/terraspace/pull/112) fix smart auto retry
8
+
9
+ ## [0.6.8] - 2021-05-07
10
+ - [#110](https://github.com/boltops-tools/terraspace/pull/110) fix popen deadlock with large amounts of output [#97](https://github.com/boltops-tools/terraspace/pull/97) Terraspace hangs when TF_LOG=TRACE environment variable exists #97
11
+
12
+ ## [0.6.7] - 2021-05-05
13
+ - [#108](https://github.com/boltops-tools/terraspace/pull/108) provide runner context to terraspace hook
14
+
15
+ ## [0.6.6] - 2021-04-15
16
+ - [#101](https://github.com/boltops-tools/terraspace/pull/101) terraspace force-unlock command
17
+ - [#102](https://github.com/boltops-tools/terraspace/pull/102) fix terraspace all summarized logging
18
+ - [#103](https://github.com/boltops-tools/terraspace/pull/103) config.build.pass_files with default files use pass strategy
19
+
20
+ ## [0.6.5] - 2021-03-24
21
+ - [#96](https://github.com/boltops-tools/terraspace/pull/96) terraspace fmt: ability to specific module or stack
22
+
6
23
  ## [0.6.4] - 2021-03-22
7
24
  - [#94](https://github.com/boltops-tools/terraspace/pull/94) terraspace fmt command
8
25
 
@@ -104,7 +104,7 @@ module Terraspace::All
104
104
  def run_terraspace(mod_name)
105
105
  set_log_path!(mod_name)
106
106
  name = command_map(@command)
107
- o = @options.merge(mod: mod_name, yes: true, build: false, input: false)
107
+ o = @options.merge(mod: mod_name, yes: true, build: false, input: false, log_to_stderr: true)
108
108
  o.merge!(quiet: false) if @command == "init" # noisy so can filter and summarize output
109
109
  case @command
110
110
  when "up"
@@ -27,6 +27,8 @@ module Terraspace
27
27
  config.build.cache_dir = ":CACHE_ROOT/:REGION/:ENV/:BUILD_DIR"
28
28
  config.build.cache_root = nil # defaults to /full/path/to/.terraspace-cache
29
29
  config.build.clean_cache = nil # defaults to /full/path/to/.terraspace-cache
30
+ config.build.default_pass_files = ["/files/"]
31
+ config.build.pass_files = []
30
32
  config.bundle = ActiveSupport::OrderedOptions.new
31
33
  config.bundle.logger = ts_logger
32
34
  config.init = ActiveSupport::OrderedOptions.new
@@ -27,6 +27,9 @@ module Terraspace
27
27
  reconfigure_option = Proc.new {
28
28
  option :reconfigure, type: :boolean, desc: "Add terraform -reconfigure option"
29
29
  }
30
+ type_option = Proc.new {
31
+ option :type, default: "stack", aliases: %w[t], desc: "Type: stack, module, or all"
32
+ }
30
33
 
31
34
  desc "all SUBCOMMAND", "all subcommands"
32
35
  long_desc Help.text(:all)
@@ -82,10 +85,18 @@ module Terraspace
82
85
  Down.new(options.merge(mod: mod)).run
83
86
  end
84
87
 
88
+ desc "force_unlock", "Calls terrform force-unlock"
89
+ long_desc Help.text(:force_unlock)
90
+ instance_option.call
91
+ def force_unlock(mod, lock_id)
92
+ Commander.new("force-unlock", options.merge(mod: mod, lock_id: lock_id)).run
93
+ end
94
+
85
95
  desc "fmt", "Run terraform fmt"
86
96
  long_desc Help.text(:fmt)
87
- def fmt
88
- Fmt.new(options).run
97
+ type_option.call
98
+ def fmt(mod=nil)
99
+ Fmt.new(options.merge(mod: mod)).run
89
100
  end
90
101
 
91
102
  desc "info STACK", "Get info about stack."
@@ -106,7 +117,7 @@ module Terraspace
106
117
 
107
118
  desc "list", "List stacks and modules."
108
119
  long_desc Help.text(:list)
109
- option :type, default: "stack", aliases: %w[t], desc: "Type: stack, module, or all"
120
+ type_option.call
110
121
  def list
111
122
  List.new(options).run
112
123
  end
@@ -5,11 +5,12 @@ class Terraspace::CLI
5
5
 
6
6
  def initialize(options={})
7
7
  @options = options
8
+ @mod_name = options[:mod]
8
9
  end
9
10
 
10
11
  def run
11
12
  logger.info "Formating terraform files"
12
- app_source_dirs.each do |dir|
13
+ dirs.each do |dir|
13
14
  format(dir)
14
15
  end
15
16
  end
@@ -17,5 +18,23 @@ class Terraspace::CLI
17
18
  def format(dir)
18
19
  Runner.new(dir).format!
19
20
  end
21
+
22
+ private
23
+ def dirs
24
+ if @mod_name
25
+ type_dirs.select { |p| p.include?(@mod_name) }
26
+ else
27
+ type_dirs
28
+ end
29
+ end
30
+
31
+ def type_dirs
32
+ type = @options[:type]
33
+ if type
34
+ app_source_dirs.select { |p| p.include?("/#{type.pluralize}/") }
35
+ else
36
+ app_source_dirs
37
+ end
38
+ end
20
39
  end
21
40
  end
@@ -1,5 +1,7 @@
1
1
  ## Example
2
2
 
3
+ Format all source files.
4
+
3
5
  $ terraspace fmt
4
6
  Formating terraform files
5
7
  app/modules/example
@@ -7,4 +9,14 @@
7
9
  outputs.tf
8
10
  variables.tf
9
11
  app/stacks/demo
10
- main.tf
12
+ main.tf
13
+
14
+ Format specific module or stack.
15
+
16
+ $ terraspace fmt stack1
17
+ $ terraspace fmt module1
18
+
19
+ Format scoping to module or stack types. In case there's a module and stack with the same name.
20
+
21
+ $ terraspace fmt example -t module
22
+ $ terraspace fmt demo -t stacke
@@ -0,0 +1,7 @@
1
+ ## Example
2
+
3
+ terraspace force_unlock demo ab7f3469-2a5f-07b9-29c5-dec1537ec8b0
4
+
5
+ Instance option:
6
+
7
+ terraspace force_unlock demo -i 2 ab7f3469-2a5f-07b9-29c5-dec1537ec8b0
@@ -1,14 +1,15 @@
1
1
  module Terraspace::Compiler::Strategy
2
2
  class Mod < AbstractBase
3
3
  def run
4
- ext = File.extname(@src_path).sub('.','')
5
- klass = strategy_class(ext)
4
+ klass = strategy_class(@src_path)
6
5
  strategy = klass.new(@mod, @src_path) # IE: Terraspace::Compiler::Strategy::Mod::Rb.new
7
6
  strategy.run
8
7
  end
9
8
 
10
- def strategy_class(ext)
9
+ def strategy_class(path)
10
+ ext = File.extname(path).sub('.','')
11
11
  return Mod::Pass if ext.empty? # infinite loop without this
12
+ return Mod::Pass if Terraspace.pass_file?(path)
12
13
  "Terraspace::Compiler::Strategy::Mod::#{ext.camelize}".constantize rescue Mod::Pass
13
14
  end
14
15
  end
@@ -10,11 +10,17 @@ module Terraspace::Compiler
10
10
  end
11
11
 
12
12
  def dest_path
13
- name = @dest_name || @src_path.sub('.rb','.tf.json')
13
+ name = get_name
14
14
  name = basename(name)
15
15
  "#{dest_dir}/#{name}"
16
16
  end
17
17
 
18
+ def get_name
19
+ return @dest_name if @dest_name
20
+ return @src_path if Terraspace.pass_file?(@src_path)
21
+ @src_path.sub('.rb','.tf.json')
22
+ end
23
+
18
24
  def dest_dir
19
25
  if @mod.is_a?(Terraspace::Mod::Remote)
20
26
  File.dirname(@src_path) # for Mod::Remote src is dest
@@ -51,5 +51,12 @@ module Terraspace
51
51
  def logger=(v)
52
52
  @@logger = v
53
53
  end
54
+
55
+ def pass_file?(path)
56
+ pass_files = config.build.pass_files + config.build.default_pass_files
57
+ pass_files.uniq.detect do |i|
58
+ i.is_a?(Regexp) ? path =~ i : path.include?(i)
59
+ end
60
+ end
54
61
  end
55
62
  end
@@ -2,6 +2,17 @@ module Terraspace::Hooks
2
2
  class Runner
3
3
  include Terraspace::Util
4
4
 
5
+ # exposing mod and hook so terraspace hooks have access to them via runner context. IE:
6
+ #
7
+ # class EnvExporter
8
+ # def call(runner)
9
+ # puts "runner.hook #{runner.hook}"
10
+ # end
11
+ # end
12
+ #
13
+ # Docs: http://terraspace.cloud/docs/config/hooks/ruby/#method-argument
14
+ #
15
+ attr_reader :mod, :hook
5
16
  def initialize(mod, hook)
6
17
  @mod, @hook = mod, hook
7
18
  @execute = @hook["execute"]
@@ -12,12 +23,24 @@ module Terraspace::Hooks
12
23
  when String
13
24
  Terraspace::Shell.new(@mod, @execute, exit_on_fail: @hook["exit_on_fail"]).run
14
25
  when -> (e) { e.respond_to?(:public_instance_methods) && e.public_instance_methods.include?(:call) }
15
- @execute.new.call
26
+ executor = @execute.new
16
27
  when -> (e) { e.respond_to?(:call) }
17
- @execute.call
28
+ executor = @execute
18
29
  else
19
30
  logger.warn "WARN: execute option not set for hook: #{@hook.inspect}"
20
31
  end
32
+
33
+ return unless executor
34
+
35
+ meth = executor.method(:call)
36
+ case meth.arity
37
+ when 0
38
+ executor.call # backwards compatibility
39
+ when 1
40
+ executor.call(self)
41
+ else
42
+ raise "The #{executor} call method definition has been more than 1 arguments and is not supported"
43
+ end
21
44
  end
22
45
  end
23
46
  end
@@ -29,70 +29,98 @@ module Terraspace
29
29
 
30
30
  def popen3(env)
31
31
  Open3.popen3(env, @command, chdir: @mod.cache_dir) do |stdin, stdout, stderr, wait_thread|
32
- mimic_terraform_input(stdin, stdout)
33
- while out = stdout.gets
34
- terraform_to_stdout(out)
35
- end
36
-
37
- while err = stderr.gets
38
- @error ||= Error.new
39
- @error.lines << err # aggregate all error lines
40
- unless @error.known?
41
- # Sometimes may print a "\e[31m\n" which like during dependencies fetcher init
42
- # suppress it so dont get a bunch of annoying "newlines"
43
- next if err == "\e[31m\n" && @options[:suppress_error_color]
44
- logger.error(err)
45
- end
46
- end
47
-
32
+ handle_streams(stdin, stdout, stderr)
48
33
  status = wait_thread.value.exitstatus
49
34
  exit_status(status)
50
35
  end
51
36
  end
52
37
 
53
- def exit_status(status)
54
- return if status == 0
38
+ BLOCK_SIZE = Integer(ENV['TS_BUFFER_BLOCK_SIZE'] || 102400)
39
+ BUFFER_TIMEOUT = Integer(ENV['TS_BUFFER_TIMEOUT'] || 3600) # 3600s = 1h
40
+ def handle_streams(stdin, stdout, stderr)
41
+ files = [stdout, stderr]
42
+ # note: t=0 and t=nil means no timeout. See: https://bit.ly/2PURlCX
43
+ t = BUFFER_TIMEOUT.to_i unless BUFFER_TIMEOUT.nil?
44
+ Timeout::timeout(t) do
45
+ until all_eof?(files) do
46
+ ready = IO.select(files, nil, nil, 0.1)
47
+ next unless ready
55
48
 
56
- exit_on_fail = @options[:exit_on_fail].nil? ? true : @options[:exit_on_fail]
57
- if @error && @error.known?
58
- raise @error.instance
59
- elsif exit_on_fail
60
- logger.error "Error running command: #{@command}".color(:red)
61
- exit status
49
+ readable = ready[0]
50
+ readable.each do |f|
51
+ buffer = f.read_nonblock(BLOCK_SIZE, exception: false)
52
+ next unless buffer
53
+
54
+ lines = buffer.split("\n")
55
+ lines.each do |line|
56
+ if f.fileno == stdout.fileno
57
+ handle_stdout(line)
58
+ handle_input(stdin, line)
59
+ else
60
+ handle_stderr(line)
61
+ end
62
+ end
63
+ end
64
+ end
62
65
  end
63
66
  end
64
67
 
68
+ def handle_stderr(line)
69
+ @error ||= Error.new
70
+ @error.lines << line # aggregate all error lines
71
+
72
+ return if @error.known?
73
+ # Sometimes may print a "\e[31m\n" which like during dependencies fetcher init
74
+ # suppress it so dont get a bunch of annoying "newlines"
75
+ return if line == "\e[31m\n" && @options[:suppress_error_color]
76
+
77
+ logger.error(line)
78
+ end
79
+
80
+ def all_eof?(files)
81
+ files.find { |f| !f.eof }.nil?
82
+ end
83
+
65
84
  # Terraform doesnt seem to stream the line that prompts with "Enter a value:" when using Open3.popen3
66
85
  # Hack around it by mimicking the "Enter a value:" prompt
67
86
  #
68
87
  # Note: system does stream the prompt but using Open3.popen3 so we can capture output to save to logs.
69
- def mimic_terraform_input(stdin, stdout)
70
- shown = false
88
+ def handle_input(stdin, line)
89
+ # stdout doesnt seem to flush and show "Enter a value: " look for earlier output
71
90
  patterns = [
72
91
  "Only 'yes' will be accepted", # prompt for apply. can happen on apply
73
92
  "\e[0m\e[1mvar.", # prompts for variable input. can happen on plan or apply. looking for bold marker also in case "var." shows up somewhere else
74
93
  ]
75
- while out = stdout.gets
76
- terraform_to_stdout(out) unless shown && out.include?("Enter a value:")
77
- shown = false if out.include?("Enter a value:") # reset shown in case of multiple input prompts
78
-
79
- # Sometimes stdout doesnt flush and show "Enter a value: ", so mimic it
80
- if patterns.any? { |pattern| out.include?(pattern) }
81
- print " Enter a value: ".bright
82
- shown = true
83
- stdin.write_nonblock($stdin.gets)
84
- end
94
+ if patterns.any? { |pattern| line.include?(pattern) }
95
+ print "\n Enter a value: ".bright
96
+ stdin.write_nonblock($stdin.gets)
97
+ end
98
+ end
99
+
100
+ def exit_status(status)
101
+ return if status == 0
102
+
103
+ exit_on_fail = @options[:exit_on_fail].nil? ? true : @options[:exit_on_fail]
104
+ if @error && @error.known?
105
+ raise @error.instance
106
+ elsif exit_on_fail
107
+ logger.error "Error running command: #{@command}".color(:red)
108
+ exit status
85
109
  end
86
110
  end
87
111
 
88
- # Allows piping to jq. IE:
89
- # terraspace show demo --json | jq
90
- def terraform_to_stdout(out)
91
- # so terraform output goes stdout
112
+ def handle_stdout(line)
113
+ prompted = line.include?('Enter a value')
114
+ @prompt_shown ||= prompted
115
+ return if @prompt_shown && prompted
116
+
117
+ # Terraspace logger has special stdout method so original terraform output
118
+ # can be piped to jq. IE:
119
+ # terraspace show demo --json | jq
92
120
  if logger.respond_to?(:stdout) && !@options[:log_to_stderr]
93
- logger.stdout(out)
121
+ logger.stdout(line)
94
122
  else
95
- logger.info(out)
123
+ logger.info(line)
96
124
  end
97
125
  end
98
126
  end
@@ -3,7 +3,7 @@ require "tempfile"
3
3
  module Terraspace::Terraform::Args
4
4
  class Default
5
5
  def initialize(mod, name, options={})
6
- @mod, @name, @options = mod, name, options
6
+ @mod, @name, @options = mod, name.underscore, options
7
7
  @quiet = @options[:quiet].nil? ? true : @options[:quiet]
8
8
  end
9
9
 
@@ -11,14 +11,18 @@ module Terraspace::Terraform::Args
11
11
  # https://terraspace.cloud/docs/ci-automation/
12
12
  ENV['TF_IN_AUTOMATION'] = '1' if @options[:auto]
13
13
 
14
- if %w[apply destroy init output plan show].include?(@name)
15
- meth = "#{@name}_args"
16
- send(meth)
14
+ args_meth = "#{@name}_args"
15
+ if respond_to?(args_meth)
16
+ send(args_meth)
17
17
  else
18
18
  []
19
19
  end
20
20
  end
21
21
 
22
+ def force_unlock_args
23
+ [" -force #{@options[:lock_id]}"]
24
+ end
25
+
22
26
  def apply_args
23
27
  args = auto_approve_arg
24
28
  var_files = @options[:var_files]
@@ -1,3 +1,3 @@
1
1
  module Terraspace
2
- VERSION = "0.6.4"
2
+ VERSION = "0.6.9"
3
3
  end
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: 0.6.4
4
+ version: 0.6.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tung Nguyen
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-22 00:00:00.000000000 Z
11
+ date: 2021-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -508,6 +508,7 @@ files:
508
508
  - lib/terraspace/cli/help/console.md
509
509
  - lib/terraspace/cli/help/down.md
510
510
  - lib/terraspace/cli/help/fmt.md
511
+ - lib/terraspace/cli/help/force_unlock.md
511
512
  - lib/terraspace/cli/help/info.md
512
513
  - lib/terraspace/cli/help/init.md
513
514
  - lib/terraspace/cli/help/list.md