test-kitchen 0.7.0 → 1.0.0.alpha.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 (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