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 +4 -4
- data/CHANGELOG.md +4 -110
- data/RELEASE_NOTES.md +167 -0
- data/lib/kitchen.rb +1 -0
- data/lib/kitchen/config.rb +13 -0
- data/lib/kitchen/data_munger.rb +63 -2
- data/lib/kitchen/generator/init.rb +0 -1
- data/lib/kitchen/instance.rb +25 -10
- data/lib/kitchen/lifecycle_hooks.rb +135 -0
- data/lib/kitchen/provisioner/chef/common_sandbox.rb +2 -6
- data/lib/kitchen/transport/ssh.rb +1 -5
- data/lib/kitchen/version.rb +1 -1
- data/spec/kitchen/config_spec.rb +3 -0
- data/spec/kitchen/data_munger_spec.rb +107 -0
- data/spec/kitchen/instance_spec.rb +33 -9
- data/spec/kitchen/lifecycle_hooks_spec.rb +171 -0
- data/spec/kitchen/transport/ssh_spec.rb +1 -2
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 885f8d3b08f1f4c860cd55c370cefc6f2b35e87dc2607ce9f6d60c39788bfd86
|
4
|
+
data.tar.gz: 352335dacee5a0ed1dbe5067d658bd2cd85bac1d0ec39c49a1283871c8604953
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc58bb9f82520bf0e616620a874d0452ac5ed0889cc7e28f0eb1aacfbb8e609436b65ff6f3f687044c3f896fc4846697cf96c78b8adee54816647c9a2f787373
|
7
|
+
data.tar.gz: '0909b191b4dc6c965ccf6e5afb59b67cff485035b049de9bea218c89f31753312ab970ca68217dc4c7f4eaa6b33ea814f67ce5a9cda3e31a0efa0a66bc2a3dd1'
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [v1.
|
4
|
-
[Full Changelog](https://github.com/test-kitchen/test-kitchen/compare/v1.22.0...v1.
|
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
|
-
-
|
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
|
|
data/RELEASE_NOTES.md
ADDED
@@ -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.
|
data/lib/kitchen.rb
CHANGED
@@ -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"
|
data/lib/kitchen/config.rb
CHANGED
@@ -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
|
#
|
data/lib/kitchen/data_munger.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
|
data/lib/kitchen/instance.rb
CHANGED
@@ -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
|
94
|
-
@platform
|
95
|
-
@name
|
96
|
-
@driver
|
97
|
-
@
|
98
|
-
@
|
99
|
-
@
|
100
|
-
@
|
101
|
-
@
|
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
|
-
|
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
|
-
|
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(
|
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
|
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
|
#
|
data/lib/kitchen/version.rb
CHANGED
data/spec/kitchen/config_spec.rb
CHANGED
@@ -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)
|
99
|
-
let(:logger_io)
|
100
|
-
let(:logger)
|
101
|
-
let(:instance)
|
102
|
-
let(:
|
103
|
-
let(:
|
104
|
-
let(:
|
105
|
-
let(:
|
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[
|
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.
|
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-
|
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.
|
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
|