rspec-system 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +31 -15
- data/lib/rspec-system/formatter.rb +10 -0
- data/lib/rspec-system/helpers.rb +77 -36
- data/lib/rspec-system/log.rb +4 -0
- data/rspec-system.gemspec +1 -1
- metadata +1 -1
data/README.md
CHANGED
@@ -32,7 +32,7 @@ Start by creating a helper file in `spec/spec_helper_system.rb` containing somet
|
|
32
32
|
c.system_setup_block = proc do
|
33
33
|
include RSpecSystem::Helpers
|
34
34
|
# Insert some setup tasks here
|
35
|
-
|
35
|
+
system_run('yum install -y ntp')
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -42,8 +42,8 @@ Create the directory `spec/system` in your project, make sure your unit tests go
|
|
42
42
|
|
43
43
|
describe 'basics' do
|
44
44
|
it 'should cat /etc/resolv.conf' do
|
45
|
-
|
46
|
-
|
45
|
+
system_run('cat /etc/resolv.conf') do |s,o,e|
|
46
|
+
o.should =~ /localhost/
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
@@ -63,23 +63,38 @@ That will setup the rake task `rake spec:system`.
|
|
63
63
|
|
64
64
|
### Creating a nodeset file
|
65
65
|
|
66
|
-
A nodeset file outlines all the node configurations for your tests. The concept here is to define one or more 'nodesets' each nodeset containing one or more 'nodes'.
|
66
|
+
A nodeset file outlines all the node configurations for your tests. The concept here is to define one or more 'nodesets' each nodeset containing one or more 'nodes'. Create the file in your projects root directory as `.nodeset.yml`.
|
67
67
|
|
68
68
|
---
|
69
69
|
default_set: 'centos-58-x64'
|
70
70
|
sets:
|
71
71
|
'centos-58-x64':
|
72
72
|
nodes:
|
73
|
-
|
73
|
+
'main.vm':
|
74
74
|
prefab: 'centos-58-x64'
|
75
|
+
'debian-606-x64':
|
76
|
+
nodes:
|
77
|
+
'main.vm':
|
78
|
+
prefab: 'debian-606-x64'
|
75
79
|
|
76
80
|
The file must adhere to the Kwalify schema supplied in `resources/kwalify-schemas/nodeset_schema.yml`.
|
77
81
|
|
82
|
+
* `sets`: Each set contains a series of nodes, and is given a unique name. You can create sets with only 1 node if you like.
|
83
|
+
* `sets -> [setname] -> nodes`: Node definitions for a set. Each node needs a unique name so you can address each one individualy if you like.
|
84
|
+
* `sets -> [setname] -> nodes -> [name] -> prefab`: This relates to the prefabricated node template you wish to use. Currently this is the only way to launch a node. Look in `resources/prefabs.yml` for more details.
|
85
|
+
* `default_set`: this is the default set to run if none are provided with `rake spec:system`. This should be the most common platform normally.
|
86
|
+
|
78
87
|
### Prefabs
|
79
88
|
|
80
|
-
Prefabs are 'pre-rolled' virtual images, for now its the only way to
|
89
|
+
Prefabs are 'pre-rolled' virtual images, for now its the only way to specify a template. In the future we will probably allow you to specify your own prefab file, and override prefab settings in a nodeset file as well.
|
90
|
+
|
91
|
+
The current built-in prefabs are defined in `resources/prefabs.yml`. The current set are based on boxes hosted on <http://puppet-vagrant-boxes.puppetlabs.com> as they have been built by myself and are generally trusted and have a reproducable build cycle (they aren't just 'golden images'). In the future I'll probably expand that list, but attempt to stick to boxes that we have control over.
|
92
|
+
|
93
|
+
Prefabs are designed to be generic across different hosting environments. For example, you should be able to use a prefab string and launch an EC2 or Vagrant image and find that the images are identical (or as much as possible). The goal should be that local Vagrant users should find their own local tests pass, and when submitting code this should not change for EC2.
|
94
|
+
|
95
|
+
For this reason there are various `provider_specific` settings that apply to different provider types. For now though, only `vagrant` specific settings are provided.
|
81
96
|
|
82
|
-
|
97
|
+
`facts` in the prefab are literally dumps of `facter -p` on the host stored in the prefab file so you can look them up without addressing the machine. These are accessed using the `system_node#facts` method on the helper results and can be used in conditional logic during test runs and setup tasks. Not all the facts are supplied, only the more interesting ones.
|
83
98
|
|
84
99
|
### Running tests
|
85
100
|
|
@@ -89,8 +104,8 @@ Run the system tests with:
|
|
89
104
|
|
90
105
|
Instead of switches, we use a number of environment variables to modify the behaviour of running tests. This is more inline with the way testing frameworks like Jenkins work, and should be pretty easy for command line users as well:
|
91
106
|
|
92
|
-
* *RSPEC_VIRTUAL_ENV* - the type of virtual environment to run (currently `vagrant` is the only option)
|
93
|
-
* *RSPEC_SET* - the set to use when running tests (defaults to the `default_set` setting in the projects `.nodeset.yml` file)
|
107
|
+
* *RSPEC_VIRTUAL_ENV* - the type of virtual environment to run (currently `vagrant` is the only option). I'm undecided about this variable, so assume it might change in the future.
|
108
|
+
* *RSPEC_SET* - the set to use when running tests (defaults to the `default_set` setting in the projects `.nodeset.yml` file). This string must align with the entries under `sets` in your `.nodeset.yml`.
|
94
109
|
|
95
110
|
So if you wanted to run an alternate nodeset you could use:
|
96
111
|
|
@@ -102,16 +117,17 @@ In Jenkins you should be able to use RSPEC\_SET in a test matrix, thus obtaining
|
|
102
117
|
|
103
118
|
I want to start an eco-system of plugins for rspec-system, but do it in a sane way. Right now I see the following potential plugin types, if you think you can help please do:
|
104
119
|
|
105
|
-
*
|
120
|
+
* node providers - that is, abstractions around other virtualisation, cloud or system tools. Right now a NodeSet is tied to a virtual type, but I think this isn't granual enough. Some ideas for future providers are:
|
106
121
|
* blimpy - for firing up EC2 and OpenStack nodes, useful for Jenkins integration
|
107
|
-
* vmware - for those who have VMWare
|
108
|
-
* razor - for launching
|
109
|
-
* manual - not everything has to be 'launched' I can see a need for defining a static configuration for older machines that can't be poked and peeked.
|
122
|
+
* vmware vsphere - for those who have VMWare vSphere deployed already, this would be an awesome bonus.
|
123
|
+
* razor - for launching bare metail nodes for testing purposes. Could be really useful to have baremetal tests for software that needs it like `facter`.
|
124
|
+
* manual - not everything has to be 'launched' I can see a need for defining a static configuration for older machines that can't be poked and peeked. Of course, we might need to add cleanup tasks for this case.
|
110
125
|
* helper libraries - libraries that provide test helpers, and setup helpers for testing development on the software in question.
|
111
126
|
* distro - helpers that wrap common linux distro tasks, like package installation.
|
112
|
-
* puppet - helpers around installing different versions of puppet, PE as well - firing up masters. Perfect for testing modules I think.
|
127
|
+
* puppet - helpers around installing different versions of puppet, PE as well - firing up masters. Perfect for testing modules I think. Puppet could be used a provisioner for setting up tests, as using shell commands for this purpose is a bit rough, not very idepotent and is re-inventing the wheel.
|
113
128
|
* mcollective - for launching the basics, activemq, broker clusters. Useful for testing mcollective agents.
|
114
|
-
* puppetdb - helpers for setting up puppetdb, probably using the modules.
|
129
|
+
* puppetdb - helpers for setting up puppetdb, probably using the modules we already have.
|
130
|
+
* other config management tools - for the purposes of testing modules against them, or using them for test setup provisioners like I've mentioned before with Puppet.
|
115
131
|
* others I'm sure ...
|
116
132
|
|
117
133
|
These could be shipped as external gems, and plugged in to the rspec-system framework somehow.
|
@@ -1,6 +1,16 @@
|
|
1
1
|
require "rspec/core/formatters/base_text_formatter"
|
2
2
|
|
3
3
|
module RSpecSystem
|
4
|
+
# This custom formatter is designed for rspec-system test presentation
|
5
|
+
#
|
6
|
+
# Because rspec-system tests are often wordier and require lots of diagnostic
|
7
|
+
# information to be enabled for future debugging, the traditional document
|
8
|
+
# and progress formatters just simply aren't sufficient.
|
9
|
+
#
|
10
|
+
# This formatter instead treats each test as a document section, splitting
|
11
|
+
# up the output with obvious breaks so the user can clearly see when a test
|
12
|
+
# has started and finished. It also attempts to use color for visibility
|
13
|
+
# as well as listing test case information in a more verbose way.
|
4
14
|
class Formatter < RSpec::Core::Formatters::BaseTextFormatter
|
5
15
|
def initialize(output)
|
6
16
|
super(output)
|
data/lib/rspec-system/helpers.rb
CHANGED
@@ -1,47 +1,86 @@
|
|
1
1
|
# This module contains the main rspec helpers that are to be used within
|
2
|
-
# rspec-system tests.
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# be used within your setup blocks as well.
|
2
|
+
# rspec-system tests. These are the meat-and-potatoes of your system tests,
|
3
|
+
# and in theory there shouldn't be anything you can't do without the helpers
|
4
|
+
# here.
|
6
5
|
#
|
7
6
|
# These helpers in particular are core to the framework. You can however
|
8
|
-
#
|
7
|
+
# combine these helpers to create your own more powerful helpers in rspec
|
9
8
|
# if you wish.
|
10
9
|
#
|
10
|
+
# The helpers themselves are split into two main groups, Queries:
|
11
|
+
#
|
12
|
+
# * +system_node+ - queries and returns node information
|
13
|
+
#
|
14
|
+
# And Actions:
|
15
|
+
#
|
16
|
+
# * +system_run+ - runs a command on a node
|
17
|
+
# * +system_rcp+ - remote copies to a node
|
18
|
+
#
|
11
19
|
# @example Using run within your tests
|
12
20
|
# describe 'test running' do
|
13
21
|
# it 'run cat' do
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
22
|
+
# system_run 'cat /etc/resolv.conf' do |s, o, e|
|
23
|
+
# s.exitstatus.should == 0
|
24
|
+
# o.should =~ /localhost/
|
17
25
|
# end
|
18
26
|
# end
|
19
27
|
# end
|
20
28
|
# @example Using rcp in your tests
|
21
29
|
# describe 'test running' do
|
22
30
|
# it 'copy my files' do
|
23
|
-
#
|
31
|
+
# system_rcp :sp => 'mydata', :dp => '/srv/data'.should be_true
|
24
32
|
# end
|
25
33
|
# end
|
26
34
|
# @example Using node in your tests
|
27
35
|
# describe 'test running' do
|
28
36
|
# it 'do something if redhat' do
|
29
|
-
# if
|
30
|
-
#
|
37
|
+
# if system_node.facts['operatingsystem'] == 'RedHat' do
|
38
|
+
# system_run 'cat /etc/redhat-release'
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# @example Make your own helper
|
43
|
+
# describe 'my own helper' do
|
44
|
+
# def install_puppet
|
45
|
+
# facts = system_node.facts
|
46
|
+
#
|
47
|
+
# # Grab PL repository and install PL copy of puppet
|
48
|
+
# if facts['osfamily'] == 'RedHat'
|
49
|
+
# system_run('rpm -ivh http://yum.puppetlabs.com/el/5/products/i386/puppetlabs-release-5-6.noarch.rpm')
|
50
|
+
# system_run('yum install -y puppet')
|
51
|
+
# elsif facts['osfamily'] == 'Debian'
|
52
|
+
# system_run("wget http://apt.puppetlabs.com/puppetlabs-release-#{facts['lsbdistcodename']}.deb")
|
53
|
+
# system_run("dpkg -i puppetlabs-release-#{facts['lsbdistcodename']}.deb")
|
54
|
+
# system_run('apt-get update')
|
55
|
+
# system_run('apt-get install -y puppet')
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# it 'test installing latest puppet' do
|
60
|
+
# install_puppet
|
61
|
+
# system_run('puppet apply --version') do |s, o, e|
|
62
|
+
# s.exitstatus == 0
|
63
|
+
# o.should =~ /3.1/
|
64
|
+
# e.should == ''
|
31
65
|
# end
|
32
66
|
# end
|
33
67
|
# end
|
34
68
|
module RSpecSystem::Helpers
|
35
69
|
# @!group Actions
|
36
70
|
|
37
|
-
# Runs a shell command on a test host
|
71
|
+
# Runs a shell command on a test host.
|
38
72
|
#
|
39
73
|
# When invoked as a block the status,stdout and stderr are yielded to the
|
40
|
-
# block as parameters.
|
74
|
+
# block as parameters. If not these three variables are returned to the
|
75
|
+
# caller.
|
41
76
|
#
|
42
77
|
# If you have only provided 1 node in your nodeset, or you have specified a
|
43
78
|
# a default you can avoid entering the name of the node if you wish.
|
44
79
|
#
|
80
|
+
# The underlying implementation is actually performed by the particular
|
81
|
+
# node provider, however this abstraction should mean you shouldn't need
|
82
|
+
# to worry about that.
|
83
|
+
#
|
45
84
|
# @api public
|
46
85
|
# @param options [Hash, String] options for command execution, if passed a
|
47
86
|
# string it will just use that for the command instead as a convenience.
|
@@ -58,15 +97,17 @@ module RSpecSystem::Helpers
|
|
58
97
|
# @yieldparam stderr [String] the standard error of the command result
|
59
98
|
# @return [Array<Process::Status,String,String>] returns status, stdout and
|
60
99
|
# stderr when called as a simple method.
|
61
|
-
def
|
100
|
+
def system_run(options)
|
62
101
|
ns = rspec_system_node_set
|
63
102
|
dn = ns.default_node
|
64
103
|
|
65
|
-
#
|
104
|
+
# If options is a string, turn the string into a command in the normal
|
105
|
+
# options hash.
|
66
106
|
if options.is_a?(String)
|
67
107
|
options = {:c => options}
|
68
108
|
end
|
69
109
|
|
110
|
+
# Defaults etc.
|
70
111
|
options = {
|
71
112
|
:node => options[:n] || dn,
|
72
113
|
:n => options[:node] || dn,
|
@@ -75,12 +116,12 @@ module RSpecSystem::Helpers
|
|
75
116
|
}.merge(options)
|
76
117
|
|
77
118
|
if options[:c].nil?
|
78
|
-
raise "Cannot use
|
119
|
+
raise "Cannot use system_run with no :command option"
|
79
120
|
end
|
80
121
|
|
81
|
-
log.info("
|
122
|
+
log.info("system_run #{options[:c]} on #{options[:n].name} executed")
|
82
123
|
status, stdout, stderr = result = ns.run(options)
|
83
|
-
log.info("
|
124
|
+
log.info("system_run results:\n" +
|
84
125
|
"-----------------------\n" +
|
85
126
|
"Exit Status: #{status.exitstatus}\n" +
|
86
127
|
"<stdout>#{stdout}</stdout>\n" +
|
@@ -94,14 +135,15 @@ module RSpecSystem::Helpers
|
|
94
135
|
end
|
95
136
|
end
|
96
137
|
|
97
|
-
# Remotely copy files to a test node
|
98
|
-
#
|
99
|
-
#
|
138
|
+
# Remotely copy files to a test node
|
139
|
+
#
|
140
|
+
# Just specify a source path, destination path, and optionally a destination
|
141
|
+
# node (if the default isn't enough) and go.
|
100
142
|
#
|
101
|
-
#
|
143
|
+
# The underlying implementation is actually performed by the particular
|
144
|
+
# node provider, however this abstraction should mean you shouldn't need
|
145
|
+
# to worry about that.
|
102
146
|
#
|
103
|
-
# @example Remote copy /srv/data to remote host
|
104
|
-
# rcp(:dest_path => '/srv/data', :source_path => 'mydata')
|
105
147
|
# @param options [Hash] options for command execution
|
106
148
|
# @option options [String] :source_path source to copy files from (currently
|
107
149
|
# only locally)
|
@@ -115,10 +157,7 @@ module RSpecSystem::Helpers
|
|
115
157
|
# for future use. Patches welcome.
|
116
158
|
# @option options [RSpecSystem::Node] :s alias for source_node
|
117
159
|
# @return [Bool] returns true if successful
|
118
|
-
|
119
|
-
# aliases and bloody yarddocs from some other magic format. Ideas?
|
120
|
-
# @todo Support system to system copy using source_node option.
|
121
|
-
def rcp(options)
|
160
|
+
def system_rcp(options)
|
122
161
|
options = {
|
123
162
|
:source_path => options[:sp],
|
124
163
|
:destination_path => options[:dp],
|
@@ -126,15 +165,15 @@ module RSpecSystem::Helpers
|
|
126
165
|
:sp => options[:source_path],
|
127
166
|
:destination_node => rspec_system_node_set.default_node,
|
128
167
|
:d => rspec_system_node_set.default_node,
|
129
|
-
:source_node =>
|
130
|
-
:s =>
|
168
|
+
:source_node => nil,
|
169
|
+
:s => nil,
|
131
170
|
}.merge(options)
|
132
171
|
|
133
172
|
d = options[:d]
|
134
173
|
sp = options[:sp]
|
135
174
|
dp = options[:dp]
|
136
175
|
|
137
|
-
log.info("
|
176
|
+
log.info("system_rcp from #{sp} to #{d.name}:#{dp} executed")
|
138
177
|
status, stdout, stderr = results = rspec_system_node_set.rcp(options)
|
139
178
|
log.info("rcp results:\n" +
|
140
179
|
"-----------------------\n" +
|
@@ -160,16 +199,18 @@ module RSpecSystem::Helpers
|
|
160
199
|
# @param options [Hash] search criteria
|
161
200
|
# @option options [String] :name the canonical name of the node
|
162
201
|
# @return [RSpecSystem::Node] node object
|
163
|
-
def
|
202
|
+
def system_node(options = {})
|
164
203
|
ns = rspec_system_node_set
|
165
204
|
options = {
|
166
|
-
:name => ns.default_node,
|
205
|
+
:name => ns.default_node.name,
|
167
206
|
}.merge(options)
|
168
207
|
|
169
|
-
|
170
|
-
|
208
|
+
name = options[:name]
|
209
|
+
|
210
|
+
if name.nil?
|
211
|
+
raise "No nodes search specified, and no default"
|
171
212
|
else
|
172
|
-
|
213
|
+
return ns.nodes[name]
|
173
214
|
end
|
174
215
|
end
|
175
216
|
end
|
data/lib/rspec-system/log.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'logger'
|
2
2
|
|
3
|
+
# This log overlay module, provides access to the +log+ method.
|
3
4
|
module RSpecSystem::Log
|
5
|
+
# Return the default Logger object.
|
6
|
+
#
|
7
|
+
# @return [Logger] default logger object
|
4
8
|
def log
|
5
9
|
return @logger if @logger
|
6
10
|
@logger = ::Logger.new(STDOUT)
|
data/rspec-system.gemspec
CHANGED