test-kitchen 1.9.2 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 81027eb8109e1f18afe8ab86a7868a6904a85143
4
- data.tar.gz: 0d3f2807d8ed919ab730c5ca015751b465cdf97c
3
+ metadata.gz: 0bc5575233f5ed2155fa31c3bb7465a277263d51
4
+ data.tar.gz: 52412155a99cc874b7cb7c9293117ea22d900945
5
5
  SHA512:
6
- metadata.gz: d3814e745b4d08b7cb20b28fa1dad5d1503ee7a49c17cea73c6f96300bb559c37eceba74b415fd7855a13b4514b198587f45bfab9ae9bc5a4e362b4202db91f2
7
- data.tar.gz: d904f5626be65776e138aaa4ed7d38365a6d30339e608dd003f2582a9ae2e35c0178103e3bb43b33ed2b971d4ac7ad70d57f54d00ac7141044e21efa9f102c00
6
+ metadata.gz: d096151c56581d3aeb407c30dfb42f2fd419c608d1f29ddc623bc3e62c025c03b988e1a99b9a26945e6325c5863b5e42b440f356f91703c3fa448084500723da
7
+ data.tar.gz: b76320028f75a9e9786462437cfbce9354181b9ca49cb6ae79ad0ea4a9cc4e379cfd64e0c8fed7d051b6f703a4b769a341a7ee4f3952806c9e90f83a5d76e613
data/.kitchen.ci.yml CHANGED
@@ -23,4 +23,4 @@ verifier:
23
23
  suites:
24
24
  - name: default
25
25
  run_list:
26
- - recipe[mercurial]
26
+ - recipe[filezilla]
data/Berksfile CHANGED
@@ -1,3 +1,3 @@
1
1
  source "https://supermarket.chef.io"
2
2
 
3
- cookbook "mercurial"
3
+ cookbook "filezilla"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Change Log
2
2
 
