test-kitchen 1.6.0 → 1.7.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.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +8 -7
  3. data/.github/ISSUE_TEMPLATE.md +56 -0
  4. data/.gitignore +28 -27
  5. data/.kitchen.ci.yml +23 -0
  6. data/.kitchen.proxy.yml +27 -0
  7. data/.rubocop.yml +3 -3
  8. data/.travis.yml +70 -53
  9. data/.yardopts +3 -3
  10. data/Berksfile +3 -0
  11. data/CHANGELOG.md +1083 -1051
  12. data/CONTRIBUTING.md +14 -14
  13. data/Gemfile +19 -14
  14. data/Gemfile.proxy_tests +4 -5
  15. data/Guardfile +42 -42
  16. data/LICENSE +15 -15
  17. data/MAINTAINERS.md +23 -24
  18. data/README.md +135 -135
  19. data/Rakefile +61 -76
  20. data/appveyor.yml +44 -34
  21. data/features/kitchen_action_commands.feature +164 -164
  22. data/features/kitchen_command.feature +16 -16
  23. data/features/kitchen_console_command.feature +34 -34
  24. data/features/kitchen_defaults.feature +38 -38
  25. data/features/kitchen_diagnose_command.feature +96 -96
  26. data/features/kitchen_driver_create_command.feature +64 -64
  27. data/features/kitchen_driver_discover_command.feature +25 -25
  28. data/features/kitchen_help_command.feature +16 -16
  29. data/features/kitchen_init_command.feature +274 -274
  30. data/features/kitchen_list_command.feature +104 -104
  31. data/features/kitchen_login_command.feature +62 -62
  32. data/features/kitchen_sink_command.feature +30 -30
  33. data/features/kitchen_test_command.feature +88 -88
  34. data/features/step_definitions/gem_steps.rb +36 -36
  35. data/features/step_definitions/git_steps.rb +5 -5
  36. data/features/step_definitions/output_steps.rb +5 -5
  37. data/features/support/env.rb +75 -75
  38. data/lib/kitchen.rb +150 -150
  39. data/lib/kitchen/base64_stream.rb +55 -55
  40. data/lib/kitchen/cli.rb +419 -419
  41. data/lib/kitchen/collection.rb +55 -55
  42. data/lib/kitchen/color.rb +65 -65
  43. data/lib/kitchen/command.rb +185 -185
  44. data/lib/kitchen/command/action.rb +45 -45
  45. data/lib/kitchen/command/console.rb +58 -58
  46. data/lib/kitchen/command/diagnose.rb +92 -92
  47. data/lib/kitchen/command/driver_discover.rb +105 -105
  48. data/lib/kitchen/command/exec.rb +41 -41
  49. data/lib/kitchen/command/list.rb +119 -119
  50. data/lib/kitchen/command/login.rb +43 -43
  51. data/lib/kitchen/command/sink.rb +54 -54
  52. data/lib/kitchen/command/test.rb +51 -51
  53. data/lib/kitchen/config.rb +322 -322
  54. data/lib/kitchen/configurable.rb +529 -529
  55. data/lib/kitchen/data_munger.rb +959 -960
  56. data/lib/kitchen/diagnostic.rb +141 -141
  57. data/lib/kitchen/driver.rb +56 -56
  58. data/lib/kitchen/driver/base.rb +134 -134
  59. data/lib/kitchen/driver/dummy.rb +108 -108
  60. data/lib/kitchen/driver/proxy.rb +72 -72
  61. data/lib/kitchen/driver/ssh_base.rb +357 -357
  62. data/lib/kitchen/errors.rb +229 -229
  63. data/lib/kitchen/generator/driver_create.rb +177 -177
  64. data/lib/kitchen/generator/init.rb +296 -296
  65. data/lib/kitchen/instance.rb +662 -662
  66. data/lib/kitchen/lazy_hash.rb +142 -142
  67. data/lib/kitchen/loader/yaml.rb +349 -349
  68. data/lib/kitchen/logger.rb +423 -423
  69. data/lib/kitchen/logging.rb +56 -56
  70. data/lib/kitchen/login_command.rb +52 -52
  71. data/lib/kitchen/metadata_chopper.rb +52 -52
  72. data/lib/kitchen/platform.rb +67 -67
  73. data/lib/kitchen/provisioner.rb +54 -54
  74. data/lib/kitchen/provisioner/base.rb +236 -236
  75. data/lib/kitchen/provisioner/chef/berkshelf.rb +114 -114
  76. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -322
  77. data/lib/kitchen/provisioner/chef/librarian.rb +112 -112
  78. data/lib/kitchen/provisioner/chef_apply.rb +124 -125
  79. data/lib/kitchen/provisioner/chef_base.rb +341 -294
  80. data/lib/kitchen/provisioner/chef_solo.rb +88 -89
  81. data/lib/kitchen/provisioner/chef_zero.rb +245 -245
  82. data/lib/kitchen/provisioner/dummy.rb +79 -79
  83. data/lib/kitchen/provisioner/shell.rb +138 -138
  84. data/lib/kitchen/rake_tasks.rb +63 -63
  85. data/lib/kitchen/shell_out.rb +93 -93
  86. data/lib/kitchen/ssh.rb +276 -276
  87. data/lib/kitchen/state_file.rb +120 -120
  88. data/lib/kitchen/suite.rb +51 -51
  89. data/lib/kitchen/thor_tasks.rb +66 -66
  90. data/lib/kitchen/transport.rb +54 -54
  91. data/lib/kitchen/transport/base.rb +176 -176
  92. data/lib/kitchen/transport/dummy.rb +79 -79
  93. data/lib/kitchen/transport/ssh.rb +364 -364
  94. data/lib/kitchen/transport/winrm.rb +486 -486
  95. data/lib/kitchen/util.rb +147 -147
  96. data/lib/kitchen/verifier.rb +55 -55
  97. data/lib/kitchen/verifier/base.rb +235 -235
  98. data/lib/kitchen/verifier/busser.rb +277 -277
  99. data/lib/kitchen/verifier/dummy.rb +79 -79
  100. data/lib/kitchen/verifier/shell.rb +101 -101
  101. data/lib/kitchen/version.rb +21 -21
  102. data/lib/vendor/hash_recursive_merge.rb +82 -82
  103. data/spec/kitchen/base64_stream_spec.rb +77 -77
  104. data/spec/kitchen/cli_spec.rb +56 -56
  105. data/spec/kitchen/collection_spec.rb +80 -80
  106. data/spec/kitchen/color_spec.rb +54 -54
  107. data/spec/kitchen/config_spec.rb +408 -408
  108. data/spec/kitchen/configurable_spec.rb +1095 -1062
  109. data/spec/kitchen/data_munger_spec.rb +2694 -2383
  110. data/spec/kitchen/diagnostic_spec.rb +129 -129
  111. data/spec/kitchen/driver/base_spec.rb +121 -121
  112. data/spec/kitchen/driver/dummy_spec.rb +199 -199
  113. data/spec/kitchen/driver/proxy_spec.rb +138 -138
  114. data/spec/kitchen/driver/ssh_base_spec.rb +1115 -1115
  115. data/spec/kitchen/driver_spec.rb +112 -112
  116. data/spec/kitchen/errors_spec.rb +309 -309
  117. data/spec/kitchen/instance_spec.rb +1419 -1419
  118. data/spec/kitchen/lazy_hash_spec.rb +117 -117
  119. data/spec/kitchen/loader/yaml_spec.rb +774 -774
  120. data/spec/kitchen/logger_spec.rb +429 -429
  121. data/spec/kitchen/logging_spec.rb +59 -59
  122. data/spec/kitchen/login_command_spec.rb +68 -68
  123. data/spec/kitchen/metadata_chopper_spec.rb +82 -82
  124. data/spec/kitchen/platform_spec.rb +89 -89
  125. data/spec/kitchen/provisioner/base_spec.rb +386 -386
  126. data/spec/kitchen/provisioner/chef_apply_spec.rb +136 -136
  127. data/spec/kitchen/provisioner/chef_base_spec.rb +1161 -1067
  128. data/spec/kitchen/provisioner/chef_solo_spec.rb +557 -557
  129. data/spec/kitchen/provisioner/chef_zero_spec.rb +1001 -1001
  130. data/spec/kitchen/provisioner/dummy_spec.rb +99 -99
  131. data/spec/kitchen/provisioner/shell_spec.rb +566 -566
  132. data/spec/kitchen/provisioner_spec.rb +107 -107
  133. data/spec/kitchen/shell_out_spec.rb +150 -150
  134. data/spec/kitchen/ssh_spec.rb +693 -693
  135. data/spec/kitchen/state_file_spec.rb +129 -129
  136. data/spec/kitchen/suite_spec.rb +62 -62
  137. data/spec/kitchen/transport/base_spec.rb +89 -89
  138. data/spec/kitchen/transport/ssh_spec.rb +1255 -1255
  139. data/spec/kitchen/transport/winrm_spec.rb +1143 -1143
  140. data/spec/kitchen/transport_spec.rb +112 -112
  141. data/spec/kitchen/util_spec.rb +165 -165
  142. data/spec/kitchen/verifier/base_spec.rb +362 -362
  143. data/spec/kitchen/verifier/busser_spec.rb +610 -610
  144. data/spec/kitchen/verifier/dummy_spec.rb +99 -99
  145. data/spec/kitchen/verifier/shell_spec.rb +160 -158
  146. data/spec/kitchen/verifier_spec.rb +120 -120
  147. data/spec/kitchen_spec.rb +114 -114
  148. data/spec/spec_helper.rb +85 -85
  149. data/spec/support/powershell_max_size_spec.rb +40 -40
  150. data/support/busser_install_command.ps1 +14 -14
  151. data/support/busser_install_command.sh +14 -14
  152. data/support/chef-client-zero.rb +77 -77
  153. data/support/chef_base_init_command.ps1 +18 -18
  154. data/support/chef_base_init_command.sh +2 -2
  155. data/support/chef_base_install_command.ps1 +85 -85
  156. data/support/chef_base_install_command.sh +229 -229
  157. data/support/chef_zero_prepare_command_legacy.ps1 +9 -9
  158. data/support/chef_zero_prepare_command_legacy.sh +10 -10
  159. data/support/download_helpers.sh +109 -109
  160. data/support/dummy-validation.pem +27 -27
  161. data/templates/driver/CHANGELOG.md.erb +3 -3
  162. data/templates/driver/Gemfile.erb +3 -3
  163. data/templates/driver/README.md.erb +64 -64
  164. data/templates/driver/Rakefile.erb +21 -21
  165. data/templates/driver/driver.rb.erb +23 -23
  166. data/templates/driver/gemspec.erb +29 -29
  167. data/templates/driver/gitignore.erb +17 -17
  168. data/templates/driver/license_apachev2.erb +15 -15
  169. data/templates/driver/license_lgplv3.erb +16 -16
  170. data/templates/driver/license_mit.erb +22 -22
  171. data/templates/driver/license_reserved.erb +5 -5
  172. data/templates/driver/tailor.erb +4 -4
  173. data/templates/driver/travis.yml.erb +11 -11
  174. data/templates/driver/version.rb.erb +12 -12
  175. data/templates/init/chefignore.erb +1 -1
  176. data/templates/init/kitchen.yml.erb +18 -18
  177. data/test-kitchen.gemspec +62 -62
  178. data/test/integration/default/default_spec.rb +3 -0
  179. data/testing_windows.md +37 -37
  180. metadata +23 -11
