test-kitchen 1.22.1 → 1.23.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
  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