3
+ ## [v1.10.0](https://github.com/test-kitchen/test-kitchen/tree/v1.10.0) (2016-06-16)
4
+ [Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.9.2...v1.10.0)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - Retry `Kitchen::Provisioner\#run\_command` after allowed exit codes [\#1055](https://github.com/test-kitchen/test-kitchen/pull/1055) ([smurawski](https://github.com/smurawski))
9
+ - Add fallback support for `policyfile` for compat with the older policyfile\_zero [\#1053](https://github.com/test-kitchen/test-kitchen/pull/1053) ([coderanger](https://github.com/coderanger))
10
+
3
11
  ## [v1.9.2](https://github.com/test-kitchen/test-kitchen/tree/v1.9.2) (2016-06-09)
4
12
  [Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.9.1...v1.9.2)
5
13
 
data/appveyor.yml CHANGED
@@ -26,9 +26,11 @@ install:
26
26
  - ps: net user /add $env:machine_user $env:machine_pass
27
27
  - ps: net localgroup administrators $env:machine_user /add
28
28
  - ps: $env:PATH="C:\Ruby$env:ruby_version\bin;$env:PATH"
29
- - ps: gem install bundler --quiet --no-ri --no-rdoc
30
29
  - ps: Invoke-WebRequest -Uri http://curl.haxx.se/ca/cacert.pem -OutFile c:\projects\test_kitchen\certs.pem
31
30
  - ps: Write-Host $env:path
31
+ - gem update --system || gem update --system || gem update --system
32
+ - gem install bundler --quiet --no-ri --no-rdoc || gem install bundler --quiet --no-ri --no-rdoc || gem install bundler --quiet --no-ri --no-rdoc
33
+ - update_rubygems
32
34
  - ruby --version
33
35
  - gem --version
34
36
  - bundler --version
@@ -175,12 +175,19 @@ module Kitchen
175
175
  end
176
176
  end
177
177
  threads.map(&:join)
178
+ report_errors
179
+ end
180
+
181
+ # private
182
+
183
+ def report_errors
178
184
  unless @action_errors.empty?
179
- raise ActionFailed.new("#{@action_errors.length} actions failed.", @action_errors)
185
+ msg = ["#{@action_errors.length} actions failed.",
186
+ @action_errors.map { |e| ">>>>>> #{e.message}" }].join("\n")
187
+ raise ActionFailed.new(msg, @action_errors)
180
188
  end
181
189
  end
182
190
 
183
- # private
184
191
  def concurrency_setting(instances)
185
192
  concurrency = 1
186
193
  if options[:concurrency]
@@ -192,6 +199,8 @@ module Kitchen
192
199
 
193
200
  def run_action_in_thread(action, instance, *args)
194
201
  instance.public_send(action, *args)
202
+ rescue Kitchen::InstanceFailure => e
203
+ @action_errors << e
195
204
  rescue Kitchen::ActionFailed => e
196
205
  new_error = Kitchen::ActionFailed.new("#{e.message} on #{instance.name}")
197
206
  new_error.set_backtrace(e.backtrace)
@@ -32,6 +32,10 @@ module Kitchen
32
32
  default_config :https_proxy, nil
33
33
  default_config :ftp_proxy, nil
34
34
 
35
+ default_config :retry_on_exit_code, []
36
+ default_config :max_retries, 1
37
+ default_config :wait_for_retry, 30
38
+
35
39
  default_config :root_path do |provisioner|
36
40
  provisioner.windows_os? ? "$env:TEMP\\kitchen" : "/tmp/kitchen"
37
41
  end
@@ -59,6 +63,7 @@ module Kitchen
59
63
  #
60
64
  # @param state [Hash] mutable instance state
61
65
  # @raise [ActionFailed] if the action could not be completed
66
+ # rubocop:disable Metrics/AbcSize
62
67
  def call(state)
63
68
  create_sandbox
64
69
  sandbox_dirs = Dir.glob(File.join(sandbox_path, "*"))
@@ -70,7 +75,12 @@ module Kitchen
70
75
  conn.upload(sandbox_dirs, config[:root_path])
71
76
  debug("Transfer complete")
72
77
  conn.execute(prepare_command)
73
- conn.execute(run_command)
78
+ conn.execute_with_retry(
79
+ run_command,
80
+ config[:retry_on_exit_code],
81
+ config[:max_retries],
82
+ config[:wait_for_retry]
83
+ )
74
84
  end
75
85
  rescue Kitchen::Transport::TransportFailed => ex
76
86
  raise ActionFailed, ex.message
@@ -92,7 +92,7 @@ module Kitchen
92
92
  # kitchen root
93
93
  # @api private
94
94
  def policyfile
95
- basename = config[:policyfile_path] || "Policyfile.rb"
95
+ basename = config[:policyfile_path] || config[:policyfile] || "Policyfile.rb"
96
96
  File.join(config[:kitchen_root], basename)
97
97
  end
98
98
 
@@ -54,6 +54,8 @@ module Kitchen
54
54
  default_config :log_file, nil
55
55
  default_config :log_level, "auto"
56
56
  default_config :profile_ruby, false
57
+ # The older policyfile_zero used `policyfile` so support it for compat.
58
+ default_config :policyfile, nil
57
59
  # Will try to autodetect by searching for `Policyfile.rb` if not set.
58
60
  # If set, will error if the file doesn't exist.
59
61
  default_config :policyfile_path, nil
@@ -167,7 +169,7 @@ module Kitchen
167
169
  # kitchen root
168
170
  # @api private
169
171
  def policyfile
170
- policyfile_basename = config[:policyfile_path] || "Policyfile.rb"
172
+ policyfile_basename = config[:policyfile_path] || config[:policyfile] || "Policyfile.rb"
171
173
  File.join(config[:kitchen_root], policyfile_basename)
172
174
  end
173
175
 
@@ -350,7 +352,7 @@ module Kitchen
350
352
  # @raise [UserError]
351
353
  # @api private
352
354
  def sanity_check_sandbox_options!
353
- if config[:policyfile_path] && !File.exist?(policyfile)
355
+ if (config[:policyfile_path] || config[:policyfile]) && !File.exist?(policyfile)
354
356
  raise UserError, "policyfile_path set in config "\
355
357
  "(#{config[:policyfile_path]} could not be found. " \
356
358
  "Expected to find it at full path #{policyfile} " \
@@ -76,7 +76,7 @@ module Kitchen
76
76
 
77
77
  prefix_command(
78
78
  wrap_shell_code(
79
- [cmd, *chef_client_args].join(" ").
79
+ [cmd, *chef_client_args, last_exit_code].join(" ").
80
80
  tap { |str| str.insert(0, reload_ps1_path) if windows_os? }
81
81
  )
82
82
  )
@@ -84,6 +84,10 @@ module Kitchen
84
84
 
85
85
  private
86
86
 
87
+ def last_exit_code
88
+ "; exit $LastExitCode" if powershell_shell?
89
+ end
90
+
87
91
  # Adds optional flags to a chef-client command, depending on
88
92
  # configuration data. Note that this method mutates the incoming Array.
89
93
  #
@@ -28,7 +28,14 @@ module Kitchen
28
28
  # Wrapped exception for any internally raised Transport errors.
29
29
  #
30
30
  # @author Salim Afiune <salim@afiunemaya.com.mx>
31
- class TransportFailed < TransientFailure; end
31
+ class TransportFailed < TransientFailure
32
+ attr_reader :exit_code
33
+
34
+ def initialize(message, exit_code = nil)
35
+ @exit_code = exit_code
36
+ super(message)
37
+ end
38
+ end
32
39
 
33
40
  # Base class for a transport.
34
41
  #
@@ -54,7 +61,8 @@ module Kitchen
54
61
  # @param state [Hash] mutable instance state
55
62
  # @return [Connection] a connection for this transport
56
63
  # @raise [TransportFailed] if a connection could not be returned
57
- def connection(state) # rubocop:disable Lint/UnusedMethodArgument
64
+ # rubocop:disable Lint/UnusedMethodArgument
65
+ def connection(state)
58
66
  raise ClientError, "#{self.class}#connection must be implemented"
59
67
  end
60
68
 
@@ -97,10 +105,41 @@ module Kitchen
97
105
  # @param command [String] command string to execute
98
106
  # @raise [TransportFailed] if the command does not exit successfully,
99
107
  # which may vary by implementation
100
- def execute(command) # rubocop:disable Lint/UnusedMethodArgument
108
+ def execute(command)
101
109
  raise ClientError, "#{self.class}#execute must be implemented"
102
110
  end
103
111
 
112
+ # Execute a command on the remote host and retry
113
+ #
114
+ # @param command [String] command string to execute
115
+ # @param retryable_exit_codes [Array] Array of exit codes to retry against
116
+ # @param max_retries [Fixnum] maximum number of retry attempts
117
+ # @param wait_time [Fixnum] number of seconds to wait before retrying command
118
+ # @raise [TransportFailed] if the command does not exit successfully,
119
+ # which may vary by implementation
120
+ def execute_with_retry(command, retryable_exit_codes = [], max_retries = 1, wait_time = 30)
121
+ tries = 0
122
+ begin
123
+ tries += 1
124
+ debug("Attempting to execute command - try #{tries} of #{max_retries}.")
125
+ execute(command)
126
+ rescue Kitchen::Transport::TransportFailed => e
127
+ if retry?(tries, max_retries, retryable_exit_codes, e.exit_code)
128
+ close
129
+ sleep wait_time
130
+ retry
131
+ else
132
+ raise e
133
+ end
134
+ end
135
+ end
136
+
137
+ def retry?(current_try, max_retries, retryable_exit_codes, exit_code)
138
+ current_try <= max_retries &&
139
+ !retryable_exit_codes.nil? &&
140
+ retryable_exit_codes.include?(exit_code)
141
+ end
142
+
104
143
  # Builds a LoginCommand which can be used to open an interactive
105
144
  # session on the remote host.
106
145
  #
@@ -124,8 +124,10 @@ module Kitchen
124
124
  exit_code = execute_with_exit_code(command)
125
125
 
126
126
  if exit_code != 0
127
- raise Transport::SshFailed,
128
- "SSH exited (#{exit_code}) for command: [#{command}]"
127
+ raise Transport::SshFailed.new(
128
+ "SSH exited (#{exit_code}) for command: [#{command}]",
129
+ exit_code
130
+ )
129
131
  end
130
132
  rescue Net::SSH::Exception => ex
131
133
  raise SshFailed, "SSH command failed (#{ex.message})"
@@ -105,8 +105,10 @@ module Kitchen
105
105
  log_stderr_on_warn(stderr)
106
106
  elsif exit_code != 0
107
107
  log_stderr_on_warn(stderr)
108
- raise Transport::WinrmFailed,
109
- "WinRM exited (#{exit_code}) for command: [#{command}]"
108
+ raise Transport::WinrmFailed.new(
109
+ "WinRM exited (#{exit_code}) for command: [#{command}]",
110
+ exit_code
111
+ )
110
112
  end
111
113
  end
112
114
 
@@ -17,5 +17,5 @@
17
17
  # limitations under the License.
18
18
 
19
19
  module Kitchen
20
- VERSION = "1.9.2"
20
+ VERSION = "1.10.0"
21
21
  end
@@ -157,6 +157,7 @@ describe Kitchen::Provisioner::Base do
157
157
  FileUtils.mkdir_p(File.join(provisioner.sandbox_path, "stuff"))
158
158
  transport.stubs(:connection).yields(connection)
159
159
  connection.stubs(:execute)
160
+ connection.stubs(:execute_with_retry)
160
161
  connection.stubs(:upload)
161
162
  end
162
163
 
@@ -193,7 +194,7 @@ describe Kitchen::Provisioner::Base do
193
194
  connection.expects(:execute).with("install").in_sequence(order)
194
195
  connection.expects(:execute).with("init").in_sequence(order)
195
196
  connection.expects(:execute).with("prepare").in_sequence(order)
196
- connection.expects(:execute).with("run").in_sequence(order)
197
+ connection.expects(:execute_with_retry).with("run", [], 1, 30).in_sequence(order)
197
198
 
198
199
  cmd
199
200
  end
@@ -1055,6 +1055,71 @@ POLICYFILE
1055
1055
  end
1056
1056
  end
1057
1057
  end
1058
+ describe "with a fallback policyfile" do
1059
+
1060
+ let(:config) do
1061
+ {
1062
+ :policyfile => "foo-policy.rb",
1063
+ :test_base_path => "/basist",
1064
+ :kitchen_root => "/rooty"
1065
+ }
1066
+ end
1067
+
1068
+ before do
1069
+ Kitchen::Provisioner::Chef::Policyfile.stubs(:load!)
1070
+ Kitchen::Provisioner::Chef::Policyfile.stubs(:new).returns(resolver)
1071
+ provisioner.stubs(:supports_policyfile?).returns(true)
1072
+ end
1073
+
1074
+ describe "when the policyfile exists" do
1075
+
1076
+ let(:policyfile_path) { "#{kitchen_root}/foo-policy.rb" }
1077
+ let(:policyfile_lock_path) { "#{kitchen_root}/foo-policy.lock.json" }
1078
+
1079
+ before do
1080
+ File.open(policyfile_path, "wb") do |file|
1081
+ file.write(<<-POLICYFILE)
1082
+ name 'wat'
1083
+ run_list 'wat'
1084
+ cookbook 'wat'
1085
+ POLICYFILE
1086
+ end
1087
+ File.open(policyfile_lock_path, "wb") do |file|
1088
+ file.write(<<-POLICYFILE)
1089
+ {
1090
+ "name": "wat"
1091
+ }
1092
+ POLICYFILE
1093
+ end
1094
+ end
1095
+
1096
+ it "uses uses the policyfile to resolve dependencies" do
1097
+ Kitchen::Provisioner::Chef::Policyfile.stubs(:load!)
1098
+ resolver.expects(:resolve)
1099
+
1100
+ provisioner.create_sandbox
1101
+ end
1102
+
1103
+ it "passes the correct path to the policyfile resolver" do
1104
+ Kitchen::Provisioner::Chef::Policyfile.
1105
+ expects(:new).
1106
+ with(policyfile_path, instance_of(String), anything).
1107
+ returns(resolver)
1108
+
1109
+ Kitchen::Provisioner::Chef::Policyfile.stubs(:load!)
1110
+ resolver.expects(:resolve)
1111
+
1112
+ provisioner.create_sandbox
1113
+ end
1114
+ end
1115
+ describe "when the policyfile doesn't exist" do
1116
+
1117
+ it "raises a UserError" do
1118
+ proc { provisioner.create_sandbox }.must_raise Kitchen::UserError
1119
+ end
1120
+
1121
+ end
1122
+ end
1058
1123
  end
1059
1124
 
1060
1125
  describe "with a Berksfile under kitchen_root" do
@@ -54,6 +54,27 @@ describe Kitchen::Transport::Base do
54
54
  transport.send(:logger).must_equal Kitchen.logger
55
55
  end
56
56
  end
57
+
58
+ describe Kitchen::Transport::TransportFailed do
59
+
60
+ let(:failure_with_no_exit_code) { Kitchen::Transport::TransportFailed.new("Boom") }
61
+ let(:failure_with_exit_code) { Kitchen::Transport::TransportFailed.new("Boom", 123) }
62
+
63
+ describe "when no exit code is provided" do
64
+
65
+ it "#exit_code is nil" do
66
+ failure_with_no_exit_code.exit_code.must_be_nil
67
+ end
68
+ end
69
+
70
+ describe "when an exit code is provided" do
71
+
72
+ it "#exit_code returns the supplied exit code" do
73
+ failure_with_exit_code.exit_code.must_equal 123
74
+ end
75
+ end
76
+ end
77
+
57
78
  end
58
79
 
59
80
  describe Kitchen::Transport::Base::Connection do
@@ -86,4 +107,25 @@ describe Kitchen::Transport::Base::Connection do
86
107
  it "has a #wait_until_ready method that does nothing" do
87
108
  connection.wait_until_ready.must_be_nil
88
109
  end
110
+
111
+ describe "#execute_with_retry" do
112
+
113
+ let(:failure_with_exit_code) { Kitchen::Transport::TransportFailed.new("Boom", 123) }
114
+
115
+ it "raises ClientError with no retries" do
116
+ proc { connection.execute_with_retry("hi", [], nil, nil) }.
117
+ must_raise Kitchen::ClientError
118
+ end
119
+
120
+ it "retries three times" do
121
+ connection.expects(:execute).with("Hi").returns("Hello")
122
+ connection.expects(:debug).with("Attempting to execute command - try 3 of 3.")
123
+ connection.expects(:execute).with("Hi").raises(failure_with_exit_code)
124
+ connection.expects(:debug).with("Attempting to execute command - try 2 of 3.")
125
+ connection.expects(:execute).with("Hi").raises(failure_with_exit_code)
126
+ connection.expects(:debug).with("Attempting to execute command - try 1 of 3.")
127
+
128
+ connection.execute_with_retry("Hi", [123], 3, 1).must_equal "Hello"
129
+ end
130
+ end
89
131
  end
@@ -900,6 +900,14 @@ describe Kitchen::Transport::Ssh::Connection do
900
900
  }.must_raise Kitchen::Transport::SshFailed
901
901
  err.message.must_equal "SSH exited (42) for command: [doit]"
902
902
  end
903
+
904
+ it "returns the exit code with an SshFailed exception" do
905
+ begin
906
+ connection.execute("doit")
907
+ rescue Kitchen::Transport::SshFailed => e
908
+ e.exit_code.must_equal 42
909
+ end
910
+ end
903
911
  end
904
912
 
905
913
  describe "for an interrupted command" do
@@ -935,6 +935,14 @@ MSG
935
935
  }.must_raise Kitchen::Transport::WinrmFailed
