test-kitchen 0.7.0 → 1.0.0.alpha.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/.gitignore +20 -0
  2. data/.travis.yml +11 -0
  3. data/.yardopts +3 -0
  4. data/Gemfile +13 -0
  5. data/Guardfile +11 -0
  6. data/LICENSE +15 -0
  7. data/README.md +131 -0
  8. data/Rakefile +69 -0
  9. data/bin/kitchen +9 -4
  10. data/features/cli.feature +17 -0
  11. data/features/cli_init.feature +156 -0
  12. data/features/support/env.rb +14 -0
  13. data/lib/kitchen/busser.rb +166 -0
  14. data/lib/kitchen/chef_data_uploader.rb +156 -0
  15. data/lib/kitchen/cli.rb +540 -0
  16. data/lib/kitchen/collection.rb +55 -0
  17. data/lib/kitchen/color.rb +46 -0
  18. data/lib/kitchen/config.rb +223 -0
  19. data/lib/kitchen/driver/base.rb +180 -0
  20. data/lib/kitchen/driver/dummy.rb +81 -0
  21. data/lib/kitchen/driver/ssh_base.rb +192 -0
  22. data/lib/kitchen/driver.rb +42 -0
  23. data/lib/kitchen/errors.rb +52 -0
  24. data/lib/kitchen/instance.rb +327 -0
  25. data/lib/kitchen/instance_actor.rb +42 -0
  26. data/lib/kitchen/loader/yaml.rb +105 -0
  27. data/lib/kitchen/logger.rb +145 -0
  28. data/{cookbooks/test-kitchen/libraries/helpers.rb → lib/kitchen/logging.rb} +13 -9
  29. data/lib/kitchen/manager.rb +45 -0
  30. data/lib/kitchen/metadata_chopper.rb +52 -0
  31. data/lib/kitchen/platform.rb +61 -0
  32. data/lib/kitchen/rake_tasks.rb +59 -0
  33. data/lib/kitchen/shell_out.rb +65 -0
  34. data/lib/kitchen/state_file.rb +88 -0
  35. data/lib/kitchen/suite.rb +76 -0
  36. data/lib/kitchen/thor_tasks.rb +62 -0
  37. data/lib/kitchen/util.rb +79 -0
  38. data/{cookbooks/test-kitchen/recipes/erlang.rb → lib/kitchen/version.rb} +9 -6
  39. data/lib/kitchen.rb +98 -0
  40. data/lib/vendor/hash_recursive_merge.rb +74 -0
  41. data/spec/kitchen/collection_spec.rb +80 -0
  42. data/spec/kitchen/color_spec.rb +54 -0
  43. data/spec/kitchen/config_spec.rb +201 -0
  44. data/spec/kitchen/driver/dummy_spec.rb +191 -0
  45. data/spec/kitchen/instance_spec.rb +162 -0
  46. data/spec/kitchen/loader/yaml_spec.rb +243 -0
  47. data/spec/kitchen/platform_spec.rb +48 -0
  48. data/spec/kitchen/state_file_spec.rb +122 -0
  49. data/spec/kitchen/suite_spec.rb +64 -0
  50. data/spec/spec_helper.rb +47 -0
  51. data/templates/plugin/driver.rb.erb +23 -0
  52. data/templates/plugin/license_apachev2.erb +15 -0
  53. data/templates/plugin/license_gplv2.erb +18 -0
  54. data/templates/plugin/license_gplv3.erb +16 -0
  55. data/templates/plugin/license_mit.erb +22 -0
  56. data/templates/plugin/license_reserved.erb +5 -0
  57. data/templates/plugin/version.rb.erb +12 -0
  58. data/test-kitchen.gemspec +44 -0
  59. metadata +290 -82
  60. data/config/Cheffile +0 -47
  61. data/config/Kitchenfile +0 -39
  62. data/config/Vagrantfile +0 -114
  63. data/cookbooks/test-kitchen/attributes/default.rb +0 -25
  64. data/cookbooks/test-kitchen/metadata.rb +0 -27
  65. data/cookbooks/test-kitchen/recipes/chef.rb +0 -19
  66. data/cookbooks/test-kitchen/recipes/compat.rb +0 -39
  67. data/cookbooks/test-kitchen/recipes/default.rb +0 -51
  68. data/cookbooks/test-kitchen/recipes/ruby.rb +0 -29
  69. data/lib/test-kitchen/cli/destroy.rb +0 -36
  70. data/lib/test-kitchen/cli/init.rb +0 -37
  71. data/lib/test-kitchen/cli/platform_list.rb +0 -37
  72. data/lib/test-kitchen/cli/project_info.rb +0 -44
  73. data/lib/test-kitchen/cli/ssh.rb +0 -36
  74. data/lib/test-kitchen/cli/status.rb +0 -36
  75. data/lib/test-kitchen/cli/test.rb +0 -68
  76. data/lib/test-kitchen/cli.rb +0 -282
  77. data/lib/test-kitchen/dsl.rb +0 -63
  78. data/lib/test-kitchen/environment.rb +0 -166
  79. data/lib/test-kitchen/platform.rb +0 -79
  80. data/lib/test-kitchen/project/base.rb +0 -159
  81. data/lib/test-kitchen/project/cookbook.rb +0 -97
  82. data/lib/test-kitchen/project/cookbook_copy.rb +0 -58
  83. data/lib/test-kitchen/project/ruby.rb +0 -37
  84. data/lib/test-kitchen/project/supported_platforms.rb +0 -75
  85. data/lib/test-kitchen/project.rb +0 -23
  86. data/lib/test-kitchen/runner/base.rb +0 -154
  87. data/lib/test-kitchen/runner/openstack/dsl.rb +0 -39
  88. data/lib/test-kitchen/runner/openstack/environment.rb +0 -141
  89. data/lib/test-kitchen/runner/openstack.rb +0 -147
  90. data/lib/test-kitchen/runner/vagrant.rb +0 -95
  91. data/lib/test-kitchen/runner.rb +0 -21
  92. data/lib/test-kitchen/scaffold.rb +0 -88
  93. data/lib/test-kitchen/ui.rb +0 -73
  94. data/lib/test-kitchen/version.rb +0 -21
  95. data/lib/test-kitchen.rb +0 -34