@@ -1,55 +1,55 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
- #
5
- # Copyright (C) 2014, Fletcher Nichol
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
- module Kitchen
20
-
21
- # Base64 encoder/decoder that operates on IO objects so as to minimize
22
- # memory allocations on large payloads.
23
- #
24
- # @author Fletcher Nichol <fnichol@nichol.ca>
25
- module Base64Stream
26
-
27
- # Encodes an input stream into a Base64 output stream. The input and ouput
28
- # objects must be opened IO resources. In other words, opening and closing
29
- # the resources are not the responsibilty of this method.
30
- #
31
- # @param io_in [#read] input stream
32
- # @param io_out [#write] output stream
33
- def self.strict_encode(io_in, io_out)
34
- buffer = ""
35
- while io_in.read(3 * 1000, buffer)
36
- io_out.write([buffer].pack("m0"))
37
- end
38
- buffer = nil # rubocop:disable Lint/UselessAssignment
39
- end
40
-
41
- # Decodes a Base64 input stream into an output stream. The input and ouput
42
- # objects must be opened IO resources. In other words, opening and closing
43
- # the resources are not the responsibilty of this method.
44
- #
45
- # @param io_in [#read] input stream
46
- # @param io_out [#write] output stream
47
- def self.strict_decode(io_in, io_out)
48
- buffer = ""
49
- while io_in.read(3 * 1000, buffer)
50
- io_out.write(buffer.unpack("m0").first)
51
- end
52
- buffer = nil # rubocop:disable Lint/UselessAssignment
53
- end
54
- end
55
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2014, Fletcher Nichol
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
+ module Kitchen
20
+
21
+ # Base64 encoder/decoder that operates on IO objects so as to minimize
22
+ # memory allocations on large payloads.
23
+ #
24
+ # @author Fletcher Nichol <fnichol@nichol.ca>
25
+ module Base64Stream
26
+
27
+ # Encodes an input stream into a Base64 output stream. The input and ouput
28
+ # objects must be opened IO resources. In other words, opening and closing
29
+ # the resources are not the responsibilty of this method.
30
+ #
31
+ # @param io_in [#read] input stream
32
+ # @param io_out [#write] output stream
33
+ def self.strict_encode(io_in, io_out)
34
+ buffer = ""
35
+ while io_in.read(3 * 1000, buffer)
36
+ io_out.write([buffer].pack("m0"))
37
+ end
38
+ buffer = nil # rubocop:disable Lint/UselessAssignment
39
+ end
40
+
41
+ # Decodes a Base64 input stream into an output stream. The input and ouput
42
+ # objects must be opened IO resources. In other words, opening and closing
43
+ # the resources are not the responsibilty of this method.
44
+ #
45
+ # @param io_in [#read] input stream
46
+ # @param io_out [#write] output stream
47
+ def self.strict_decode(io_in, io_out)
48
+ buffer = ""
49
+ while io_in.read(3 * 1000, buffer)
50
+ io_out.write(buffer.unpack("m0").first)
51
+ end
52
+ buffer = nil # rubocop:disable Lint/UselessAssignment
53
+ end
54
+ end
55
+ end
data/lib/kitchen/cli.rb CHANGED
@@ -1,419 +1,419 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
- #
5
- # Copyright (C) 2012, Fletcher Nichol
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 "thor"
20
-
21
- require "kitchen"
22
- require "kitchen/generator/driver_create"
23
- require "kitchen/generator/init"
24
-
25
- module Kitchen
26
-
27
- # The command line runner for Kitchen.
28
- #
29
- # @author Fletcher Nichol <fnichol@nichol.ca>
30
- class CLI < Thor
31
-
32
- # Common module to load and invoke a CLI-implementation agnostic command.
33
- module PerformCommand
34
-
35
- # Perform a CLI subcommand.
36
- #
37
- # @param task [String] action to take, usually corresponding to the
38
- # subcommand name
39
- # @param command [String] command class to create and invoke]
40
- # @param args [Array] remainder arguments from processed ARGV
41
- # (default: `nil`)
42
- # @param additional_options [Hash] additional configuration needed to
43
- # set up the command class (default: `{}`)
44
- def perform(task, command, args = nil, additional_options = {})
45
- require "kitchen/command/#{command}"
46
-
47
- command_options = {
48
- :action => task,
49
- :help => -> { help(task) },
50
- :config => @config,
51
- :shell => shell
52
- }.merge(additional_options)
53
-
54
- str_const = Thor::Util.camel_case(command)
55
- klass = ::Kitchen::Command.const_get(str_const)
56
- klass.new(args, options, command_options).call
57
- end
58
- end
59
-
60
- include Logging
61
- include PerformCommand
62
-
63
- # The maximum number of concurrent instances that can run--which is a bit
64
- # high
65
- MAX_CONCURRENCY = 9999
66
-
67
- attr_reader :config
68
-
69
- # Constructs a new instance.
70
- def initialize(*args)
71
- super
72
- $stdout.sync = true
73
- @loader = Kitchen::Loader::YAML.new(
74
- :project_config => ENV["KITCHEN_YAML"],
75
- :local_config => ENV["KITCHEN_LOCAL_YAML"],
76
- :global_config => ENV["KITCHEN_GLOBAL_YAML"]
77
- )
78
- @config = Kitchen::Config.new(
79
- :loader => @loader
80
- )
81
- @config.log_level = Kitchen.env_log unless Kitchen.env_log.nil?
82
- @config.log_overwrite = Kitchen.env_log_overwrite unless Kitchen.env_log_overwrite.nil?
83
- end
84
-
85
- # Sets the logging method_options
86
- # @api private
87
- def self.log_options
88
- method_option :log_level,
89
- :aliases => "-l",
90
- :desc => "Set the log level (debug, info, warn, error, fatal)"
91
- method_option :log_overwrite,
92
- :desc => "Set to false to prevent log overwriting each time Test Kitchen runs",
93
- :type => :boolean
94
- end
95
-
96
- # Sets the test_base_path method_options
97
- # @api private
98
- def self.test_base_path
99
- method_option :test_base_path,
100
- :aliases => "-t",
101
- :desc => "Set the base path of the tests"
102
- end
103
-
104
- desc "list [INSTANCE|REGEXP|all]", "Lists one or more instances"
105
- method_option :bare,
106
- :aliases => "-b",
107
- :type => :boolean,
108
- :desc => "List the name of each instance only, one per line"
109
- method_option :debug,
110
- :aliases => "-d",
111
- :type => :boolean,
112
- :desc => "[Deprecated] Please use `kitchen diagnose'"
113
- log_options
114
- def list(*args)
115
- update_config!
116
- perform("list", "list", args)
117
- end
118
-
119
- desc "diagnose [INSTANCE|REGEXP|all]", "Show computed diagnostic configuration"
120
- method_option :loader,
121
- :type => :boolean,
122
- :desc => "Include data loader diagnostics"
123
- method_option :plugins,
124
- :type => :boolean,
125
- :desc => "Include plugin diagnostics"
126
- method_option :instances,
127
- :type => :boolean,
128
- :default => true,
129
- :desc => "Include instances diagnostics"
130
- method_option :all,
131
- :type => :boolean,
132
- :desc => "Include all diagnostics"
133
- log_options
134
- def diagnose(*args)
135
- update_config!
136
- perform("diagnose", "diagnose", args, :loader => @loader)
137
- end
138
-
139
- {
140
- :create => "Change instance state to create. " \
141
- "Start one or more instances",
142
- :converge => "Change instance state to converge. " \
143
- "Use a provisioner to configure one or more instances",
144
- :setup => "Change instance state to setup. " \
145
- "Prepare to run automated tests. " \
146
- "Install busser and related gems on one or more instances",
147
- :verify => "Change instance state to verify. " \
148
- "Run automated tests on one or more instances",
149
- :destroy => "Change instance state to destroy. " \
150
- "Delete all information for one or more instances"
151
- }.each do |action, short_desc|
152
- desc(
153
- "#{action} [INSTANCE|REGEXP|all]",
154
- short_desc
155
- )
156
- long_desc <<-DESC
157
- The instance states are in order: destroy, create, converge, setup, verify, destroy.
158
- Change one or more instances from the current state to the #{action} state. Actions for all
159
- intermediate states will be executed. See http://kitchen.ci for further explanation.
160
- DESC
161
- method_option :concurrency,
162
- :aliases => "-c",
163
- :type => :numeric,
164
- :lazy_default => MAX_CONCURRENCY,
165
- :desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
166
- Run a #{action} against all matching instances concurrently. Only N
167
- instances will run at the same time if a number is given.
168
- DESC
169
- method_option :parallel,
170
- :aliases => "-p",
171
- :type => :boolean,
172
- :desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
173
- [Future DEPRECATION, use --concurrency]
174
- Run a #{action} against all matching instances concurrently.
175
- DESC
176
- test_base_path
177
- log_options
178
- define_method(action) do |*args|
179
- update_config!
180
- perform(action, "action", args)
181
- end
182
- end
183
-
184
- desc "test [INSTANCE|REGEXP|all]",
185
- "Test (destroy, create, converge, setup, verify and destroy) one or more instances"
186
- long_desc <<-DESC
187
- The instance states are in order: destroy, create, converge, setup, verify, destroy.
188
- Test changes the state of one or more instances to destroyed, then executes
189
- the actions for each state up to destroy. At any sign of failure, executing the
190
- actions stops and the instance is left in the last successful execution state.
191
-
192
- There are 3 post-verify modes for instance cleanup, triggered with
193
- the `--destroy' flag:
194
-
195
- * passing: instances passing verify will be destroyed afterwards.\n
196
- * always: instances will always be destroyed afterwards.\n
197
- * never: instances will never be destroyed afterwards.
198
- DESC
199
- method_option :concurrency,
200
- :aliases => "-c",
201
- :type => :numeric,
202
- :lazy_default => MAX_CONCURRENCY,
203
- :desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
204
- Run a test against all matching instances concurrently. Only N
205
- instances will run at the same time if a number is given.
206
- DESC
207
- method_option :parallel,
208
- :aliases => "-p",
209
- :type => :boolean,
210
- :desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
211
- [Future DEPRECATION, use --concurrency]
212
- Run a test against all matching instances concurrently.
213
- DESC
214
- method_option :destroy,
215
- :aliases => "-d",
216
- :default => "passing",
217
- :desc => "Destroy strategy to use after testing (passing, always, never)."
218
- method_option :auto_init,
219
- :type => :boolean,
220
- :default => false,
221
- :desc => "Invoke init command if .kitchen.yml is missing"
222
- test_base_path
223
- log_options
224
- def test(*args)
225
- update_config!
226
- ensure_initialized
227
- perform("test", "test", args)
228
- end
229
-
230
- desc "login INSTANCE|REGEXP", "Log in to one instance"
231
- log_options
232
- def login(*args)
233
- update_config!
234
- perform("login", "login", args)
235
- end
236
-
237
- desc "exec INSTANCE|REGEXP -c REMOTE_COMMAND",
238
- "Execute command on one or more instance"
239
- method_option :command,
240
- :aliases => "-c",
241
- :desc => "execute via ssh"
242
- log_options
243
- def exec(*args)
244
- update_config!
245
- perform("exec", "exec", args)
246
- end
247
-
248
- desc "version", "Print Kitchen's version information"
249
- def version
250
- puts "Test Kitchen version #{Kitchen::VERSION}"
251
- end
252
- map %w[-v --version] => :version
253
-
254
- desc "sink", "Show the Kitchen sink!", :hide => true
255
- def sink
256
- perform("sink", "sink")
257
- end
258
-
259
- desc "console", "Kitchen Console!"
260
- def console
261
- perform("console", "console")
262
- end
263
-
264
- register Kitchen::Generator::Init, "init",
265
- "init", "Adds some configuration to your cookbook so Kitchen can rock"
266
- long_desc <<-D, :for => "init"
267
- Init will add Test Kitchen support to an existing project for
268
- convergence integration testing. A default .kitchen.yml file (which is
269
- intended to be customized) is created in the project's root directory
270
- and one or more gems will be added to the project's Gemfile.
271
- D
272
- tasks["init"].options = Kitchen::Generator::Init.class_options
273
-
274
- # Thor class for kitchen driver commands.
275
- #
276
- # @author Fletcher Nichol <fnichol@nichol.ca>
277
- class Driver < Thor
278
-
279
- include PerformCommand
280
-
281
- register Kitchen::Generator::DriverCreate, "create",
282
- "create [NAME]", "Create a new Kitchen Driver gem project"
283
- long_desc <<-D, :for => "create"
284
- Create will generate a project scaffold for a brand new Test Kitchen
285
- Driver RubyGem. For example:
286
-
287
- > kitchen driver create foobar
288
-
289
- will create a project scaffold for a RubyGem called `kitchen-foobar'.
290
- D
291
- tasks["create"].options = Kitchen::Generator::DriverCreate.class_options
292
-
293
- desc "discover", "Discover Test Kitchen drivers published on RubyGems"
294
- long_desc <<-D
295
- Discover will perform a search aginst the RubyGems service for any
296
- published gems of the form: "kitchen-*". Note that it it cannot be
297
- guarenteed that every result is a driver, but chances are good most
298
- relevant drivers will be returned.
299
- D
300
- method_option :chef_config_path,
301
- :default => nil,
302
- :desc => "Path to chef config file containing proxy configuration to use"
303
- def discover
304
- perform("discover", "driver_discover", args)
305
- end
306
-
307
- # @return [String] basename
308
- def self.basename
309
- super + " driver"
310
- end
311
- end
312
-
313
- register Kitchen::CLI::Driver, "driver",
314
- "driver", "Driver subcommands"
315
-
316
- no_tasks do
317
- def invoke_task(command, *args)
318
- if command.name == "help" && args.first.first == "driver"
319
- Kitchen::CLI::Driver.task_help(shell, args.first.last)
320
- else
321
- super
322
- end
323
- end
324
- alias_method :invoke_command, :invoke_task
325
- end
326
-
327
- private
328
-
329
- # Ensure the any failing commands exit non-zero.
330
- #
331
- # @return [true] you die always on failure
332
- # @api private
333
- def self.exit_on_failure?
334
- true
335
- end
336
-
337
- # @return [Logger] the common logger
338
- # @api private
339
- def logger
340
- Kitchen.logger
341
- end
342
-
343
- # Update and finalize options for logging, concurrency, and other concerns.
344
- #
345
- # @api private
346
- def update_config!
347
- @config.log_level = log_level if log_level
348
-
349
- unless options[:log_overwrite].nil?
350
- @config.log_overwrite = options[:log_overwrite]
351
- end
352
-
353
- if options[:test_base_path]
354
- # ensure we have an absolute path
355
- @config.test_base_path = File.absolute_path(options[:test_base_path])
356
- end
357
-
358
- # Now that we have required configs, lets create our file logger
359
- Kitchen.logger = Kitchen.default_file_logger(
360
- log_level,
361
- options[:log_overwrite]
362
- )
363
-
364
- update_parallel!
365
- end
366
-
367
- # Validate the log level from the config / CLI options, defaulting
368
- # to :info if the supplied level is empty or invalid
369
- #
370
- # @api private
371
- def log_level
372
- return unless options[:log_level]
373
- return @log_level if @log_level
374
-
375
- level = options[:log_level].downcase.to_sym
376
- unless valid_log_level?(level)
377
- level = :info
378
- banner "WARNING - invalid log level specified: " \
379
- "\"#{options[:log_level]}\" - reverting to :info log level."
380
- end
381
-
382
- @log_level = level
383
- end
384
-
385
- # Check to whether a provided log level is valid
386
- #
387
- # @api private
388
- def valid_log_level?(level)
389
- !Util.to_logger_level(level).nil?
390
- end
391
-
392
- # Set parallel concurrency options for Thor
393
- #
394
- # @api private
395
- def update_parallel!
396
- if options[:parallel]
397
- # warn here in a future release when option is used
398
- @options = Thor::CoreExt::HashWithIndifferentAccess.new(options.to_hash)
399
- if options[:parallel] && !options[:concurrency]
400
- options[:concurrency] = MAX_CONCURRENCY
401
- end
402
- options.delete(:parallel)
403
- options.freeze
404
- end
405
- end
406
-
407
- # If auto_init option is active, invoke the init generator.
408
- #
409
- # @api private
410
- def ensure_initialized
411
- yaml = ENV["KITCHEN_YAML"] || ".kitchen.yml"
412
-
413
- if options[:auto_init] && !File.exist?(yaml)
414
- banner "Invoking init as '#{yaml}' file is missing"
415
- invoke "init"
416
- end
417
- end
418
- end
419
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2012, Fletcher Nichol
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 "thor"
20
+
21
+ require "kitchen"
22
+ require "kitchen/generator/driver_create"
23
+ require "kitchen/generator/init"
24
+
25
+ module Kitchen
26
+
27
+ # The command line runner for Kitchen.
28
+ #
29
+ # @author Fletcher Nichol <fnichol@nichol.ca>
30
+ class CLI < Thor
31
+
32
+ # Common module to load and invoke a CLI-implementation agnostic command.
33
+ module PerformCommand
34
+
35
+ # Perform a CLI subcommand.
36
+ #
37
+ # @param task [String] action to take, usually corresponding to the
38
+ # subcommand name
39
+ # @param command [String] command class to create and invoke]
40
+ # @param args [Array] remainder arguments from processed ARGV
41
+ # (default: `nil`)
42
+ # @param additional_options [Hash] additional configuration needed to
43
+ # set up the command class (default: `{}`)
44
+ def perform(task, command, args = nil, additional_options = {})
45
+ require "kitchen/command/#{command}"
46
+
47
+ command_options = {
48
+ :action => task,
49
+ :help => -> { help(task) },
50
+ :config => @config,
51
+ :shell => shell
52
+ }.merge(additional_options)
53
+
54
+ str_const = Thor::Util.camel_case(command)
55
+ klass = ::Kitchen::Command.const_get(str_const)
56
+ klass.new(args, options, command_options).call
57
+ end
58
+ end
59
+
60
+ include Logging
61
+ include PerformCommand
62
+
63
+ # The maximum number of concurrent instances that can run--which is a bit
64
+ # high
65
+ MAX_CONCURRENCY = 9999
66
+
67
+ attr_reader :config
68
+
69
+ # Constructs a new instance.
70
+ def initialize(*args)
71
+ super
72
+ $stdout.sync = true
73
+ @loader = Kitchen::Loader::YAML.new(
74
+ :project_config => ENV["KITCHEN_YAML"],
75
+ :local_config => ENV["KITCHEN_LOCAL_YAML"],
76
+ :global_config => ENV["KITCHEN_GLOBAL_YAML"]
77
+ )
78
+ @config = Kitchen::Config.new(
79
+ :loader => @loader
80
+ )
81
+ @config.log_level = Kitchen.env_log unless Kitchen.env_log.nil?
82
+ @config.log_overwrite = Kitchen.env_log_overwrite unless Kitchen.env_log_overwrite.nil?
83
+ end
84
+
85
+ # Sets the logging method_options
86
+ # @api private
87
+ def self.log_options
88
+ method_option :log_level,
89
+ :aliases => "-l",
90
+ :desc => "Set the log level (debug, info, warn, error, fatal)"
91
+ method_option :log_overwrite,
92
+ :desc => "Set to false to prevent log overwriting each time Test Kitchen runs",
93
+ :type => :boolean
94
+ end
95
+
96
+ # Sets the test_base_path method_options
97
+ # @api private
98
+ def self.test_base_path
99
+ method_option :test_base_path,
100
+ :aliases => "-t",
101
+ :desc => "Set the base path of the tests"
102
+ end
103
+
104
+ desc "list [INSTANCE|REGEXP|all]", "Lists one or more instances"
105
+ method_option :bare,
106
+ :aliases => "-b",
107
+ :type => :boolean,
108
+ :desc => "List the name of each instance only, one per line"
109
+ method_option :debug,
110
+ :aliases => "-d",
111
+ :type => :boolean,
112
+ :desc => "[Deprecated] Please use `kitchen diagnose'"
113
+ log_options
114
+ def list(*args)
115
+ update_config!
116
+ perform("list", "list", args)
117
+ end
118
+
119
+ desc "diagnose [INSTANCE|REGEXP|all]", "Show computed diagnostic configuration"
120
+ method_option :loader,
121
+ :type => :boolean,
122
+ :desc => "Include data loader diagnostics"
123
+ method_option :plugins,
124
+ :type => :boolean,
125
+ :desc => "Include plugin diagnostics"
126
+ method_option :instances,
127
+ :type => :boolean,
128
+ :default => true,
129
+ :desc => "Include instances diagnostics"
130
+ method_option :all,
131
+ :type => :boolean,
132
+ :desc => "Include all diagnostics"
133
+ log_options
134
+ def diagnose(*args)
135
+ update_config!
136
+ perform("diagnose", "diagnose", args, :loader => @loader)
137
+ end
138
+
139
+ {
140
+ :create => "Change instance state to create. " \
141
+ "Start one or more instances",
142
+ :converge => "Change instance state to converge. " \
143
+ "Use a provisioner to configure one or more instances",
144
+ :setup => "Change instance state to setup. " \
145
+ "Prepare to run automated tests. " \
146
+ "Install busser and related gems on one or more instances",
147
+ :verify => "Change instance state to verify. " \
148
+ "Run automated tests on one or more instances",
149
+ :destroy => "Change instance state to destroy. " \
150
+ "Delete all information for one or more instances"
151
+ }.each do |action, short_desc|
152
+ desc(
153
+ "#{action} [INSTANCE|REGEXP|all]",
154
+ short_desc
155
+ )
156
+ long_desc <<-DESC
157
+ The instance states are in order: destroy, create, converge, setup, verify, destroy.
158
+ Change one or more instances from the current state to the #{action} state. Actions for all
159
+ intermediate states will be executed. See http://kitchen.ci for further explanation.
160
+ DESC
161
+ method_option :concurrency,
162
+ :aliases => "-c",
163
+ :type => :numeric,
164
+ :lazy_default => MAX_CONCURRENCY,
165
+ :desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
166
+ Run a #{action} against all matching instances concurrently. Only N
167
+ instances will run at the same time if a number is given.
168
+ DESC
169
+ method_option :parallel,
170
+ :aliases => "-p",
171
+ :type => :boolean,
172
+ :desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
173
+ [Future DEPRECATION, use --concurrency]
174
+ Run a #{action} against all matching instances concurrently.
175
+ DESC
176
+ test_base_path
177
+ log_options
178
+ define_method(action) do |*args|
179
+ update_config!
180
+ perform(action, "action", args)
181
+ end
182
+ end
183
+
184
+ desc "test [INSTANCE|REGEXP|all]",
185
+ "Test (destroy, create, converge, setup, verify and destroy) one or more instances"
186
+ long_desc <<-DESC
187
+ The instance states are in order: destroy, create, converge, setup, verify, destroy.
188
+ Test changes the state of one or more instances to destroyed, then executes
189
+ the actions for each state up to destroy. At any sign of failure, executing the
190
+ actions stops and the instance is left in the last successful execution state.
191
+
192
+ There are 3 post-verify modes for instance cleanup, triggered with
193
+ the `--destroy' flag:
194
+
195
+ * passing: instances passing verify will be destroyed afterwards.\n
196
+ * always: instances will always be destroyed afterwards.\n
197
+ * never: instances will never be destroyed afterwards.
198
+ DESC
199
+ method_option :concurrency,
200
+ :aliases => "-c",
201
+ :type => :numeric,
202
+ :lazy_default => MAX_CONCURRENCY,
203
+ :desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
204
+ Run a test against all matching instances concurrently. Only N
205
+ instances will run at the same time if a number is given.
206
+ DESC
207
+ method_option :parallel,
208
+ :aliases => "-p",
209
+ :type => :boolean,
210
+ :desc => <<-DESC.gsub(/^\s+/, "").gsub(/\n/, " ")
211
+ [Future DEPRECATION, use --concurrency]
212
+ Run a test against all matching instances concurrently.
213
+ DESC
214
+ method_option :destroy,
215
+ :aliases => "-d",
216
+ :default => "passing",
217
+ :desc => "Destroy strategy to use after testing (passing, always, never)."
218
+ method_option :auto_init,
219
+ :type => :boolean,
220
+ :default => false,
221
+ :desc => "Invoke init command if .kitchen.yml is missing"
222
+ test_base_path
223
+ log_options
224
+ def test(*args)
225
+ update_config!
226
+ ensure_initialized
227
+ perform("test", "test", args)
228
+ end
229
+
230
+ desc "login INSTANCE|REGEXP", "Log in to one instance"
231
+ log_options
232
+ def login(*args)
233
+ update_config!
234
+ perform("login", "login", args)
235
+ end
236
+
237
+ desc "exec INSTANCE|REGEXP -c REMOTE_COMMAND",
238
+ "Execute command on one or more instance"
239
+ method_option :command,
240
+ :aliases => "-c",
241
+ :desc => "execute via ssh"
242
+ log_options
243
+ def exec(*args)
244
+ update_config!
245
+ perform("exec", "exec", args)
246
+ end
247
+
248
+ desc "version", "Print Kitchen's version information"
249
+ def version
250
+ puts "Test Kitchen version #{Kitchen::VERSION}"
251
+ end
252
+ map %w[-v --version] => :version
253
+
254
+ desc "sink", "Show the Kitchen sink!", :hide => true
255
+ def sink
256
+ perform("sink", "sink")
257
+ end
258
+
259
+ desc "console", "Kitchen Console!"
260
+ def console
261
+ perform("console", "console")
262
+ end
263
+
264
+ register Kitchen::Generator::Init, "init",
265
+ "init", "Adds some configuration to your cookbook so Kitchen can rock"
266
+ long_desc <<-D, :for => "init"
267
+ Init will add Test Kitchen support to an existing project for
268
+ convergence integration testing. A default .kitchen.yml file (which is
269
+ intended to be customized) is created in the project's root directory
270
+ and one or more gems will be added to the project's Gemfile.
271
+ D
272
+ tasks["init"].options = Kitchen::Generator::Init.class_options
273
+
274
+ # Thor class for kitchen driver commands.
275
+ #
276
+ # @author Fletcher Nichol <fnichol@nichol.ca>
277
+ class Driver < Thor
278
+
279
+ include PerformCommand
280
+
281
+ register Kitchen::Generator::DriverCreate, "create",
282
+ "create [NAME]", "Create a new Kitchen Driver gem project"
283
+ long_desc <<-D, :for => "create"
284
+ Create will generate a project scaffold for a brand new Test Kitchen
285
+ Driver RubyGem. For example:
286
+
287
+ > kitchen driver create foobar
288
+
289
+ will create a project scaffold for a RubyGem called `kitchen-foobar'.
290
+ D
291
+ tasks["create"].options = Kitchen::Generator::DriverCreate.class_options
292
+
293
+ desc "discover", "Discover Test Kitchen drivers published on RubyGems"
294
+ long_desc <<-D
295
+ Discover will perform a search aginst the RubyGems service for any
296
+ published gems of the form: "kitchen-*". Note that it it cannot be
297
+ guarenteed that every result is a driver, but chances are good most
298
+ relevant drivers will be returned.
299
+ D
300
+ method_option :chef_config_path,
301
+ :default => nil,
302
+ :desc => "Path to chef config file containing proxy configuration to use"
303
+ def discover
304
+ perform("discover", "driver_discover", args)
305
+ end
306
+
307
+ # @return [String] basename
308
+ def self.basename
309
+ super + " driver"
310
+ end
311
+ end
312
+
313
+ register Kitchen::CLI::Driver, "driver",
314
+ "driver", "Driver subcommands"
315
+
316
+ no_tasks do
317
+ def invoke_task(command, *args)
318
+ if command.name == "help" && args.first.first == "driver"
319
+ Kitchen::CLI::Driver.task_help(shell, args.first.last)
320
+ else
321
+ super
322
+ end
323
+ end
324
+ alias_method :invoke_command, :invoke_task
325
+ end
326
+
327
+ private
328
+
329
+ # Ensure the any failing commands exit non-zero.
330
+ #
331
+ # @return [true] you die always on failure
332
+ # @api private
333
+ def self.exit_on_failure?
334
+ true
335
+ end
336
+
337
+ # @return [Logger] the common logger
338
+ # @api private
339
+ def logger
340
+ Kitchen.logger
341
+ end
342
+
343
+ # Update and finalize options for logging, concurrency, and other concerns.
344
+ #
345
+ # @api private
346
+ def update_config!
347
+ @config.log_level = log_level if log_level
348
+
349
+ unless options[:log_overwrite].nil?
350
+ @config.log_overwrite = options[:log_overwrite]
351
+ end
352
+
353
+ if options[:test_base_path]
354
+ # ensure we have an absolute path
355
+ @config.test_base_path = File.absolute_path(options[:test_base_path])
356
+ end
357
+
358
+ # Now that we have required configs, lets create our file logger
359
+ Kitchen.logger = Kitchen.default_file_logger(
360
+ log_level,
361
+ options[:log_overwrite]
362
+ )
363
+
364
+ update_parallel!
365
+ end
366
+
367
+ # Validate the log level from the config / CLI options, defaulting
368
+ # to :info if the supplied level is empty or invalid
369
+ #
370
+ # @api private
371
+ def log_level
372
+ return unless options[:log_level]
373
+ return @log_level if @log_level
374
+
375
+ level = options[:log_level].downcase.to_sym
376
+ unless valid_log_level?(level)
377
+ level = :info
378
+ banner "WARNING - invalid log level specified: " \
379
+ "\"#{options[:log_level]}\" - reverting to :info log level."
380
+ end
381
+
382
+ @log_level = level
383
+ end
384
+
385
+ # Check to whether a provided log level is valid
386
+ #
387
+ # @api private
388
+ def valid_log_level?(level)
389
+ !Util.to_logger_level(level).nil?
390
+ end
391
+
392
+ # Set parallel concurrency options for Thor
393
+ #
394
+ # @api private
395
+ def update_parallel!
396
+ if options[:parallel]
397
+ # warn here in a future release when option is used
398
+ @options = Thor::CoreExt::HashWithIndifferentAccess.new(options.to_hash)
399
+ if options[:parallel] && !options[:concurrency]
400
+ options[:concurrency] = MAX_CONCURRENCY
401
+ end
402
+ options.delete(:parallel)
403
+ options.freeze
404
+ end
405
+ end
406
+
407
+ # If auto_init option is active, invoke the init generator.
408
+ #
409
+ # @api private
410
+ def ensure_initialized
411
+ yaml = ENV["KITCHEN_YAML"] || ".kitchen.yml"
412
+
413
+ if options[:auto_init] && !File.exist?(yaml)
414
+ banner "Invoking init as '#{yaml}' file is missing"
415
+ invoke "init"
416
+ end
417
+ end
418
+ end
419
+ end