test-kitchen 1.9.2 → 1.10.0

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
  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