@@ -0,0 +1,55 @@
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 'delegate'
20
+
21
+ module Kitchen
22
+
23
+ # Delegate class which adds the ability to find single and multiple
24
+ # objects by their #name in an Array. Hey, it's better than monkey-patching
25
+ # Array, right?
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
28
+ class Collection < SimpleDelegator
29
+
30
+ # Returns a single object by its name, or nil if none are found.
31
+ #
32
+ # @param name [String] name of object
33
+ # @return [Object] first match by name, or nil if none are found
34
+ def get(name)
35
+ __getobj__.find { |i| i.name == name }
36
+ end
37
+
38
+ # Returns a Collection of all objects whose #name is matched by the
39
+ # regular expression.
40
+ #
41
+ # @param regexp [Regexp] a regular expression pattern
42
+ # @return [Kitchen::Config::Collection<Object>] a new collection of
43
+ # matched objects
44
+ def get_all(regexp)
45
+ Kitchen::Collection.new(__getobj__.find_all { |i| i.name =~ regexp })
46
+ end
47
+
48
+ # Returns an Array of names from the collection as strings.
49
+ #
50
+ # @return [Array<String>] array of name strings
51
+ def as_names
52
+ __getobj__.map { |i| i.name }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,46 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2013, 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
+ module Color
22
+ ANSI = {
23
+ :reset => 0, :black => 30, :red => 31, :green => 32, :yellow => 33,
24
+ :blue => 34, :magenta => 35, :cyan => 36, :white => 37,
25
+ :bright_black => 30, :bright_red => 31, :bright_green => 32,
26
+ :bright_yellow => 33, :bright_blue => 34, :bright_magenta => 35,
27
+ :bright_cyan => 36, :bright_white => 37
28
+ }.freeze
29
+
30
+ COLORS = %w(
31
+ cyan yellow green magenta red blue bright_cyan bright_yellow
32
+ bright_green bright_magenta bright_red bright_blue
33
+ ).freeze
34
+
35
+ def self.escape(name)
36
+ return "" if name.nil?
37
+ return "" unless ansi = ANSI[name]
38
+ "\e[#{ansi}m"
39
+ end
40
+
41
+ def self.colorize(str, name)
42
+ color = escape(name)
43
+ color.empty? ? str : "#{color}#{str}#{escape(:reset)}"
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,223 @@
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 'celluloid'
20
+ require 'vendor/hash_recursive_merge'
21
+
22
+ module Kitchen
23
+
24
+ # Base configuration class for Kitchen. This class exposes configuration such
25
+ # as the location of the Kitchen config file, instances, log_levels, etc.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
28
+ class Config
29
+
30
+ attr_accessor :kitchen_root
31
+ attr_accessor :test_base_path
32
+ attr_accessor :log_level
33
+ attr_writer :supervised
34
+ attr_writer :platforms
35
+ attr_writer :suites
36
+
37
+ # Default driver plugin to use
38
+ DEFAULT_DRIVER_PLUGIN = "dummy".freeze
39
+
40
+ # Default base path which may contain `data_bags/` directories
41
+ DEFAULT_TEST_BASE_PATH = File.join(Dir.pwd, 'test/integration').freeze
42
+
43
+ # Creates a new configuration.
44
+ #
45
+ # @param [Hash] options configuration
46
+ # @option options [#read] :loader
47
+ # @option options [String] :kitchen_root
48
+ # @option options [String] :test_base_path
49
+ # @option options [Symbol] :log_level
50
+ # @option options [TrueClass,FalseClass] :supervised
51
+ def initialize(options = {})
52
+ @loader = options[:loader] || Kitchen::Loader::YAML.new
53
+ @kitchen_root = options[:kitchen_root] || Dir.pwd
54
+ @test_base_path = options[:test_base_path] || DEFAULT_TEST_BASE_PATH
55
+ @log_level = options[:log_level] || Kitchen::DEFAULT_LOG_LEVEL
56
+ @supervised = options[:supervised]
57
+ end
58
+
59
+ # @return [Array<Platform>] all defined platforms which will be used in
60
+ # convergence integration
61
+ def platforms
62
+ @platforms ||= Collection.new(
63
+ Array(data[:platforms]).map { |hash| new_platform(hash) })
64
+ end
65
+
66
+ # @return [Array<Suite>] all defined suites which will be used in
67
+ # convergence integration
68
+ def suites
69
+ @suites ||= Collection.new(
70
+ Array(data[:suites]).map { |hash| new_suite(hash) })
71
+ end
72
+
73
+ # @return [Array<Instance>] all instances, resulting from all platform and
74
+ # suite combinations
75
+ def instances
76
+ @instances ||= build_instances
77
+ end
78
+
79
+ # Returns an InstanceActor for all instance names matched by the regexp,
80
+ # or all by default.
81
+ #
82
+ # @param [Regexp] a regular expression to match on instance names
83
+ # @return [Array<InstanceActor>] all instance actors matching the regexp
84
+ def instance_actors(regexp = nil)
85
+ result = regexp.nil? ? instances : instances.get_all(regexp)
86
+ Collection.new(result.map { |instance| actor_registry(instance) })
87
+ end
88
+
89
+ def supervised
90
+ @supervised.nil? ? @supervised = true : @supervised
91
+ end
92
+
93
+ private
94
+
95
+ def new_suite(hash)
96
+ path_hash = {
97
+ :data_bags_path => calculate_path("data_bags", hash[:name]),
98
+ :roles_path => calculate_path("roles", hash[:name]),
99
+ }
100
+
101
+ Suite.new(hash.rmerge(path_hash))
102
+ end
103
+
104
+ def new_platform(hash)
105
+ Platform.new(hash)
106
+ end
107
+
108
+ def new_driver(hash)
109
+ hash[:driver_config] ||= Hash.new
110
+ hash[:driver_config][:kitchen_root] = kitchen_root
111
+
112
+ Driver.for_plugin(hash[:driver_plugin], hash[:driver_config])
113
+ end
114
+
115
+ def build_instances
116
+ results = []
117
+ filtered_instances = suites.product(platforms).delete_if do |arr|
118
+ arr[0].excludes.include?(arr[1].name)
119
+ end
120
+ filtered_instances.each_with_index do |arr, index|
121
+ results << new_instance(arr[0], arr[1], index)
122
+ end
123
+ Collection.new(results)
124
+ end
125
+
126
+ def new_instance(suite, platform, index)
127
+ platform_hash = platform_driver_hash(platform.name)
128
+ driver = new_driver(merge_driver_hash(platform_hash))
129
+
130
+ Instance.new(
131
+ :suite => suite,
132
+ :platform => platform,
133
+ :driver => driver,
134
+ :logger => new_instance_logger(index)
135
+ )
136
+ end
137
+
138
+ def actor_registry(instance)
139
+ Celluloid::Actor[actor_key(instance)] || new_instance_actor(instance)
140
+ end
141
+
142
+ def actor_key(instance)
143
+ "#{object_id}__#{instance.name}"
144
+ end
145
+
146
+ def new_instance_actor(instance)
147
+ actor_name = actor_key(instance)
148
+
149
+ actor = if supervised
150
+ supervisor = InstanceActor.supervise_as(actor_name, instance)
151
+ actor = supervisor.actors.first
152
+ Kitchen.logger.debug("Supervising #{actor.to_str} with #{supervisor}")
153
+ actor
154
+ else
155
+ Celluloid::Actor[actor_name] = InstanceActor.new(instance)
156
+ end
157
+
158
+ manager.link(actor)
159
+
160
+ actor
161
+ end
162
+
163
+ def manager
164
+ @manager ||= Manager.new
165
+ end
166
+
167
+ def log_root
168
+ File.expand_path(File.join(kitchen_root, ".kitchen", "logs"))
169
+ end
170
+
171
+ def platform_driver_hash(platform_name)
172
+ h = data[:platforms].find { |p| p[:name] == platform_name } || Hash.new
173
+
174
+ h.select { |key, value| [:driver_plugin, :driver_config].include?(key) }
175
+ end
176
+
177
+ def new_instance_logger(index)
178
+ level = Util.to_logger_level(self.log_level)
179
+ color = Color::COLORS[index % Color::COLORS.size].to_sym
180
+
181
+ lambda do |name|
182
+ logfile = File.join(log_root, "#{name}.log")
183
+
184
+ Logger.new(:stdout => STDOUT, :color => color, :logdev => logfile,
185
+ :level => level, :progname => name)
186
+ end
187
+ end
188
+
189
+ def data
190
+ @data ||= @loader.read
191
+ end
192
+
193
+ def merge_driver_hash(driver_hash)
194
+ default_driver_hash.rmerge(common_driver_hash.rmerge(driver_hash))
195
+ end
196
+
197
+ def calculate_path(path, suite_name)
198
+ suite_path = File.join(test_base_path, suite_name, path)
199
+ common_path = File.join(test_base_path, path)
200
+ top_level_path = File.join(Dir.pwd, path)
201
+
202
+ if File.directory?(suite_path)
203
+ suite_path
204
+ elsif File.directory?(common_path)
205
+ common_path
206
+ elsif File.directory?(top_level_path)
207
+ top_level_path
208
+ else
209
+ nil
210
+ end
211
+ end
212
+
213
+ def default_driver_hash
214
+ { :driver_plugin => DEFAULT_DRIVER_PLUGIN, :driver_config => {} }
215
+ end
216
+
217
+ def common_driver_hash
218
+ data.select do |key, value|
219
+ [:driver_plugin, :driver_config].include?(key)
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,180 @@
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
+ module Kitchen
20
+
21
+ module Driver
22
+
23
+ # Base class for a driver. A driver is responsible for carrying out the
24
+ # lifecycle activities of an instance, such as creating, converging, and
25
+ # destroying an instance.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
28
+ class Base
29
+
30
+ include ShellOut
31
+ include Logging
32
+
33
+ attr_writer :instance
34
+
35
+ class << self
36
+ attr_reader :serial_actions
37
+ end
38
+
39
+ def initialize(config = {})
40
+ @config = config
41
+ self.class.defaults.each do |attr, value|
42
+ @config[attr] = value unless @config[attr]
43
+ end
44
+ Array(self.class.validations).each do |tuple|
45
+ tuple.last.call(tuple.first, config[tuple.first])
46
+ end
47
+ end
48
+
49
+ # Provides hash-like access to configuration keys.
50
+ #
51
+ # @param attr [Object] configuration key
52
+ # @return [Object] value at configuration key
53
+ def [](attr)
54
+ config[attr]
55
+ end
56
+
57
+ # Creates an instance.
58
+ #
59
+ # @param state [Hash] mutable instance and driver state
60
+ # @raise [ActionFailed] if the action could not be completed
61
+ def create(state) ; end
62
+
63
+ # Converges a running instance.
64
+ #
65
+ # @param state [Hash] mutable instance and driver state
66
+ # @raise [ActionFailed] if the action could not be completed
67
+ def converge(state) ; end
68
+
69
+ # Sets up an instance.
70
+ #
71
+ # @param state [Hash] mutable instance and driver state
72
+ # @raise [ActionFailed] if the action could not be completed
73
+ def setup(state) ; end
74
+
75
+ # Verifies a converged instance.
76
+ #
77
+ # @param state [Hash] mutable instance and driver state
78
+ # @raise [ActionFailed] if the action could not be completed
79
+ def verify(state) ; end
80
+
81
+ # Destroys an instance.
82
+ #
83
+ # @param state [Hash] mutable instance and driver state
84
+ # @raise [ActionFailed] if the action could not be completed
85
+ def destroy(state) ; end
86
+
87
+ # Returns the shell command array that will log into an instance.
88
+ #
89
+ # @param state [Hash] mutable instance and driver state
90
+ # @return [Array] an array of command line tokens to be used in a
91
+ # fork/exec
92
+ # @raise [ActionFailed] if the action could not be completed
93
+ def login_command(state)
94
+ raise ActionFailed, "Remote login is not supported in this driver."
95
+ end
96
+
97
+ protected
98
+
99
+ attr_reader :config, :instance
100
+
101
+ ACTION_METHODS = %w{create converge setup verify destroy}.
102
+ map(&:to_sym).freeze
103
+
104
+ def logger
105
+ instance.logger
106
+ end
107
+
108
+ def puts(msg)
109
+ info(msg)
110
+ end
111
+
112
+ def print(msg)
113
+ info(msg)
114
+ end
115
+
116
+ def run_command(cmd, use_sudo = nil, log_subject = nil)
117
+ use_sudo = config[:use_sudo] if use_sudo.nil?
118
+ log_subject = Util.to_snake_case(self.class.to_s)
119
+
120
+ super(cmd, use_sudo, log_subject)
121
+ end
122
+
123
+ def kb_setup_cmd
124
+ busser.setup_cmd
125
+ end
126
+
127
+ def kb_sync_cmd
128
+ busser.sync_cmd
129
+ end
130
+
131
+ def kb_run_cmd
132
+ busser.run_cmd
133
+ end
134
+
135
+ def busser
136
+ @busser ||= begin
137
+ raise ClientError, "Instance must be set for Driver" if instance.nil?
138
+
139
+ Busser.new(instance.suite.name)
140
+ end
141
+ end
142
+
143
+ def self.defaults
144
+ @defaults ||= Hash.new
145
+ end
146
+
147
+ def self.default_config(attr, value)
148
+ defaults[attr] = value
149
+ end
150
+
151
+ def self.validations
152
+ @validations
153
+ end
154
+
155
+ def self.required_config(attr, &block)
156
+ @validations = [] if @validations.nil?
157
+ if ! block_given?
158
+ klass = self
159
+ block = lambda do |attr, value|
160
+ if value.nil? || value.to_s.empty?
161
+ raise UserError, "#{klass}#config[:#{attr}] cannot be blank"
162
+ end
163
+ end
164
+ end
165
+ @validations << [attr, block]
166
+ end
167
+
168
+ def self.no_parallel_for(*methods)
169
+ Array(methods).each do |meth|
170
+ if ! ACTION_METHODS.include?(meth)
171
+ raise ClientError, "##{meth} is not a valid no_parallel_for method"
172
+ end
173
+ end
174
+
175
+ @serial_actions ||= []
176
+ @serial_actions += methods
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,81 @@
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 'kitchen'
20
+
21
+ module Kitchen
22
+
23
+ module Driver
24
+
25
+ # Dummy driver for Kitchen.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
28
+ class Dummy < Kitchen::Driver::Base
29
+
30
+ default_config :sleep, 0
31
+ default_config :random_failure, false
32
+
33
+ def create(state)
34
+ state[:my_id] = "#{instance.name}-#{Time.now.to_i}"
35
+ report(:create, state)
36
+ end
37
+
38
+ def converge(state)
39
+ report(:converge, state)
40
+ end
41
+
42
+ def setup(state)
43
+ report(:setup, state)
44
+ end
45
+
46
+ def verify(state)
47
+ report(:verify, state)
48
+ end
49
+
50
+ def destroy(state)
51
+ report(:destroy, state)
52
+ state.delete(:my_id)
53
+ end
54
+
55
+ private
56
+
57
+ def report(action, state)
58
+ what = action.capitalize
59
+ info("[Dummy] #{what} on instance=#{instance} with state=#{state}")
60
+ sleep_if_set
61
+ random_failure_if_set(action)
62
+ debug("[Dummy] #{what} completed (#{config[:sleep]}s).")
63
+ end
64
+
65
+ def sleep_if_set
66
+ sleep(config[:sleep].to_f) if config[:sleep].to_f > 0.0
67
+ end
68
+
69
+ def random_failure_if_set(action)
70
+ if config[:random_failure] && randomly_fail?
71
+ debug("[Dummy] Random failure for action ##{action}.")
72
+ raise ActionFailed, "Action ##{action} failed for #{instance.to_str}."
73
+ end
74
+ end
75
+
76
+ def randomly_fail?
77
+ [true, false].sample
78
+ end
79
+ end
80
+ end
81
+ end