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.
- 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
|