test-kitchen 1.22.1 → 1.23.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
  SHA256:
3
- metadata.gz: a25f1efb06c17a12ffbdfdf5047e74b1fb784945e687848b1966da79e571b164
4
- data.tar.gz: 58e429e9df4ecd71a335243a0580a0351aacff07d2ee1d1f9a05f0eb82a17554
3
+ metadata.gz: 885f8d3b08f1f4c860cd55c370cefc6f2b35e87dc2607ce9f6d60c39788bfd86
4
+ data.tar.gz: 352335dacee5a0ed1dbe5067d658bd2cd85bac1d0ec39c49a1283871c8604953
5
5
  SHA512:
6
- metadata.gz: 35cf9c12e8bdc1eba84b38b50bcee1332cca0db9e5548bccf07f3bc0185d88afc40d75341baee5a3cba64e888bd011e2479206e9796eb6c4a5bbcf51b4aef66f
7
- data.tar.gz: 58f2c081cb0ac675cde486c9455b01e25f7dd98d49122c86039c76bd62ea33603fc203c1d521b3698c684a0b7464c13ca5d70fcfea2bc2f2b78062fa938b4b30
6
+ metadata.gz: cc58bb9f82520bf0e616620a874d0452ac5ed0889cc7e28f0eb1aacfbb8e609436b65ff6f3f687044c3f896fc4846697cf96c78b8adee54816647c9a2f787373
7
+ data.tar.gz: '0909b191b4dc6c965ccf6e5afb59b67cff485035b049de9bea218c89f31753312ab970ca68217dc4c7f4eaa6b33ea814f67ce5a9cda3e31a0efa0a66bc2a3dd1'
@@ -1,12 +1,12 @@
1
1
  # Change Log
2
2
 
3
- ## [v1.22.1](https://github.com/test-kitchen/test-kitchen/tree/v1.22.1) (2018-08-03)
4
- [Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.22.0...v1.22.1)
3
+ ## [v1.23.0](https://github.com/test-kitchen/test-kitchen/tree/v1.23.0) (2018-07-30)
4
+ [Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.22.0...v1.23.0)
5
5
 
6
6
  **Merged pull requests:**
7
7
 
8
- - \[SHACK-295\] ChefDK 2.x uses an old version of net-ssh [\#1439](https://github.com/test-kitchen/test-kitchen/pull/1439) ([tyler-ball](https://github.com/tyler-ball))
9
-
8
+ - Lifecycle hooks [\#1428](https://github.com/test-kitchen/test-kitchen/pull/1428) ([coderanger](https://github.com/coderanger))
9
+ - Minor technical cleanup and unify behavior for files and directories. [\#1401](https://github.com/test-kitchen/test-kitchen/pull/1401) ([coderanger](https://github.com/coderanger))
10
10
 
11
11
  ## [v1.22.0](https://github.com/test-kitchen/test-kitchen/tree/v1.22.0) (2018-06-28)
12
12
  [Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.21.2...v1.22.0)
@@ -21,23 +21,14 @@
21
21
  - Removing thor upper bound in step with berks [\#1410](https://github.com/test-kitchen/test-kitchen/pull/1410) ([cheeseplus](https://github.com/cheeseplus)
22
22
  )
23
23
 
24
-
25
24
  ## [v1.21.1](https://github.com/test-kitchen/test-kitchen/tree/v1.21.1) (2018-04-18)
26
25
  [Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.21.0...v1.21.1)
27
26
 
28
-
29
27
  - Revert: honor root\_path for location of chef installer script [\#1369]
30
28
 
31
29
  ## [v1.21.0](https://github.com/test-kitchen/test-kitchen/tree/v1.21.0) (2018-04-16)
32
30
  [Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.20.0...v1.21.0)
33
31
 
34
- **Release Notes:**
35
-
36
- ### Configuration UX improvements
37
-
38
- Having the kitchen configuration file be hidden has always been a bit odd and so we're moving to using `kitchen.yml` over `.kitchen.yml`.
39
- This also applies to `kitchen.local.yml` and we've made the change backwards compatible so you're not forced to move over right away. Additionally, we've added support for the environment variables `KITCHEN_YML` and KITCHEN_LOCAL_YML` again preserving compatibility if you're using the `*_YAML` forms.
40
-
41
32
  **Merged pull requests:**
42
33
 
43
34
  - Support `\*\_YML` for env vars too, for better UX [\#1398](https://github.com/test-kitchen/test-kitchen/pull/1398) ([coderanger](https://github.com/coderanger))
@@ -57,58 +48,6 @@ This also applies to `kitchen.local.yml` and we've made the change backwards com
57
48
  ## [v1.20.0](https://github.com/test-kitchen/test-kitchen/tree/v1.20.0) (2018-01-19)
58
49
  [Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.19.2...v1.20.0)
59
50
 
60
- **Release Notes:**
61
-
62
- #### Multiple paths for data_bags
63
-
64
- Allows a user to use data_bags from an array of directories
65
-
66
- ```
67
- data_bags_path:
68
- - 'data_bags'
69
- - 'test/integrations/data_bags'
70
- ```
71
-
72
- #### Deprecation Warnings for Configuration Keys
73
-
74
- ```
75
- $ kitchen list default-centos-7
76
- $$$$$$ Deprecated configuration detected:
77
- require_chef_omnibus
78
- Run 'kitchen doctor' for details.
79
- ```
80
-
81
- ```
82
- $ kitchen doctor
83
- $$$$$$ Deprecated configuration detected:
84
- require_chef_omnibus
85
- Run 'kitchen doctor' for details.
86
-
87
- -----> The doctor is in
88
- **** require_chef_omnibus deprecated
89
- The 'require_chef_omnibus' attribute with version values will change
90
- to use the new 'product_version' attribute.
91
-
92
- Note: 'product_name' must be set in order to use 'product_version'
93
- until 'product_name' replaces 'require_chef_omnibus' as the default.
94
-
95
- # New Usage #
96
- provisioner:
97
- product_name: <chef or chefdk>
98
- product_version: 12.0.3
99
- ```
100
-
101
- #### SSH via an HTTP Proxy
102
-
103
- This allows configuring the SSH transport to utilize an HTTP Proxy. The following configuration keys have been added to `transport`:
104
-
105
- ```
106
- ssh_http_proxy_user
107
- ssh_http_proxy_password
108
- ssh_http_proxy_port
109
- ssh_http_proxy
110
- ```
111
-
112
51
  **Merged pull requests:**
113
52
 
114
53
  - Support multiple paths for data bags [\#1313](https://github.com/test-kitchen/test-kitchen/pull/1313) ([thomasdziedzic](https://github.com/thomasdziedzic))
@@ -141,50 +80,6 @@ Removed an extraneous bash shebang that caused the script generated to install c
141
80
  ## [v1.19.0](https://github.com/test-kitchen/test-kitchen/tree/v1.19.0) (2017-11-1)
142
81
  [Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.18.0...v1.19.0)
143
82
 
144
- **Release Notes:**
145
-
146
- #### Driver Commands Removed
147
-
148
- The `kitchen driver` family of commands have been removed. It was not recommended
149
- to use them and it was judged to be more harm than good to leave them in. If you
150
- regularly create new drivers and relied on the skeleton generator, check out
151
- other code skeleton projects like [`chef generate`](https://blog.chef.io/2014/12/09/guest-post-creating-your-own-chef-cookbook-generator/),
152
- and [Cookiecutter](https://github.com/audreyr/cookiecutter).
153
-
154
- #### `kitchen converge -D`
155
-
156
- When you want to get debug logging for your provisioner or verifier, you can now
157
- use the new `-D` (or `--debug`) command line option for `kitchen converge`,
158
- `kitchen verify`, and `kitchen test`. Support has been added to the Chef provisioners,
159
- avoiding the need to use the `log_level: debug` configuration option every time.
160
-
161
- #### `exec` Driver
162
-
163
- A new driver named `exec` is included with Test Kitchen which runs all the
164
- provisioning and verification commands locally, rather than on a VM. This can
165
- be used for testing on systems where you've already created the VM yourself and
166
- installed Test Kitchen on it. Note that this is related but different from the
167
- included `proxy` driver, which also connects to an existing server, but over
168
- SSH/WinRM rather than running commands locally.
169
-
170
- #### `shell` Provisioner `command`
171
-
172
- Previously the included `shell` provisioner allowed running a user-specified bootstrap
173
- script. This has been extended to allow specifying a `command` option with a
174
- string to run, rather than managing a script file.
175
-
176
- #### Faster Busser
177
-
178
- The `busser` verifier has been improved to be faster on the second (or beyond)
179
- verification, or in other cases where the required gems are already present.
180
-
181
- #### `kitchen doctor`
182
-
183
- A `kitchen doctor` command has been added, modeled on Homebrew's `brew doctor`.
184
- This currently doesn't do much, but if you are a Kitchen plugin author, consider
185
- adding more detailed debugging checks and troubleshooting help to your plugin
186
- via this system.
187
-
188
83
  **Merged pull requests**
189
84
 
190
85
  - Basic framework for kitchen doctor [\#1301](https://github.com/test-kitchen/test-kitchen/pull/1301) ([coderanger](https://github.com/coderanger))
@@ -224,7 +119,6 @@ via this system.
224
119
  - New install\_strategy option used in conjunction with product\_name [\#1262](https://github.com/test-kitchen/test-kitchen/pull/1262) ([wrightp](https://github.com/wrightp))
225
120
  - Allow command line arguments config in shell provisioner [\#943](https://github.com/test-kitchen/test-kitchen/pull/943) ([mmckinst](https://github.com/mmckinst))
226
121
 
227
-
228
122
  ## [v1.17.0](https://github.com/test-kitchen/test-kitchen/tree/v1.17.0) (2017-08-11)
229
123
  [Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.16.0...v1.17.0)
230
124
 
@@ -0,0 +1,167 @@
1
+ # Test Kitchen 1.23.0 Release Notes
2
+
3
+ ## Life Cycle Hooks
4
+
5
+ The life cycle hooks system allows running commands before or after any phase
6
+ of Test Kitchen (`create`, `converge`, `verify`, or `destroy`). Commands can be
7
+ run either locally on your workstation (the default) or remotely on the test instance.
8
+
9
+ These hooks are configured under a new `lifecycle:` section in `kitchen.yml`:
10
+
11
+ ```yaml
12
+ lifecycle:
13
+ pre_create: echo before
14
+ post_create:
15
+ - echo after
16
+ - local: echo also after
17
+ - remote: echo after but in the instance
18
+ ```
19
+
20
+ You can also configure hooks on a single platform or suite:
21
+
22
+ ```yaml
23
+ platforms:
24
+ - name: ubuntu-18.04
25
+ lifecycle:
26
+ pre_converge:
27
+ - remote: apt update
28
+
29
+ suites:
30
+ - name: default
31
+ lifecycle:
32
+ post_verify:
33
+ - my_coverage_formatter
34
+ ```
35
+
36
+ Local commands automatically get some environment variables with information
37
+ about which instance the hook is evaluating against:
38
+
39
+ * `KITCHEN_INSTANCE_NAME` - The full name of the instance
40
+ * `KITCHEN_SUITE_NAME` - The name of the suite of the instance
41
+ * `KITCHEN_PLATFORM_NAME` - The name of the platform of the instance
42
+ * `KITCHEN_INSTANCE_HOSTNAME` - The hostname of the instance as reported by the driver plugin
43
+
44
+ You can also pass additional configuration for local commands:
45
+
46
+ ```yaml
47
+ lifecycle:
48
+ pre_converge:
49
+ - local: ./setup.sh
50
+ environment:
51
+ API_KEY: asdf1234
52
+ timeout: 60
53
+ ```
54
+
55
+ Remote commands are normally not allowed during `pre_create` or `post_destroy`
56
+ hooks as there is generally no instance running at that point, but with `pre_destroy`
57
+ hooks you may want to use the `skippable` flag so as to not fail during `kitchen test`:
58
+
59
+ ```yaml
60
+ lifecycle:
61
+ pre_destroy:
62
+ - remote: myapp --unregister-license
63
+ skippable: true
64
+ ```
65
+
66
+ # Test Kitchen 1.21.0 Release Notes
67
+
68
+ ## Configuration UX improvements
69
+
70
+ Having the kitchen configuration file be hidden has always been a bit odd and so we're moving to using `kitchen.yml` over `.kitchen.yml`.
71
+ This also applies to `kitchen.local.yml` and we've made the change backwards compatible so you're not forced to move over right away. Additionally, we've added support for the environment variables `KITCHEN_YML` and KITCHEN_LOCAL_YML` again preserving compatibility if you're using the `*_YAML` forms.
72
+
73
+ # Test Kitchen 1.20.0 Release Notes
74
+
75
+ ## Multiple paths for data_bags
76
+
77
+ Allows a user to use data_bags from an array of directories
78
+
79
+ ```
80
+ data_bags_path:
81
+ - 'data_bags'
82
+ - 'test/integrations/data_bags'
83
+ ```
84
+
85
+ ## Deprecation Warnings for Configuration Keys
86
+
87
+ ```
88
+ $ kitchen list default-centos-7
89
+ $$$$$$ Deprecated configuration detected:
90
+ require_chef_omnibus
91
+ Run 'kitchen doctor' for details.
92
+ ```
93
+
94
+ ```
95
+ $ kitchen doctor
96
+ $$$$$$ Deprecated configuration detected:
97
+ require_chef_omnibus
98
+ Run 'kitchen doctor' for details.
99
+
100
+ -----> The doctor is in
101
+ **** require_chef_omnibus deprecated
102
+ The 'require_chef_omnibus' attribute with version values will change
103
+ to use the new 'product_version' attribute.
104
+
105
+ Note: 'product_name' must be set in order to use 'product_version'
106
+ until 'product_name' replaces 'require_chef_omnibus' as the default.
107
+
108
+ # New Usage #
109
+ provisioner:
110
+ product_name: <chef or chefdk>
111
+ product_version: 12.0.3
112
+ ```
113
+
114
+ ## SSH via an HTTP Proxy
115
+
116
+ This allows configuring the SSH transport to utilize an HTTP Proxy. The following configuration keys have been added to `transport`:
117
+
118
+ ```
119
+ ssh_http_proxy_user
120
+ ssh_http_proxy_password
121
+ ssh_http_proxy_port
122
+ ssh_http_proxy
123
+ ```
124
+
125
+ # Test Kitchen 1.20.0 Release Notes
126
+
127
+ ## Driver Commands Removed
128
+
129
+ The `kitchen driver` family of commands have been removed. It was not recommended
130
+ to use them and it was judged to be more harm than good to leave them in. If you
131
+ regularly create new drivers and relied on the skeleton generator, check out
132
+ other code skeleton projects like [`chef generate`](https://blog.chef.io/2014/12/09/guest-post-creating-your-own-chef-cookbook-generator/),
133
+ and [Cookiecutter](https://github.com/audreyr/cookiecutter).
134
+
135
+ ## `kitchen converge -D`
136
+
137
+ When you want to get debug logging for your provisioner or verifier, you can now
138
+ use the new `-D` (or `--debug`) command line option for `kitchen converge`,
139
+ `kitchen verify`, and `kitchen test`. Support has been added to the Chef provisioners,
140
+ avoiding the need to use the `log_level: debug` configuration option every time.
141
+
142
+ ## `exec` Driver
143
+
144
+ A new driver named `exec` is included with Test Kitchen which runs all the
145
+ provisioning and verification commands locally, rather than on a VM. This can
146
+ be used for testing on systems where you've already created the VM yourself and
147
+ installed Test Kitchen on it. Note that this is related but different from the
148
+ included `proxy` driver, which also connects to an existing server, but over
149
+ SSH/WinRM rather than running commands locally.
150
+
151
+ ## `shell` Provisioner `command`
152
+
153
+ Previously the included `shell` provisioner allowed running a user-specified bootstrap
154
+ script. This has been extended to allow specifying a `command` option with a
155
+ string to run, rather than managing a script file.
156
+
157
+ ## Faster Busser
158
+
159
+ The `busser` verifier has been improved to be faster on the second (or beyond)
160
+ verification, or in other cases where the required gems are already present.
161
+
162
+ ## `kitchen doctor`
163
+
164
+ A `kitchen doctor` command has been added, modeled on Homebrew's `brew doctor`.
165
+ This currently doesn't do much, but if you are a Kitchen plugin author, consider
166
+ adding more detailed debugging checks and troubleshooting help to your plugin
167
+ via this system.
@@ -37,6 +37,7 @@ require "kitchen/driver/base"
37
37
  require "kitchen/driver/ssh_base"
38
38
  require "kitchen/driver/proxy"
39
39
  require "kitchen/instance"
40
+ require "kitchen/lifecycle_hooks"
40
41
  require "kitchen/transport"
41
42
  require "kitchen/transport/base"
42
43
  require "kitchen/loader/yaml"
@@ -245,6 +245,7 @@ module Kitchen
245
245
  def new_instance(suite, platform, index)
246
246
  Instance.new(
247
247
  driver: new_driver(suite, platform),
248
+ lifecycle_hooks: new_lifecycle_hooks(suite, platform),
248
249
  logger: new_instance_logger(suite, platform, index),
249
250
  suite: suite,
250
251
  platform: platform,
@@ -277,6 +278,18 @@ module Kitchen
277
278
  )
278
279
  end
279
280
 
281
+ # Builds a newly configured LifecycleHooks object, for a given a Suite and
282
+ # Platform.
283
+ #
284
+ # @param suite [Suite,#name] a Suite
285
+ # @param platform [Platform,#name] a Platform
286
+ # @return [LifecycleHooks] a new LifecycleHooks object
287
+ # @api private
288
+ def new_lifecycle_hooks(suite, platform)
289
+ lhdata = data.lifecycle_hooks_data_for(suite.name, platform.name)
290
+ LifecycleHooks.new(lhdata)
291
+ end
292
+
280
293
  # Builds a newly configured Provisioner object, for a given Suite and
281
294
  # Platform.
282
295
  #
@@ -45,6 +45,7 @@ module Kitchen
45
45
  convert_legacy_busser_format!
46
46
  convert_legacy_driver_http_proxy_format!
47
47
  move_chef_data_to_provisioner!
48
+ convert_legacy_pre_create_command!
48
49
  end
49
50
 
50
51
  # Generate a new Hash of configuration data that can be used to construct
@@ -62,6 +63,25 @@ module Kitchen
62
63
  end
63
64
  end
64
65
 
66
+ # Generate a new Hash of configuration data that can be used to construct
67
+ # a new LifecycleHooks object.
68
+ #
69
+ # @param suite [String] a suite name
70
+ # @param platform [String] a platform name
71
+ # @return [Hash] a new configuration Hash that can be used to construct a
72
+ # new LifecycleHooks
73
+ def lifecycle_hooks_data_for(suite, platform)
74
+ merged_data_for(:lifecycle, suite, platform).tap do |lhdata|
75
+ lhdata.each_key do |k|
76
+ combine_arrays!(lhdata, k, :common, :platform, :suite)
77
+ end
78
+ set_kitchen_config_at!(lhdata, :kitchen_root)
79
+ set_kitchen_config_at!(lhdata, :test_base_path)
80
+ set_kitchen_config_at!(lhdata, :log_level)
81
+ set_kitchen_config_at!(lhdata, :debug)
82
+ end
83
+ end
84
+
65
85
  # Returns an Array of platform Hashes.
66
86
  #
67
87
  # @return [Array<Hash>] an Array of Hashes
@@ -591,6 +611,27 @@ module Kitchen
591
611
  end
592
612
  end
593
613
 
614
+ def convert_legacy_pre_create_command!
615
+ convert_legacy_pre_create_command_at!(data)
616
+ data.fetch(:platforms, []).each do |platform|
617
+ convert_legacy_pre_create_command_at!(platform)
618
+ end
619
+ data.fetch(:suites, []).each do |suite|
620
+ convert_legacy_pre_create_command_at!(suite)
621
+ end
622
+ end
623
+
624
+ def convert_legacy_pre_create_command_at!(root)
625
+ ddata = root[:driver] || {}
626
+ if ddata.is_a?(Hash) && ddata.include?(:pre_create_command)
627
+ root[:lifecycle] ||= {}
628
+ root[:lifecycle][:pre_create] ||= []
629
+ root[:lifecycle][:pre_create] = Array(root[:lifecycle][:pre_create])
630
+ root[:lifecycle][:pre_create] << { local: ddata[:pre_create_command] }
631
+ ddata.delete(:pre_create_command)
632
+ end
633
+ end
634
+
594
635
  # Performs a prioritized recursive merge of several source Hashes and
595
636
  # returns a new merged Hash. For these data sub-hash structures, there are
596
637
  # 4 sources for configuration data:
@@ -758,6 +799,12 @@ module Kitchen
758
799
  cdata = data.fetch(key, {})
759
800
  cdata = cdata.nil? ? {} : cdata.dup
760
801
  cdata = { default_key => cdata } if cdata.is_a?(String)
802
+ case key
803
+ when :lifecycle
804
+ cdata.each_key do |k|
805
+ namespace_array!(cdata, k, :common)
806
+ end
807
+ end
761
808
  cdata
762
809
  end
763
810
 
@@ -856,7 +903,14 @@ module Kitchen
856
903
  pdata = platform_data_for(platform).fetch(key, {})
857
904
  pdata = pdata.nil? ? {} : pdata.dup
858
905
  pdata = { default_key => pdata } if pdata.is_a?(String)
859
- namespace_array!(pdata, :run_list, :platform)
906
+ case key
907
+ when :provisioner
908
+ namespace_array!(pdata, :run_list, :platform)
909
+ when :lifecycle
910
+ pdata.each_key do |k|
911
+ namespace_array!(pdata, k, :platform)
912
+ end
913
+ end
860
914
  pdata
861
915
  end
862
916
 
@@ -911,7 +965,14 @@ module Kitchen
911
965
  sdata = suite_data_for(suite).fetch(key, {})
912
966
  sdata = sdata.nil? ? {} : sdata.dup
913
967
  sdata = { default_key => sdata } if sdata.is_a?(String)
914
- namespace_array!(sdata, :run_list, :suite)
968
+ case key
969
+ when :provisioner
970
+ namespace_array!(sdata, :run_list, :suite)
971
+ when :lifecycle
972
+ sdata.each_key do |k|
973
+ namespace_array!(sdata, k, :suite)
974
+ end
975
+ end
915
976
  sdata
916
977
  end
917
978
 
@@ -18,7 +18,6 @@
18
18
 
19
19
  require "rubygems/gem_runner"
20
20
  require "thor/group"
21
- require "kitchen"
22
21
 
23
22
  module Kitchen
24
23
  module Generator
@@ -56,6 +56,9 @@ module Kitchen
56
56
  # lifecycle actions
57
57
  attr_accessor :driver
58
58
 
59
+ # @return [LifecycleHooks] lifecycle hooks manager object
60
+ attr_accessor :lifecycle_hooks
61
+
59
62
  # @return [Provisioner::Base] provisioner object which will the setup
60
63
  # and invocation instructions for configuration management and other
61
64
  # automation tools
@@ -90,20 +93,22 @@ module Kitchen
90
93
  def initialize(options = {})
91
94
  validate_options(options)
92
95
 
93
- @suite = options.fetch(:suite)
94
- @platform = options.fetch(:platform)
95
- @name = self.class.name_for(@suite, @platform)
96
- @driver = options.fetch(:driver)
97
- @provisioner = options.fetch(:provisioner)
98
- @transport = options.fetch(:transport)
99
- @verifier = options.fetch(:verifier)
100
- @logger = options.fetch(:logger) { Kitchen.logger }
101
- @state_file = options.fetch(:state_file)
96
+ @suite = options.fetch(:suite)
97
+ @platform = options.fetch(:platform)
98
+ @name = self.class.name_for(@suite, @platform)
99
+ @driver = options.fetch(:driver)
100
+ @lifecycle_hooks = options.fetch(:lifecycle_hooks)
101
+ @provisioner = options.fetch(:provisioner)
102
+ @transport = options.fetch(:transport)
103
+ @verifier = options.fetch(:verifier)
104
+ @logger = options.fetch(:logger) { Kitchen.logger }
105
+ @state_file = options.fetch(:state_file)
102
106
 
103
107
  setup_driver
104
108
  setup_provisioner
105
109
  setup_transport
106
110
  setup_verifier
111
+ setup_lifecycle_hooks
107
112
  end
108
113
 
109
114
  # Returns a displayable representation of the instance.
@@ -333,6 +338,14 @@ module Kitchen
333
338
  end
334
339
  end
335
340
 
341
+ # Perform any final configuration or preparation needed for the lifecycle hooks
342
+ # object carry out its duties.
343
+ #
344
+ # @api private
345
+ def setup_lifecycle_hooks
346
+ lifecycle_hooks.finalize_config!(self)
347
+ end
348
+
336
349
  # Perform any final configuration or preparation needed for the provisioner
337
350
  # object carry out its duties.
338
351
  #
@@ -365,7 +378,9 @@ module Kitchen
365
378
  def transition_to(desired)
366
379
  result = nil
367
380
  FSM.actions(last_action, desired).each do |transition|
368
- result = send("#{transition}_action")
381
+ @lifecycle_hooks.run_with_hooks(transition, state_file) do
382
+ result = send("#{transition}_action")
383
+ end
369
384
  end
370
385
  result
371
386
  end
@@ -0,0 +1,135 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Noah Kantrowitz <noah@coderanger.net>
4
+ #
5
+ # Copyright (C) 2018, Noah Kantrowitz
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "kitchen/errors"
20
+ require "kitchen/shell_out"
21
+
22
+ module Kitchen
23
+ # A helper object used by {Instance} to coordinate lifecycle hook calls from
24
+ # the `lifecycle:` configuration section.
25
+ #
26
+ # @api internal
27
+ # @since 1.22
28
+ class LifecycleHooks
29
+ include Configurable
30
+ include Logging
31
+ include ShellOut
32
+
33
+ def initialize(config)
34
+ init_config(config)
35
+ end
36
+
37
+ # Run a lifecycle phase with the pre and post hooks.
38
+ #
39
+ # @param phase [String] Lifecycle phase which is being executed.
40
+ # @param state_file [StateFile] Instance state file object.
41
+ # @param block [Proc] Block of code implementing the lifecycle phase.
42
+ # @return [void]
43
+ def run_with_hooks(phase, state_file, &block)
44
+ run(instance, phase, state_file, :pre)
45
+ yield
46
+ run(instance, phase, state_file, :post)
47
+ end
48
+
49
+ private
50
+
51
+ # Execute a specific lifecycle hook.
52
+ #
53
+ # @param instance [Instance] The instance object to run against.
54
+ # @param phase [String] Lifecycle phase which is being executed.
55
+ # @param state_file [StateFile] Instance state file object.
56
+ # @param hook_timing [Symbol] `:pre` or `:post` to indicate which hook to run.
57
+ # @return [void]
58
+ def run(instance, phase, state_file, hook_timing)
59
+ # Yes this has to be a symbol because of how data munger works.
60
+ hook_key = :"#{hook_timing}_#{phase}"
61
+ # No hooks? We're outta here.
62
+ hook_data = Array(config[hook_key])
63
+ return if hook_data.empty?
64
+ hook_data.each do |hook|
65
+ # Coerce the common case of a bare string to be a local command. This
66
+ # is to match the behavior of the old `pre_create_command` semi-hook.
67
+ hook = { local: hook } if hook.is_a?(String)
68
+ if hook.include?(:local)
69
+ # Local command execution on the workstation.
70
+ run_local_hook(instance, state_file, hook)
71
+ elsif hook.include?(:remote)
72
+ # Remote command execution on the test instance.
73
+ run_remote_hook(instance, state_file, hook)
74
+ else
75
+ raise UserError, "Unknown lifecycle hook target #{hook.inspect}"
76
+ end
77
+ end
78
+ end
79
+
80
+ # Execute a specific local command hook.
81
+ #
82
+ # @param instance [Instance] The instance object to run against.
83
+ # @param state_file [StateFile] Instance state file object.
84
+ # @param hook [Hash] Hook configration to use.
85
+ # @return [void]
86
+ def run_local_hook(instance, state_file, hook)
87
+ cmd = hook.delete(:local)
88
+ state = state_file.read
89
+ # Set up some environment variables with instance info.
90
+ environment = {
91
+ "KITCHEN_INSTANCE_NAME" => instance.name,
92
+ "KITCHEN_SUITE_NAME" => instance.suite.name,
93
+ "KITCHEN_PLATFORM_NAME" => instance.platform.name,
94
+ "KITCHEN_INSTANCE_HOSTNAME" => state[:hostname].to_s,
95
+ }
96
+ # If the user specified env vars too, fix them up because symbol keys
97
+ # make mixlib-shellout sad.
98
+ if hook[:environment]
99
+ hook[:environment].each do |k, v|
100
+ environment[k.to_s] = v.to_s
101
+ end
102
+ end
103
+ # Default the cwd to the kitchen root and resolve a relative input cwd against that.
104
+ cwd = if hook[:cwd]
105
+ File.expand_path(hook[:cwd], config[:kitchen_root])
106
+ else
107
+ config[:kitchen_root]
108
+ end
109
+ # Build the options for mixlib-shellout.
110
+ opts = {}.merge(hook).merge(cwd: cwd, environment: environment)
111
+ run_command(cmd, opts)
112
+ end
113
+
114
+ # Execute a specific remote command hook.
115
+ #
116
+ # @param instance [Instance] The instance object to run against.
117
+ # @param state_file [StateFile] Instance state file object.
118
+ # @param hook [Hash] Hook configration to use.
119
+ # @return [void]
120
+ def run_remote_hook(instance, state_file, hook)
121
+ # Check if we're in a state that makes sense to even try.
122
+ unless instance.last_action
123
+ if hook[:skippable]
124
+ # Just not even trying.
125
+ return
126
+ else
127
+ raise UserError, "Cannot use remote lifecycle hooks during phases when the instance is not available"
128
+ end
129
+ end
130
+ cmd = hook.delete(:remote)
131
+ conn = instance.transport.connection(state_file.read)
132
+ conn.execute(cmd)
133
+ end
134
+ end
135
+ end
@@ -234,14 +234,10 @@ module Kitchen
234
234
  case opts[:type]
235
235
  when :directory
236
236
  FileUtils.mkdir_p(dest)
237
- if src.kind_of?(Array)
238
- src.each { |x| FileUtils.cp_r(Util.list_directory(x), dest) }
239
- else
240
- FileUtils.cp_r(Util.list_directory(src), dest)
241
- end
237
+ Array(src).each { |dir| FileUtils.cp_r(Util.list_directory(dir), dest) }
242
238
  when :file
243
239
  FileUtils.mkdir_p(File.dirname(dest))
244
- FileUtils.cp_r(src, dest)
240
+ Array(src).each { |file| FileUtils.cp_r(file, dest) }
245
241
  end
246
242
  end
247
243
 
@@ -517,17 +517,13 @@ module Kitchen
517
517
  # net-ssh to ~> 4.2, this will prevent InSpec from being used in
518
518
  # Chef v12 because of it pinning to a v3 of net-ssh.
519
519
  #
520
- def self.verify_host_key_option
520
+ def verify_host_key_option
521
521
  current_net_ssh = Net::SSH::Version::CURRENT
522
522
  new_option_version = Net::SSH::Version[4, 2, 0]
523
523
 
524
524
  current_net_ssh >= new_option_version ? :verify_host_key : :paranoid
525
525
  end
526
526
 
527
- def verify_host_key_option
528
- self.class.verify_host_key_option
529
- end
530
-
531
527
  # Creates a new SSH Connection instance and save it for potential future
532
528
  # reuse.
533
529
  #
@@ -17,5 +17,5 @@
17
17
  # limitations under the License.
18
18
 
19
19
  module Kitchen
20
- VERSION = "1.22.1".freeze
20
+ VERSION = "1.23.0".freeze
21
21
  end
@@ -295,6 +295,7 @@ describe Kitchen::Config do
295
295
  let(:munger) do
296
296
  stub(
297
297
  driver_data_for: { "junk" => true },
298
+ lifecycle_hooks_data_for: { "junk" => true },
298
299
  provisioner_data_for: { "junk" => true },
299
300
  transport_data_for: { "junk" => true },
300
301
  verifier_data_for: { "junk" => true }
@@ -309,6 +310,7 @@ describe Kitchen::Config do
309
310
  Kitchen::Verifier.stubs(:for_plugin).returns("verifier")
310
311
  Kitchen::Logger.stubs(:new).returns("logger")
311
312
  Kitchen::StateFile.stubs(:new).returns("state_file")
313
+ Kitchen::LifecycleHooks.stubs(:new).returns("lifecycle_hooks")
312
314
 
313
315
  Kitchen::DataMunger.stubs(:new).returns(munger)
314
316
  config.stubs(:platforms).returns(platforms)
@@ -384,6 +386,7 @@ describe Kitchen::Config do
384
386
  driver: "driver",
385
387
  logger: "logger",
386
388
  suite: suites.first,
389
+ lifecycle_hooks: "lifecycle_hooks",
387
390
  platform: platforms.first,
388
391
  provisioner: "provisioner",
389
392
  transport: "transport",
@@ -2689,5 +2689,112 @@ module Kitchen # rubocop:disable Metrics/ModuleLength
2689
2689
  end
2690
2690
  end
2691
2691
  end
2692
+
2693
+ describe "lifecycle_hooks stuff" do
2694
+ it "handles a single global hook" do
2695
+ DataMunger.new(
2696
+ {
2697
+ lifecycle: {
2698
+ pre_create: "echo foo"
2699
+ },
2700
+ platforms: [{ name: "plat" }],
2701
+ suites: [{ name: "sweet" }],
2702
+ },
2703
+ {}
2704
+ ).lifecycle_hooks_data_for("sweet", "plat").must_equal(
2705
+ pre_create: ["echo foo"]
2706
+ )
2707
+ end
2708
+
2709
+ it "handles multiple global hooks" do
2710
+ DataMunger.new(
2711
+ {
2712
+ lifecycle: {
2713
+ pre_create: "echo foo",
2714
+ post_converge: ["echo bar", { local: "echo baz" }],
2715
+ pre_verify: [{ remote: "echo other" }],
2716
+ },
2717
+ platforms: [{ name: "plat" }],
2718
+ suites: [{ name: "sweet" }],
2719
+ },
2720
+ {}
2721
+ ).lifecycle_hooks_data_for("sweet", "plat").must_equal(
2722
+ pre_create: ["echo foo"],
2723
+ post_converge: ["echo bar", { local: "echo baz" }],
2724
+ pre_verify: [{ remote: "echo other" }]
2725
+ )
2726
+ end
2727
+
2728
+ it "handles hooks in platforms and suites" do
2729
+ DataMunger.new(
2730
+ {
2731
+ lifecycle: {
2732
+ pre_create: "echo foo",
2733
+ post_create: "echo post"
2734
+ },
2735
+ platforms: [{
2736
+ name: "plat",
2737
+ lifecycle: {
2738
+ pre_create: "echo bar"
2739
+ }
2740
+ }],
2741
+ suites: [{
2742
+ name: "sweet",
2743
+ lifecycle: {
2744
+ pre_create: "echo baz"
2745
+ }
2746
+ }],
2747
+ },
2748
+ {}
2749
+ ).lifecycle_hooks_data_for("sweet", "plat").must_equal(
2750
+ pre_create: ["echo foo", "echo bar", "echo baz"],
2751
+ post_create: ["echo post"]
2752
+ )
2753
+ end
2754
+
2755
+ it "munges a global legacy pre_create_command" do
2756
+ DataMunger.new(
2757
+ {
2758
+ driver: {
2759
+ pre_create_command: "echo bar"
2760
+ },
2761
+ lifecycle: {
2762
+ pre_create: "echo foo"
2763
+ },
2764
+ platforms: [{ name: "plat" }],
2765
+ suites: [{ name: "sweet" }],
2766
+ },
2767
+ {}
2768
+ ).lifecycle_hooks_data_for("sweet", "plat").must_equal(
2769
+ pre_create: ["echo foo", { local: "echo bar" }]
2770
+ )
2771
+ end
2772
+
2773
+ it "munges a platform/suite legacy pre_create_commands" do
2774
+ DataMunger.new(
2775
+ {
2776
+ lifecycle: {
2777
+ pre_create: "echo foo"
2778
+ },
2779
+ platforms: [{
2780
+ name: "plat",
2781
+ driver: {
2782
+ pre_create_command: "echo bar"
2783
+ }
2784
+ }],
2785
+ suites: [{
2786
+ name: "sweet",
2787
+ driver: {
2788
+ pre_create_command: "echo baz"
2789
+ }
2790
+ }],
2791
+ },
2792
+ {}
2793
+ ).lifecycle_hooks_data_for("sweet", "plat").must_equal(
2794
+ pre_create: ["echo foo", { local: "echo bar" }, { local: "echo baz" }]
2795
+ )
2796
+ end
2797
+
2798
+ end
2692
2799
  end
2693
2800
  end
@@ -95,17 +95,18 @@ class LegacyDriver < Kitchen::Driver::SSHBase
95
95
  end
96
96
 
97
97
  describe Kitchen::Instance do
98
- let(:driver) { Kitchen::Driver::Dummy.new({}) }
99
- let(:logger_io) { StringIO.new }
100
- let(:logger) { Kitchen::Logger.new(logdev: logger_io) }
101
- let(:instance) { Kitchen::Instance.new(opts) }
102
- let(:provisioner) { Kitchen::Provisioner::Dummy.new({}) }
103
- let(:state_file) { DummyStateFile.new }
104
- let(:transport) { Kitchen::Transport::Dummy.new({}) }
105
- let(:verifier) { Kitchen::Verifier::Dummy.new({}) }
98
+ let(:driver) { Kitchen::Driver::Dummy.new({}) }
99
+ let(:logger_io) { StringIO.new }
100
+ let(:logger) { Kitchen::Logger.new(logdev: logger_io) }
101
+ let(:instance) { Kitchen::Instance.new(opts) }
102
+ let(:lifecycle_hooks) { Kitchen::LifecycleHooks.new({}) }
103
+ let(:provisioner) { Kitchen::Provisioner::Dummy.new({}) }
104
+ let(:state_file) { DummyStateFile.new }
105
+ let(:transport) { Kitchen::Transport::Dummy.new({}) }
106
+ let(:verifier) { Kitchen::Verifier::Dummy.new({}) }
106
107
 
107
108
  let(:opts) do
108
- { suite: suite, platform: platform, driver: driver,
109
+ { suite: suite, platform: platform, driver: driver, lifecycle_hooks: lifecycle_hooks,
109
110
  provisioner: provisioner, verifier: verifier,
110
111
  logger: logger, state_file: state_file, transport: transport }
111
112
  end
@@ -477,6 +478,13 @@ describe Kitchen::Instance do
477
478
  logger_io.string
478
479
  .must_match regex_for("Finished creating #{instance.to_str}")
479
480
  end
481
+
482
+ it "calls lifecycle hooks" do
483
+ lifecycle_hooks.expects(:run).with(instance, :create, state_file, :pre)
484
+ lifecycle_hooks.expects(:run).with(instance, :create, state_file, :post)
485
+
486
+ instance.create
487
+ end
480
488
  end
481
489
 
482
490
  describe "with last_action of create" do
@@ -525,6 +533,15 @@ describe Kitchen::Instance do
525
533
  logger_io.string
526
534
  .must_match regex_for("Finished converging #{instance.to_str}")
527
535
  end
536
+
537
+ it "calls lifecycle hooks" do
538
+ lifecycle_hooks.expects(:run).with(instance, :create, state_file, :pre)
539
+ lifecycle_hooks.expects(:run).with(instance, :create, state_file, :post)
540
+ lifecycle_hooks.expects(:run).with(instance, :converge, state_file, :pre)
541
+ lifecycle_hooks.expects(:run).with(instance, :converge, state_file, :post)
542
+
543
+ instance.converge
544
+ end
528
545
  end
529
546
 
530
547
  describe "with last action of create" do
@@ -542,6 +559,13 @@ describe Kitchen::Instance do
542
559
 
543
560
  state_file.read[:last_action].must_equal "converge"
544
561
  end
562
+
563
+ it "calls lifecycle hooks" do
564
+ lifecycle_hooks.expects(:run).with(instance, :converge, state_file, :pre)
565
+ lifecycle_hooks.expects(:run).with(instance, :converge, state_file, :post)
566
+
567
+ instance.converge
568
+ end
545
569
  end
546
570
 
547
571
  describe "with last action of converge" do
@@ -0,0 +1,171 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Noah Kantrowitz <noah@coderanger.net>
4
+ #
5
+ # Copyright (C) 2018, Noah Kantrowitz
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative "../spec_helper"
20
+
21
+ require "kitchen/errors"
22
+ require "kitchen/lifecycle_hooks"
23
+
24
+ describe Kitchen::LifecycleHooks do
25
+ let(:suite) { mock("suite").tap { |i| i.stubs(name: "default") } }
26
+ let(:platform) { mock("platform").tap { |i| i.stubs(name: "toaster-1.0") } }
27
+ let(:state_file) { mock("state_file").tap { |s| s.stubs(read: { hostname: "localhost" }) } }
28
+ let(:connection) { mock("connection") }
29
+ let(:transport) { mock("transport").tap { |t| t.stubs(:connection).with({ hostname: "localhost" }).returns(connection) } }
30
+ let(:last_action) { :create }
31
+ let(:instance) { mock("instance").tap { |i| i.stubs(name: "default-toaster-10", transport: transport, last_action: last_action, suite: suite, platform: platform) } }
32
+ let(:config) { { kitchen_root: "/kitchen" } }
33
+ let(:lifecycle_hooks) { Kitchen::LifecycleHooks.new(config).tap { |lh| lh.finalize_config!(instance) } }
34
+
35
+ def run_lifecycle_hooks
36
+ lifecycle_hooks.run_with_hooks(:create, state_file) {}
37
+ end
38
+
39
+ # Pull this out because it's used in a bunch of tests.
40
+ STANDARD_LOCAL_OPTIONS = {
41
+ cwd: "/kitchen",
42
+ environment: {
43
+ "KITCHEN_INSTANCE_NAME" => "default-toaster-10",
44
+ "KITCHEN_SUITE_NAME" => "default",
45
+ "KITCHEN_PLATFORM_NAME" => "toaster-1.0",
46
+ "KITCHEN_INSTANCE_HOSTNAME" => "localhost",
47
+ }
48
+ }
49
+
50
+ it "runs a single local command" do
51
+ config.update(post_create: ["echo foo"])
52
+ lifecycle_hooks.expects(:run_command).with("echo foo", STANDARD_LOCAL_OPTIONS)
53
+ run_lifecycle_hooks
54
+ end
55
+
56
+ it "runs multiple local commands" do
57
+ config.update(post_create: ["echo foo", { local: "echo bar" }])
58
+ lifecycle_hooks.expects(:run_command).with("echo foo", STANDARD_LOCAL_OPTIONS)
59
+ lifecycle_hooks.expects(:run_command).with("echo bar", STANDARD_LOCAL_OPTIONS)
60
+ run_lifecycle_hooks
61
+ end
62
+
63
+ it "runs multiple local hooks" do
64
+ config.update(pre_create: ["echo foo"], post_create: ["echo bar"])
65
+ lifecycle_hooks.expects(:run_command).with("echo foo", STANDARD_LOCAL_OPTIONS)
66
+ lifecycle_hooks.expects(:run_command).with("echo bar", STANDARD_LOCAL_OPTIONS)
67
+ run_lifecycle_hooks
68
+ end
69
+
70
+ it "runs a local command with a user option" do
71
+ config.update(post_create: [{ local: "echo foo", user: "bar" }])
72
+ lifecycle_hooks.expects(:run_command).with("echo foo", {
73
+ cwd: "/kitchen",
74
+ user: "bar",
75
+ environment: {
76
+ "KITCHEN_INSTANCE_NAME" => "default-toaster-10",
77
+ "KITCHEN_SUITE_NAME" => "default",
78
+ "KITCHEN_PLATFORM_NAME" => "toaster-1.0",
79
+ "KITCHEN_INSTANCE_HOSTNAME" => "localhost",
80
+ }
81
+ })
82
+ run_lifecycle_hooks
83
+ end
84
+
85
+ it "runs a local command with environment options" do
86
+ config.update(post_create: [{ local: "echo foo", environment: { FOO: "one", BAR: "two" } }])
87
+ lifecycle_hooks.expects(:run_command).with("echo foo", {
88
+ cwd: "/kitchen",
89
+ environment: {
90
+ "FOO" => "one",
91
+ "BAR" => "two",
92
+ "KITCHEN_INSTANCE_NAME" => "default-toaster-10",
93
+ "KITCHEN_SUITE_NAME" => "default",
94
+ "KITCHEN_PLATFORM_NAME" => "toaster-1.0",
95
+ "KITCHEN_INSTANCE_HOSTNAME" => "localhost",
96
+ }
97
+ })
98
+ run_lifecycle_hooks
99
+ end
100
+
101
+ it "runs a local command with a relative cwd option" do
102
+ config.update(post_create: [{ local: "echo foo", cwd: "test" }])
103
+ lifecycle_hooks.expects(:run_command).with("echo foo", {
104
+ cwd: os_safe_root_path("/kitchen/test"),
105
+ environment: {
106
+ "KITCHEN_INSTANCE_NAME" => "default-toaster-10",
107
+ "KITCHEN_SUITE_NAME" => "default",
108
+ "KITCHEN_PLATFORM_NAME" => "toaster-1.0",
109
+ "KITCHEN_INSTANCE_HOSTNAME" => "localhost",
110
+ }
111
+ })
112
+ run_lifecycle_hooks
113
+ end
114
+
115
+ it "runs a local command with an absolute cwd option" do
116
+ config.update(post_create: [{ local: "echo foo", cwd: "/test" }])
117
+ lifecycle_hooks.expects(:run_command).with("echo foo", {
118
+ cwd: os_safe_root_path("/test"),
119
+ environment: {
120
+ "KITCHEN_INSTANCE_NAME" => "default-toaster-10",
121
+ "KITCHEN_SUITE_NAME" => "default",
122
+ "KITCHEN_PLATFORM_NAME" => "toaster-1.0",
123
+ "KITCHEN_INSTANCE_HOSTNAME" => "localhost",
124
+ }
125
+ })
126
+ run_lifecycle_hooks
127
+ end
128
+
129
+ it "runs a single remote command" do
130
+ config.update(post_create: [{ remote: "echo foo" }])
131
+ lifecycle_hooks.expects(:run_command).never
132
+ connection.expects(:execute).with("echo foo")
133
+ run_lifecycle_hooks
134
+ end
135
+
136
+ it "rejects unknown hook targets" do
137
+ config.update(post_create: [{ banana: "echo foo" }])
138
+ lifecycle_hooks.expects(:run_command).never
139
+ proc { run_lifecycle_hooks }.must_raise Kitchen::UserError
140
+ end
141
+
142
+ it "runs mixed local and remote commands" do
143
+ config.update(post_create: ["echo foo", { local: "echo bar" }, { remote: "echo baz" }])
144
+ lifecycle_hooks.expects(:run_command).with("echo foo", STANDARD_LOCAL_OPTIONS)
145
+ lifecycle_hooks.expects(:run_command).with("echo bar", STANDARD_LOCAL_OPTIONS)
146
+ connection.expects(:execute).with("echo baz")
147
+ run_lifecycle_hooks
148
+ end
149
+
150
+ describe "with no last_action" do
151
+ let(:last_action) { nil }
152
+
153
+ it "runs local commands" do
154
+ config.update(post_create: [{ local: "echo foo" }])
155
+ lifecycle_hooks.expects(:run_command).with("echo foo", STANDARD_LOCAL_OPTIONS)
156
+ run_lifecycle_hooks
157
+ end
158
+
159
+ it "fails on remote commands" do
160
+ config.update(post_create: [{ remote: "echo foo" }])
161
+ lifecycle_hooks.expects(:run_command).never
162
+ proc { run_lifecycle_hooks }.must_raise Kitchen::UserError
163
+ end
164
+
165
+ it "ignores skippable remote commands" do
166
+ config.update(post_create: [{ remote: "echo foo", skippable: true }])
167
+ lifecycle_hooks.expects(:run_command).never
168
+ run_lifecycle_hooks
169
+ end
170
+ end
171
+ end
@@ -188,9 +188,8 @@ describe Kitchen::Transport::Ssh do
188
188
  end
189
189
 
190
190
  it "sets the :verify_host_key flag to false" do
191
- verify_host_key_option = Kitchen::Transport::Ssh.verify_host_key_option
192
191
  klass.expects(:new).with do |hash|
193
- hash[verify_host_key_option] == false
192
+ hash[:verify_host_key] == false
194
193
  end
195
194
 
196
195
  make_connection
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.22.1
4
+ version: 1.23.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: 2018-08-03 00:00:00.000000000 Z
11
+ date: 2018-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mixlib-shellout
@@ -336,6 +336,7 @@ files:
336
336
  - LICENSE
337
337
  - MAINTAINERS.md
338
338
  - README.md
339
+ - RELEASE_NOTES.md
339
340
  - Rakefile
340
341
  - appveyor.yml
341
342
  - bin/kitchen
@@ -384,6 +385,7 @@ files:
384
385
  - lib/kitchen/generator/init.rb
385
386
  - lib/kitchen/instance.rb
386
387
  - lib/kitchen/lazy_hash.rb
388
+ - lib/kitchen/lifecycle_hooks.rb
387
389
  - lib/kitchen/loader/yaml.rb
388
390
  - lib/kitchen/logger.rb
389
391
  - lib/kitchen/logging.rb
@@ -439,6 +441,7 @@ files:
439
441
  - spec/kitchen/errors_spec.rb
440
442
  - spec/kitchen/instance_spec.rb
441
443
  - spec/kitchen/lazy_hash_spec.rb
444
+ - spec/kitchen/lifecycle_hooks_spec.rb
442
445
  - spec/kitchen/loader/yaml_spec.rb
443
446
  - spec/kitchen/logger_spec.rb
444
447
  - spec/kitchen/logging_spec.rb
@@ -525,7 +528,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
525
528
  version: '0'
526
529
  requirements: []
527
530
  rubyforge_project:
528
- rubygems_version: 2.7.7
531
+ rubygems_version: 2.7.6
529
532
  signing_key:
530
533
  specification_version: 4
531
534
  summary: Test Kitchen is an integration tool for developing and testing infrastructure
@@ -563,6 +566,7 @@ test_files:
563
566
  - spec/kitchen/errors_spec.rb
564
567
  - spec/kitchen/instance_spec.rb
565
568
  - spec/kitchen/lazy_hash_spec.rb
569
+ - spec/kitchen/lifecycle_hooks_spec.rb
566
570
  - spec/kitchen/loader/yaml_spec.rb
567
571
  - spec/kitchen/logger_spec.rb
568
572
  - spec/kitchen/logging_spec.rb