936
936
  err.message.must_equal "WinRM exited (1) for command: [doit]"
937
937
  end
938
+
939
+ it "raises WinrmFailed exception with the exit code of the failure" do
940
+ begin
941
+ connection.execute("doit")
942
+ rescue Kitchen::Transport::WinrmFailed => e
943
+ e.exit_code.must_equal 1
944
+ end
945
+ end
938
946
  end
939
947
  end
940
948
 
data/test-kitchen.gemspec CHANGED
@@ -34,6 +34,7 @@ Gem::Specification.new do |gem|
34
34
  gem.add_development_dependency "pry-byebug"
35
35
  gem.add_development_dependency "pry-stack_explorer"
36
36
  gem.add_development_dependency "rb-readline"
37
+ gem.add_development_dependency "overcommit", "= 0.33.0"
37
38
  gem.add_development_dependency "winrm", "~> 1.6"
38
39
  gem.add_development_dependency "winrm-elevated", "~> 0.4.0"
39
40
  gem.add_development_dependency "winrm-fs", "~> 0.4.1"
@@ -1,3 +1,3 @@
1
- describe command("hg") do
2
- it { should exist }
1
+ describe oneget("FileZilla Client 3.14.1") do
2
+ it { should be_installed }
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: test-kitchen
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.2
4
+ version: 1.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fletcher Nichol
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-09 00:00:00.000000000 Z
11
+ date: 2016-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mixlib-shellout
@@ -168,6 +168,20 @@ dependencies:
168
168
  - - ">="
169
169
  - !ruby/object:Gem::Version
170
170
  version: '0'
171
+ - !ruby/object:Gem::Dependency
172
+ name: overcommit
173
+ requirement: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - '='
176
+ - !ruby/object:Gem::Version
177
+ version: 0.33.0
178
+ type: :development
179
+ prerelease: false
180
+ version_requirements: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - '='
183
+ - !ruby/object:Gem::Version
184
+ version: 0.33.0
171
185
  - !ruby/object:Gem::Dependency
172
186
  name: winrm
173
187
  requirement: !ruby/object:Gem::Requirement