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.
- data/.gitignore +20 -0
- data/.travis.yml +11 -0
- data/.yardopts +3 -0
- data/Gemfile +13 -0
- data/Guardfile +11 -0
- data/LICENSE +15 -0
- data/README.md +131 -0
- data/Rakefile +69 -0
- data/bin/kitchen +9 -4
- data/features/cli.feature +17 -0
- data/features/cli_init.feature +156 -0
- data/features/support/env.rb +14 -0
- data/lib/kitchen/busser.rb +166 -0
- data/lib/kitchen/chef_data_uploader.rb +156 -0
- data/lib/kitchen/cli.rb +540 -0
- data/lib/kitchen/collection.rb +55 -0
- data/lib/kitchen/color.rb +46 -0
- data/lib/kitchen/config.rb +223 -0
- data/lib/kitchen/driver/base.rb +180 -0
- data/lib/kitchen/driver/dummy.rb +81 -0
- data/lib/kitchen/driver/ssh_base.rb +192 -0
- data/lib/kitchen/driver.rb +42 -0
- data/lib/kitchen/errors.rb +52 -0
- data/lib/kitchen/instance.rb +327 -0
- data/lib/kitchen/instance_actor.rb +42 -0
- data/lib/kitchen/loader/yaml.rb +105 -0
- data/lib/kitchen/logger.rb +145 -0
- data/{cookbooks/test-kitchen/libraries/helpers.rb → lib/kitchen/logging.rb} +13 -9
- data/lib/kitchen/manager.rb +45 -0
- data/lib/kitchen/metadata_chopper.rb +52 -0
- data/lib/kitchen/platform.rb +61 -0
- data/lib/kitchen/rake_tasks.rb +59 -0
- data/lib/kitchen/shell_out.rb +65 -0
- data/lib/kitchen/state_file.rb +88 -0
- data/lib/kitchen/suite.rb +76 -0
- data/lib/kitchen/thor_tasks.rb +62 -0
- data/lib/kitchen/util.rb +79 -0
- data/{cookbooks/test-kitchen/recipes/erlang.rb → lib/kitchen/version.rb} +9 -6
- data/lib/kitchen.rb +98 -0
- data/lib/vendor/hash_recursive_merge.rb +74 -0
- data/spec/kitchen/collection_spec.rb +80 -0
- data/spec/kitchen/color_spec.rb +54 -0
- data/spec/kitchen/config_spec.rb +201 -0
- data/spec/kitchen/driver/dummy_spec.rb +191 -0
- data/spec/kitchen/instance_spec.rb +162 -0
- data/spec/kitchen/loader/yaml_spec.rb +243 -0
- data/spec/kitchen/platform_spec.rb +48 -0
- data/spec/kitchen/state_file_spec.rb +122 -0
- data/spec/kitchen/suite_spec.rb +64 -0
- data/spec/spec_helper.rb +47 -0
- data/templates/plugin/driver.rb.erb +23 -0
- data/templates/plugin/license_apachev2.erb +15 -0
- data/templates/plugin/license_gplv2.erb +18 -0
- data/templates/plugin/license_gplv3.erb +16 -0
- data/templates/plugin/license_mit.erb +22 -0
- data/templates/plugin/license_reserved.erb +5 -0
- data/templates/plugin/version.rb.erb +12 -0
- data/test-kitchen.gemspec +44 -0
- metadata +290 -82
- data/config/Cheffile +0 -47
- data/config/Kitchenfile +0 -39
- data/config/Vagrantfile +0 -114
- data/cookbooks/test-kitchen/attributes/default.rb +0 -25
- data/cookbooks/test-kitchen/metadata.rb +0 -27
- data/cookbooks/test-kitchen/recipes/chef.rb +0 -19
- data/cookbooks/test-kitchen/recipes/compat.rb +0 -39
- data/cookbooks/test-kitchen/recipes/default.rb +0 -51
- data/cookbooks/test-kitchen/recipes/ruby.rb +0 -29
- data/lib/test-kitchen/cli/destroy.rb +0 -36
- data/lib/test-kitchen/cli/init.rb +0 -37
- data/lib/test-kitchen/cli/platform_list.rb +0 -37
- data/lib/test-kitchen/cli/project_info.rb +0 -44
- data/lib/test-kitchen/cli/ssh.rb +0 -36
- data/lib/test-kitchen/cli/status.rb +0 -36
- data/lib/test-kitchen/cli/test.rb +0 -68
- data/lib/test-kitchen/cli.rb +0 -282
- data/lib/test-kitchen/dsl.rb +0 -63
- data/lib/test-kitchen/environment.rb +0 -166
- data/lib/test-kitchen/platform.rb +0 -79
- data/lib/test-kitchen/project/base.rb +0 -159
- data/lib/test-kitchen/project/cookbook.rb +0 -97
- data/lib/test-kitchen/project/cookbook_copy.rb +0 -58
- data/lib/test-kitchen/project/ruby.rb +0 -37
- data/lib/test-kitchen/project/supported_platforms.rb +0 -75
- data/lib/test-kitchen/project.rb +0 -23
- data/lib/test-kitchen/runner/base.rb +0 -154
- data/lib/test-kitchen/runner/openstack/dsl.rb +0 -39
- data/lib/test-kitchen/runner/openstack/environment.rb +0 -141
- data/lib/test-kitchen/runner/openstack.rb +0 -147
- data/lib/test-kitchen/runner/vagrant.rb +0 -95
- data/lib/test-kitchen/runner.rb +0 -21
- data/lib/test-kitchen/scaffold.rb +0 -88
- data/lib/test-kitchen/ui.rb +0 -73
- data/lib/test-kitchen/version.rb +0 -21
- 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
|