chef-workflow-testlib 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .chef-workflow
19
+ html
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in chef-workflow-testlib.gemspec
4
+ gemspec
5
+
6
+ if ENV["ERIKH_LOCAL"]
7
+ gem 'chef-workflow', :path => '~/repos/ht/chef-workflow'
8
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Erik Hollensbe
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,316 @@
1
+ # Chef Workflow - Test Libraries and Tooling
2
+
3
+ This gem provides a set of libraries to drive
4
+ [minitest](https://github.com/seattlerb/minitest) integration for a number of things:
5
+
6
+ 1. spawning whole networks of machines
7
+ 2. testing their converge successes
8
+ 3. testing interoperability between machines
9
+ 4. testing advanced functionality in chef, like search.
10
+
11
+ **It is not intended for testing individual cookbooks.** The default
12
+ provisioning systems expect roles, for example, but it's a different tool for a
13
+ different problem. If you want to verify your open-source cookbooks against a
14
+ variety of environments, I strongly suggest you look at
15
+ [test-kitchen](https://github.com/opscode/test-kitchen), which is intended to
16
+ solve this problem.
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ gem 'chef-workflow-testlib'
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install chef-workflow-testlib
31
+
32
+ ## Quick Start
33
+
34
+ The easiest way to get started is to integrate
35
+ [chef-workflow-tasklib](https://github.com/chef-workflow/chef-workflow-tasklib)
36
+ into your environment. This has a number of benefits, such as working within
37
+ the same system to control machine provisioning, building a chef server,
38
+ uploading your repository to a chef server, several options for driving tests,
39
+ and so on. Once you've set that up, use the instructions over there to build a
40
+ test chef server.
41
+
42
+ So, after you've done that go ahead and create a `test` directory at the root
43
+ of your repository. Inside that, create a `test_mytest.rb` that provisions a
44
+ few machines and performs a search on them. This actually doesn't have much
45
+ real-world application, but it'll get you familiar with the workflow.
46
+
47
+ You will need some working roles to perform this test. We'll name these roles
48
+ `my_role` and `my_other_role` in the example, but you'll need to replace them
49
+ with roles you actually use.
50
+
51
+ This test will use `vagrant` to provision your machines, bootstrap them with
52
+ knife, then run your tests. After it's done, it will tear those machines down.
53
+ If the nodes fail to come up for any reason, your test should yield an error
54
+ and deprovision the machines.
55
+
56
+ ```ruby
57
+ require 'chef-workflow/helper'
58
+ class MyTest < MiniTest::Unit::VagrantTestCase
59
+ def self.before_suite
60
+ provision('my_role')
61
+ provision('my_other_role')
62
+ wait_for('my_role')
63
+ wait_for('my_other_role')
64
+ end
65
+
66
+ def self.after_suite
67
+ deprovision('my_role')
68
+ deprovision('my_other_role')
69
+ end
70
+
71
+ def test_searching_one
72
+ assert_search_count(:node, 'roles:my_role', 1)
73
+ end
74
+
75
+ def test_searching_two
76
+ assert_search_count(:node, 'roles:my_other_role', 1)
77
+ end
78
+ end
79
+ ```
80
+
81
+ Then run your test suite with `bundle exec rake test:build`, which will perform
82
+ an upload of your repository to the test chef server, and run your tests.
83
+
84
+ We're quite aware this takes a while to run, and does very little. There are a
85
+ couple of ways to deal with this, but involve understanding the system a little
86
+ more; something we're about to cover.
87
+
88
+ ## Dealing with Slow
89
+
90
+ Ok, so we got our test suite off the ground, and boy do those tests take a
91
+ while to run. I have two bits of good news for you:
92
+
93
+ You aren't locked into using Vagrant, and while Vagrant has limitations which
94
+ make it impossible to parallelize, machine provisions don't have to be serial.
95
+ EC2 support allows you to provision many machines in parallel, which can
96
+ improve run time. The provisioning system is also flexible enough to support
97
+ other systems too, so if you'd like to see your favorite system get some love,
98
+ the best way to do so is to file an issue or pull request.
99
+
100
+ The second is that in most scenarios, you have a few servers that you don't
101
+ really care about for this test, but need to be available for the machine to
102
+ function or converge properly, like a DNS or syslog server. All provisioning is
103
+ tracked and the state information is actually written to disk in your
104
+ `.chef-workflow` directory. What that means is that if you don't need to
105
+ rebuild your machines, you don't actually have to until you're ready to.
106
+
107
+ ### State Management with MiniTest Subclasses
108
+
109
+ Let's start off easy. Take our example above, and remove the deprovisioning
110
+ lines in the `after_suite` call:
111
+
112
+ ```ruby
113
+ require 'chef-workflow/helper'
114
+ class MyTest < MiniTest::Unit::VagrantTestCase
115
+ def self.before_suite
116
+ provision('my_role')
117
+ provision('my_other_role')
118
+ wait_for('my_role')
119
+ wait_for('my_other_role')
120
+ end
121
+
122
+ def self.after_suite
123
+ # omg, where did they go?
124
+ end
125
+
126
+ def test_searching_one
127
+ assert_search_count(:node, 'roles:my_role', 1)
128
+ end
129
+
130
+ def test_searching_two
131
+ assert_search_count(:node, 'roles:my_other_role', 1)
132
+ end
133
+ end
134
+ ```
135
+
136
+ And run your tests again. Go ahead, run them twice. The second time should be
137
+ pretty quick, in the order of a second or so. This is because your machines
138
+ weren't provisioned the second time; the system determined they were already
139
+ built and didn't have to provision them again.
140
+
141
+ So how do we clean them up? `bundle exec rake chef:clean:machines`, or add our
142
+ deprovisioning lines back in and run the suite again. The state for a provision
143
+ lasts until the machine is deprovisioned, whether or not that's the same test
144
+ run. The rake task just reaps everything except for your chef server.
145
+
146
+ Now that we've seen the obvious performance increase, what is this good for?
147
+
148
+ ### Pre-baking essential infra with MiniTest subclasses
149
+
150
+ MiniTest (and all SUnit alikes) leverage subclassing pretty heavily when done
151
+ right to manage a common set of test dependencies. For example, we have a
152
+ subclass called 'WithInfra' in our test suite that looks like this:
153
+
154
+ ```ruby
155
+ class MiniTest::Unit::VagrantTestCase::WithInfra < MiniTest::Unit::VagrantTestCase
156
+ include SSHHelper
157
+
158
+ def self.before_suite
159
+ super
160
+
161
+ provision('bind_master')
162
+ provision('syslog_server')
163
+
164
+ wait_for('bind_master', 'syslog_server')
165
+
166
+ ssh_role_command('bind_master', 'chef-client')
167
+ ssh_role_command('syslog_server', 'chef-client')
168
+ end
169
+ end
170
+ ```
171
+
172
+ `SSHHelper` is a small mixin that provides `ssh_role_command` and other bits of
173
+ ssh-related functionality. In this case, our syslog depends on working DNS and
174
+ our BIND depends on working syslog; while they have cookbooks that are smart
175
+ enough to walk past those dependencies in the first chef run, once they're both
176
+ up converging them again will resolve each other and configure them
177
+ appropriately.
178
+
179
+ The good news is that we don't have to (but can) tear these servers down during
180
+ a run or even between runs if they're not causing problems, and you can always
181
+ check from a fresh provision by running the `chef:clean:machines` task before
182
+ `test`, or simply `test:rebuild`, which does a full deprovision, repository
183
+ upload, and test run. This saves you time (and money, in the EC2 case) and
184
+ still gives you a way to do a "full" run without a lot of extra work.
185
+
186
+ An actual test suite that uses this looks like this:
187
+
188
+ ```ruby
189
+ class TestBIND < MiniTest::Unit::VagrantTestCase::WithInfra
190
+ def self.before_suite
191
+ provision('bind_slave', 1, %w[bind_master syslog_server])
192
+ wait_for('bind_slave')
193
+ end
194
+
195
+ def test_something
196
+ # maybe a test that nsupdates the master and ensures it made it to slave
197
+ # could go here
198
+ end
199
+
200
+ def self.after_suite
201
+ deprovision('bind_slave')
202
+ end
203
+ end
204
+ ```
205
+
206
+ This test when run will create, test, and destroy the bind slave, but leave the
207
+ master and the syslog server alone -- something that we will undoubtedly need
208
+ for the next test case.
209
+
210
+ ### Dependency-based Provisioning
211
+
212
+ All provisions have a dependency list. Until they are satisfied, the stated
213
+ provision will not happen. This is mostly noticable in threaded provisioning
214
+ mode where multiple provisions can occur at once.
215
+
216
+ To wait for a specific machine to provision before continuing the test process,
217
+ use the `wait_for` method.
218
+
219
+ We can see this in our example above:
220
+
221
+ ```ruby
222
+ def self.before_suite
223
+ provision('bind_slave', 1, %w[bind_master syslog_server])
224
+ wait_for('bind_slave')
225
+ end
226
+ ```
227
+
228
+ In this instance, a `bind_slave` provision is scheduled, which depends on the
229
+ `bind_master` and `syslog_server` provisions. The `wait_for` statement is used
230
+ to control the test flow, so that it does not continue before `bind_slave`
231
+ provisions, which is what we want to actually test in this test case. Properly
232
+ used, this can enhance provisioning times by letting the scheduler provision
233
+ things as soon as they are capable of being provisioned, where you just care
234
+ about the servers you wish to test at any given point, instead of having to
235
+ manage this problem yourself.
236
+
237
+ You can't declare provisions that are dependent on provisions that don't exist
238
+ -- this will raise an exception. So, don't worry about fat-fingering it. :)
239
+
240
+ Here's a more advanced example that abuses the performance characteristics of the
241
+ scheduler. In this instance, we intend to test our monitoring system, which
242
+ will assert the "alive" status of many servers. We provision numerous ones in
243
+ the setup, and only wait for what we care about in each unit test.
244
+
245
+ ```ruby
246
+ class TestNagios < MiniTest::Unit::VagrantTestCase
247
+ def self.before_suite
248
+ provision('syslog_server')
249
+ provision('bind_master', 1, %w[syslog_server])
250
+ # we just care about bind for all these
251
+ provision('bind_slave', 1, %w[bind_master])
252
+ provision('web_server', 1, %w[bind_master])
253
+ provision('db_server', 1, %w[bind_master])
254
+ provision('nagios_server', 1, %w[bind_master])
255
+ # we need the nagios server available for all tests, so wait for that
256
+ wait_for('nagios_server')
257
+ end
258
+
259
+ def test_dns_monitoring
260
+ wait_for('bind_master', 'bind_slave')
261
+ # ensure monitoring works
262
+ end
263
+
264
+ def test_web_monitoring
265
+ wait_for('web_server')
266
+ # ensure monitoring works
267
+ end
268
+
269
+ def test_db_monitoring
270
+ wait_for('db_server')
271
+ # ensure monitoring works
272
+ end
273
+
274
+ def self.after_suite
275
+ %w[
276
+ bind_master
277
+ bind_slave
278
+ syslog_server
279
+ web_server
280
+ db_server
281
+ nagios_server
282
+ ].each { |x| deprovision(x) }
283
+ end
284
+ end
285
+ ```
286
+
287
+ The flow of this test is as such:
288
+
289
+ As soon as `bind_master` completes, `bind_slave`, `web_server`, `db_server`,
290
+ and `nagios_server` will begin provisioning. `setup` will wait until
291
+ `nagios_server` completes, and normal testing will begin. Each test waits for
292
+ its testable unit to finish provisioning before running the actual tests
293
+ against those units. With rare exception, after a test or two, the `wait_for`
294
+ commands will succeed immediately, largely because they have been provsioning
295
+ in the background while the other tests have been running.
296
+
297
+ If you're curious how all this works under the hood, see the
298
+ [chef-workflow](https://github.com/chef-workflow/chef-workflow) documentation on
299
+ the subject.
300
+
301
+ ## Contributing
302
+
303
+ 1. Fork it
304
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
305
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
306
+ 4. Push to the branch (`git push origin my-new-feature`)
307
+ 5. Create new Pull Request
308
+ 6. Adjustment of the license terms or the author credits will result in a
309
+ rejected pull request. It ain't cool, yo.
310
+
311
+ ## Authors
312
+
313
+ This work was partially sponsored by [HotelTonight](http://hoteltonight.com),
314
+ and you should check them out. They use this system for testing their
315
+ infrastructure. The programming was done primarily by [Erik
316
+ Hollensbe](https://github.com/erikh).
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rdoc/task'
4
+
5
+ RDoc::Task.new do |r|
6
+ r.main = "README.md"
7
+ r.rdoc_files.include(r.main, "LICENSE.txt", "lib/**/*.rb")
8
+ r.options << "--all"
9
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'chef-workflow-testlib/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "chef-workflow-testlib"
8
+ gem.version = Chef::Workflow::Testlib::VERSION
9
+ gem.authors = ["Erik Hollensbe"]
10
+ gem.email = ["erik+github@hollensbe.org"]
11
+ gem.description = %q{Test helpers and assertions for chef-workflow}
12
+ gem.summary = %q{Test helpers and assertions for chef-workflow}
13
+ gem.homepage = "https://github.com/chef-workflow/chef-workflow-testlib"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'chef-workflow', '~> 0.1.0'
21
+ gem.add_dependency 'minitest', '~> 4.3.0'
22
+ gem.add_dependency 'net-ssh', '~> 2.6.0'
23
+
24
+ gem.add_development_dependency 'rdoc'
25
+ gem.add_development_dependency 'rake'
26
+ end
@@ -0,0 +1,12 @@
1
+ require 'chef-workflow'
2
+ require 'chef-workflow/helpers/minitest'
3
+ require 'chef-workflow/test-case/ec2'
4
+ require 'chef-workflow/test-case/vagrant'
5
+ require 'minitest/unit'
6
+
7
+ class MiniTest::Unit::TestCase
8
+ include MiniTest::Assertions::RemoteChef
9
+ end
10
+
11
+ require 'chef-workflow/runner/provisioned'
12
+ require 'minitest/autorun'
@@ -0,0 +1,15 @@
1
+ require 'chef/search/query'
2
+ require 'chef/config'
3
+
4
+ #
5
+ # Small helper library, intended to be mixed into others that provides short
6
+ # helpers for doing complicated things with the Chef API.
7
+ #
8
+ module ChefHelper
9
+ #
10
+ # Perform a search and return the names of the nodes that match the search.
11
+ #
12
+ def perform_search(type, query)
13
+ Chef::Search::Query.new.search(type, query).first.map(&:name)
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ require 'chef-workflow/helpers/chef'
2
+ require 'minitest/unit'
3
+
4
+ #
5
+ # Small assertion library for minitest to assist with remote chef tests.
6
+ #
7
+ module MiniTest::Assertions::RemoteChef
8
+ include ChefHelper
9
+
10
+ #
11
+ # Assert that a search included the node names.
12
+ #
13
+ def assert_search(type, query, node_names)
14
+ assert_equal(node_names.sort, perform_search(type, query).sort)
15
+ end
16
+
17
+ #
18
+ # Refute that a search included the node names.
19
+ #
20
+ def refute_search(type, query, node_names)
21
+ refute_equal(node_names.sort, perform_search(type, query).sort)
22
+ end
23
+
24
+ #
25
+ # Assert the search included `count` elements. Does not verify what that
26
+ # count is of.
27
+ #
28
+ def assert_search_count(type, query, count)
29
+ assert_equal(count, perform_search(type, query).count)
30
+ end
31
+
32
+ #
33
+ # Refute the search included `count` elements. Does not verify what that
34
+ # count is of.
35
+ #
36
+ def refute_search_count(type, query, count)
37
+ refute_equal(count, perform_search(type, query).count)
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ require 'chef-workflow/support/scheduler'
2
+
3
+ $SCHEDULER ||= Scheduler.new
4
+
5
+ #
6
+ # Helper for provisioning. Intended to be instanced and assigned to the
7
+ # provision_helper attribute of a ProvisionedTestCase.
8
+ #
9
+ # All methods except `provision`, which is shorthand, are passed directly to
10
+ # the scheduler.
11
+ #
12
+ class ProvisionHelper
13
+ def schedule_provision(*args)
14
+ $SCHEDULER.schedule_provision(*args)
15
+ end
16
+
17
+ def deprovision(group_name)
18
+ $SCHEDULER.deprovision_group(group_name)
19
+ end
20
+
21
+ def wait_for(*args)
22
+ $SCHEDULER.wait_for(*args)
23
+ end
24
+
25
+ def serial=(arg)
26
+ $SCHEDULER.serial = arg
27
+ end
28
+
29
+ def run
30
+ $SCHEDULER.run
31
+ end
32
+
33
+ def provision(group_name, number_of_servers, dependencies)
34
+ raise "Please override this method"
35
+ end
36
+ end
@@ -0,0 +1,99 @@
1
+ require 'chef-workflow/support/ip'
2
+ require 'chef-workflow/support/knife'
3
+ require 'chef-workflow/support/debug'
4
+ require 'net/ssh'
5
+
6
+ #
7
+ # Helper for performing SSH on groups of servers. Intended to be mixed into
8
+ # test case classes.
9
+ #
10
+ module SSHHelper
11
+ include KnifePluginSupport
12
+ include DebugSupport
13
+
14
+ #
15
+ # run a command against a group of servers. These commands are run in
16
+ # parallel, but the command itself does not complete until all the threads
17
+ # have finished running.
18
+ #
19
+ def ssh_role_command(role, command)
20
+ t = []
21
+ IPSupport.singleton.get_role_ips(role).each do |ip|
22
+ t.push(
23
+ Thread.new do
24
+ ssh_command(ip, command)
25
+ end
26
+ )
27
+ end
28
+ t.each(&:join)
29
+ end
30
+
31
+ #
32
+ # takes a block which it uses inside of the open_channel block that Net::SSH
33
+ # uses. Intended to provide a consistent way of setting up Net::SSH Makes
34
+ # heavy use of KnifeSupport to determine how to drive the command.
35
+ #
36
+ def configure_ssh_command(ip, command)
37
+ command = "#{KnifeSupport.singleton.use_sudo ? 'sudo ': ''}#{command}"
38
+
39
+ options = { }
40
+
41
+ options[:password] = KnifeSupport.singleton.ssh_password if KnifeSupport.singleton.ssh_password
42
+ options[:keys] = [KnifeSupport.singleton.ssh_identity_file] if KnifeSupport.singleton.ssh_identity_file
43
+
44
+ Net::SSH.start(ip, KnifeSupport.singleton.ssh_user, options) do |ssh|
45
+ ssh.open_channel do |ch|
46
+ ch.on_open_failed do |ch, code, desc|
47
+ raise "Connection Error to #{ip}: #{desc}"
48
+ end
49
+
50
+ ch.exec(command) do |ch, success|
51
+ yield ch, success
52
+ end
53
+ end
54
+
55
+ ssh.loop
56
+ end
57
+ end
58
+
59
+ #
60
+ # Run a command against a single IP. Returns the exit status.
61
+ #
62
+ #
63
+ def ssh_command(ip, command)
64
+ configure_ssh_command(ip, command) do |ch, success|
65
+ return 1 unless success
66
+
67
+ if_debug(2) do
68
+ ch.on_data do |ch, data|
69
+ $stderr.puts data
70
+ end
71
+ end
72
+
73
+ ch.on_request("exit-status") do |ch, data|
74
+ return data.read_long
75
+ end
76
+ end
77
+ end
78
+
79
+ #
80
+ # run a command, and instead of capturing the exit status, return the data
81
+ # captured during the command run.
82
+ #
83
+ def ssh_capture(ip, command)
84
+ retval = ""
85
+ configure_ssh_command(ip, command) do |ch, success|
86
+ return "" unless success
87
+
88
+ ch.on_data do |ch, data|
89
+ retval << data
90
+ end
91
+
92
+ ch.on_request("exit-status") do |ch, data|
93
+ return retval
94
+ end
95
+ end
96
+
97
+ return retval
98
+ end
99
+ end
@@ -0,0 +1,23 @@
1
+ require 'minitest/unit'
2
+
3
+ class MiniTest::Unit::TestCase
4
+ def self.before_suite; end
5
+ def self.after_suite; end
6
+ v = $VERBOSE
7
+ $VERBOSE = nil
8
+ const_set(:SUPPORTS_INFO_SIGNAL, nil)
9
+ $VERBOSE = v
10
+ end
11
+
12
+ class MiniTest::Unit::ProvisionedRunner < MiniTest::Unit
13
+ def _run_suite(suite, type)
14
+ begin
15
+ suite.before_suite unless suite.test_methods.empty?
16
+ super(suite, type)
17
+ ensure
18
+ suite.after_suite unless suite.test_methods.empty?
19
+ end
20
+ end
21
+ end
22
+
23
+ MiniTest::Unit.runner = MiniTest::Unit::ProvisionedRunner.new
@@ -0,0 +1,41 @@
1
+ require 'chef-workflow/support/vm/ec2'
2
+ require 'chef-workflow/support/vm/knife'
3
+ require 'tempfile'
4
+ require 'chef-workflow/test-case/provisioned'
5
+ require 'chef-workflow/helpers/provision'
6
+ require 'chef-workflow/helpers/ssh'
7
+
8
+ #
9
+ # Subclass of ProvisionHelper, centered around EC2. Pulls some
10
+ # configuration from KnifeSupport and then drives VM::EC2Provisioner and
11
+ # VM::KnifeProvisioner.
12
+ #
13
+ class EC2ProvisionHelper < ProvisionHelper
14
+ def provision(group_name, number_of_servers=1, dependencies=[])
15
+ kp = VM::KnifeProvisioner.new
16
+ kp.username = KnifeSupport.singleton.ssh_user
17
+ kp.password = KnifeSupport.singleton.ssh_password
18
+ kp.use_sudo = KnifeSupport.singleton.use_sudo
19
+ kp.ssh_key = KnifeSupport.singleton.ssh_identity_file
20
+ kp.environment = KnifeSupport.singleton.test_environment
21
+
22
+ schedule_provision(
23
+ group_name,
24
+ [
25
+ VM::EC2Provisioner.new(group_name, number_of_servers),
26
+ kp
27
+ ],
28
+ dependencies
29
+ )
30
+ end
31
+ end
32
+
33
+ #
34
+ # ProvisionedTestCase that uses EC2ProvisionHelper
35
+ #
36
+ class MiniTest::Unit::EC2TestCase < MiniTest::Unit::ProvisionedTestCase
37
+ include SSHHelper
38
+ extend SSHHelper
39
+
40
+ self.provision_helper = EC2ProvisionHelper.new
41
+ end
@@ -0,0 +1,88 @@
1
+ require 'chef-workflow/support/knife'
2
+ require 'chef-workflow/runner/provisioned'
3
+
4
+ #
5
+ # Basic provisioned test case. Generally not intended for direct use but
6
+ # provides the scaffolding for subclasses.
7
+ #
8
+ # Set the class attribute `provision_helper` to configure your provision
9
+ # helper, which will be used for many methods this class provides.
10
+ #
11
+ class MiniTest::Unit::ProvisionedTestCase < MiniTest::Unit::TestCase
12
+ module ProvisionHelper
13
+ def inherited(klass)
14
+ unless klass.provision_helper
15
+ klass.provision_helper = self.provision_helper
16
+ end
17
+
18
+ MiniTest::Unit::TestCase.inherited(klass)
19
+ end
20
+
21
+ #
22
+ # Retrieves the provision helper.
23
+ #
24
+ def provision_helper
25
+ @provision_helper || (self.class.provision_helper rescue nil)
26
+ end
27
+
28
+ #
29
+ # Sets the provision helper.
30
+ #
31
+ def provision_helper=(arg)
32
+ @provision_helper = arg
33
+ end
34
+
35
+ #
36
+ # wait for a provision. takes a list of server group names. delegates to the
37
+ # provision helper.
38
+ #
39
+ def wait_for(*deps)
40
+ provision_helper.wait_for(*deps)
41
+ end
42
+
43
+ #
44
+ # Provision a server group. Takes a name, number of servers, and a list of
45
+ # dependencies (server group names). Delegates to the provision helper.
46
+ #
47
+ def provision(role, number_of_servers=1, addl_dependencies=[])
48
+ provision_helper.provision(role, number_of_servers, addl_dependencies)
49
+ provision_helper.run
50
+ end
51
+
52
+ #
53
+ # De-Provision a server group. Takes a name. Delegates to the provision
54
+ # helper.
55
+ #
56
+ def deprovision(role)
57
+ provision_helper.deprovision(role)
58
+ end
59
+
60
+ #
61
+ # Obtains the IP addresses for a given role as an array.
62
+ #
63
+ def get_role_ips(role)
64
+ IPSupport.singleton.get_role_ips(role)
65
+ end
66
+
67
+ #
68
+ # Easy way to reference KnifeSupport for getting configuration data.
69
+ #
70
+ def knife_config
71
+ KnifeSupport.singleton
72
+ end
73
+ end
74
+
75
+ include ProvisionHelper
76
+ extend ProvisionHelper
77
+
78
+ #
79
+ # Hook before the suite starts. Be sure in your subclasses to call this with
80
+ # `super`. Provisions machines configured as dependencies and starts the
81
+ # scheduler.
82
+ #
83
+ def self.before_suite
84
+ super
85
+
86
+ Chef::Config.from_file(KnifeSupport.singleton.knife_config_path)
87
+ end
88
+ end
@@ -0,0 +1,44 @@
1
+ require 'chef-workflow/support/vagrant'
2
+ require 'chef-workflow/support/vm/vagrant'
3
+ require 'chef-workflow/support/vm/knife'
4
+ require 'tempfile'
5
+ require 'chef-workflow/test-case/provisioned'
6
+ require 'chef-workflow/helpers/provision'
7
+ require 'chef-workflow/helpers/ssh'
8
+
9
+ #
10
+ # Subclass of ProvisionHelper, centered around Vagrant. Pulls some
11
+ # configuration from KnifeSupport and then drives VM::VagrantProvisioner and
12
+ # VM::KnifeProvisioner.
13
+ #
14
+ class VagrantProvisionHelper < ProvisionHelper
15
+ def provision(group_name, number_of_servers=1, dependencies=[])
16
+ self.serial = true
17
+
18
+ kp = VM::KnifeProvisioner.new
19
+ kp.username = KnifeSupport.singleton.ssh_user
20
+ kp.password = KnifeSupport.singleton.ssh_password
21
+ kp.use_sudo = KnifeSupport.singleton.use_sudo
22
+ kp.ssh_key = KnifeSupport.singleton.ssh_identity_file
23
+ kp.environment = KnifeSupport.singleton.test_environment
24
+
25
+ schedule_provision(
26
+ group_name,
27
+ [
28
+ VM::VagrantProvisioner.new(group_name, number_of_servers),
29
+ kp
30
+ ],
31
+ dependencies
32
+ )
33
+ end
34
+ end
35
+
36
+ #
37
+ # ProvisionedTestCase that uses VagrantProvisionHelper.
38
+ #
39
+ class MiniTest::Unit::VagrantTestCase < MiniTest::Unit::ProvisionedTestCase
40
+ include SSHHelper
41
+ extend SSHHelper
42
+
43
+ self.provision_helper = VagrantProvisionHelper.new
44
+ end
@@ -0,0 +1,7 @@
1
+ class Chef
2
+ module Workflow
3
+ module Testlib
4
+ VERSION = "0.1.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ require "chef-workflow-testlib/version"
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chef-workflow-testlib
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Erik Hollensbe
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: chef-workflow
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.1.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: minitest
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 4.3.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 4.3.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: net-ssh
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.6.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 2.6.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: rdoc
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Test helpers and assertions for chef-workflow
95
+ email:
96
+ - erik+github@hollensbe.org
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .gitignore
102
+ - Gemfile
103
+ - LICENSE.txt
104
+ - README.md
105
+ - Rakefile
106
+ - chef-workflow-testlib.gemspec
107
+ - lib/chef-workflow-testlib.rb
108
+ - lib/chef-workflow-testlib/version.rb
109
+ - lib/chef-workflow/helper.rb
110
+ - lib/chef-workflow/helpers/chef.rb
111
+ - lib/chef-workflow/helpers/minitest.rb
112
+ - lib/chef-workflow/helpers/provision.rb
113
+ - lib/chef-workflow/helpers/ssh.rb
114
+ - lib/chef-workflow/runner/provisioned.rb
115
+ - lib/chef-workflow/test-case/ec2.rb
116
+ - lib/chef-workflow/test-case/provisioned.rb
117
+ - lib/chef-workflow/test-case/vagrant.rb
118
+ homepage: https://github.com/chef-workflow/chef-workflow-testlib
119
+ licenses: []
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 1.8.24
139
+ signing_key:
140
+ specification_version: 3
141
+ summary: Test helpers and assertions for chef-workflow
142
+ test_files: []
143
+ has_rdoc: