barking_iguana-compound 0.1.1 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -177
- data/docs/CHANGELOG.md +49 -0
- data/docs/TODO.md +12 -0
- data/docs/_config.yml +1 -0
- data/docs/index.md +232 -0
- data/exe/compound +5 -0
- data/lib/barking_iguana/compound/ansible/inventory.rb +1 -1
- data/lib/barking_iguana/compound/ansible/inventory_writer.rb +40 -0
- data/lib/barking_iguana/compound/ansible/playbook.rb +14 -14
- data/lib/barking_iguana/compound/command_line_client.rb +25 -0
- data/lib/barking_iguana/compound/host.rb +21 -4
- data/lib/barking_iguana/compound/host_manager.rb +0 -3
- data/lib/barking_iguana/compound/server_spec.rb +1 -1
- data/lib/barking_iguana/compound/test.rb +7 -8
- data/lib/barking_iguana/compound/test_stage.rb +58 -18
- data/lib/barking_iguana/compound/test_suite.rb +22 -19
- data/lib/barking_iguana/compound/vagrant.rb +33 -2
- data/lib/barking_iguana/compound/version.rb +1 -1
- data/lib/barking_iguana/compound.rb +5 -2
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 660f437659c9d71510010c474a79652d23ae8f72
|
4
|
+
data.tar.gz: 6d9144927d86139196261607bffe1087ec1a60b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22aac3dbdb690d0b08230002e1a6391c8dbbc5b6c729064f1860027f552f81a7ad26b8906e4f8c223f2c6a3d0c54547d68cd2610f22d9073e4e02030dfcd5c5c
|
7
|
+
data.tar.gz: d2e833cc167db10884952f020accb87780b9341e0cac9264571d6548d1ae7c7d73bcd60bfdf78005fa78a76f03744619c278ffb24b1832cc54726000000f5c76
|
data/README.md
CHANGED
@@ -1,179 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# Compound
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
When you need to test several roles and/or playbooks together over their lifetime e.g. testing database failover and recovery.
|
6
|
-
|
7
|
-
## Installation
|
8
|
-
|
9
|
-
Add this line to your application's Gemfile:
|
10
|
-
|
11
|
-
```ruby
|
12
|
-
gem 'barking_iguana-compound'
|
13
|
-
```
|
14
|
-
|
15
|
-
And then execute:
|
16
|
-
|
17
|
-
$ bundle
|
18
|
-
|
19
|
-
Or install it yourself as:
|
20
|
-
|
21
|
-
$ gem install barking_iguana-compound
|
22
|
-
|
23
|
-
## Usage
|
24
|
-
|
25
|
-
Install it, as per above.
|
26
|
-
|
27
|
-
Normally you'll use Compond as part of a Rake task, so include it in your
|
28
|
-
`Rakefile`. Tell it to define Rake tasks for your compound tests - we assume
|
29
|
-
these live in `test/compound` - and where your playbooks live, your control
|
30
|
-
repository:
|
31
|
-
|
32
|
-
```ruby
|
33
|
-
require 'barking_iguana/compound'
|
34
|
-
ansible_control_repo = File.dirname __FILE__
|
35
|
-
BarkingIguana::Compound.new('test/compound', ansible_control_repo).define_rake_tasks
|
36
|
-
```
|
37
|
-
|
38
|
-
Now let's define a test managing hosts files. It's a trivial test, but it
|
39
|
-
suffices to demonstrate the capacilities of Compound.
|
40
|
-
|
41
|
-
### Writing a compound test
|
42
|
-
|
43
|
-
Create a directory for the test:
|
44
|
-
|
45
|
-
$ mkdir -p test/compound/hosts_file_management
|
46
|
-
|
47
|
-
Each test will have several _stages_, which represent parts of the expected
|
48
|
-
lifecycle of your servers. For example, you set up your servers using Ansible,
|
49
|
-
to you probably have a setup stage. You may be setting up a HA cluster, so
|
50
|
-
perhaps you have a stage where the master in the cluster fails.
|
51
|
-
|
52
|
-
Each stage involves several actions. We'll cover them in detail below, but in
|
53
|
-
summary:
|
54
|
-
|
55
|
-
The `setup` action runs first. For each stage this action decides which virtual
|
56
|
-
machines should be powered on or off during the rest of the stage.
|
57
|
-
|
58
|
-
Next, the `converge` action runs the playbook for that stage, if one is provided
|
59
|
-
by you, to allow things to happen. The playbook may apply roles or kill
|
60
|
-
processes, whatever you need to simulate what's happening to your servers at
|
61
|
-
that part of the lifecycle.
|
62
|
-
|
63
|
-
Finally, the `verify` stage runs some serverspec tests which you will define
|
64
|
-
against the results of the `converge` action. This allows you to check what
|
65
|
-
happens after the tasks in the playbook have been run e.g. you may verify that a
|
66
|
-
standby server has become the master.
|
67
|
-
|
68
|
-
Each stage lives in a directory inside the test. They're executed in
|
69
|
-
alphabetical order, and you are encouraged to prefix each stage with numbers to
|
70
|
-
make it very clear which one should execute in which order.
|
71
|
-
|
72
|
-
We'll cover each of those actions in greater detail now, for the setup stage of
|
73
|
-
our hosts file example.
|
74
|
-
|
75
|
-
#### Test Stages
|
76
|
-
|
77
|
-
Start by creating the directory for that stage:
|
78
|
-
|
79
|
-
$ mkdir -p test/compound/hosts_file_management/000-setup
|
80
|
-
|
81
|
-
##### The Setup Action
|
82
|
-
|
83
|
-
This action controls which virtual machines are available for you to work with,
|
84
|
-
by looking at an Ansible inventory file in the stage directory and starting all
|
85
|
-
the hosts it finds.
|
86
|
-
|
87
|
-
Let's create a simple inventory file with 2 hosts and 1 group.
|
88
|
-
|
89
|
-
The inventory file for each stage lives in the stage directory in a file called
|
90
|
-
`inventory`, so in our example so for that's `test/compound/hosts_file_management/000-setup/inventory`.
|
91
|
-
|
92
|
-
They're just normal Ansible inventory files, with only one restriction: the
|
93
|
-
`ansible_host` _must_ start with `10.8.`.
|
94
|
-
|
95
|
-
```
|
96
|
-
[linux]
|
97
|
-
host001 ansible_host=10.8.100.11
|
98
|
-
host002 ansible_host=10.8.100.12
|
99
|
-
```
|
100
|
-
|
101
|
-
When the setup action for a stage with this inventory is run, Compound will
|
102
|
-
launch two virtual machines for you to test with, `host001` and `host002` with
|
103
|
-
the respective IP addresses.
|
104
|
-
|
105
|
-
##### The Converge Action
|
106
|
-
|
107
|
-
Now that virtual machines are available to use, the converge action can run
|
108
|
-
the playbook for your stage against them.
|
109
|
-
|
110
|
-
Playbooks live in the stage directory, in a file called `playbook.yml`. I'm
|
111
|
-
inventive like that. For our example stage that's `test/compound/hosts_file_management/000-setup/playbook.yml`.
|
112
|
-
|
113
|
-
For our setup action we'll apply the `hosts` role to all hosts.
|
114
|
-
|
115
|
-
```
|
116
|
-
---
|
117
|
-
- hosts: all
|
118
|
-
roles:
|
119
|
-
- hosts
|
120
|
-
```
|
121
|
-
|
122
|
-
After a converge, Compound will attempt to verify whatever you ask it to.
|
123
|
-
Onwards, to the verify action.
|
124
|
-
|
125
|
-
##### The Verify Action
|
126
|
-
|
127
|
-
Now we run automated tests to assert that our virtual machines behave like we
|
128
|
-
think they should, after the stage playbook has been applied in the converge
|
129
|
-
action.
|
130
|
-
|
131
|
-
The verify action is based around serverspec tests, arranged around the name of
|
132
|
-
the host which they test.
|
133
|
-
|
134
|
-
For example, to test `host001` we'd create tests under a `test/compound/hosts_file_management/host001/`
|
135
|
-
directory. Each test file must be suffixed with `_spec.rb`. A simple example
|
136
|
-
would be checking that the other host of the pair is resolvable now that the
|
137
|
-
hosts role has been applied to the hosts:
|
138
|
-
|
139
|
-
```ruby
|
140
|
-
# test/compound/hosts_file_management/host001/resolving_spec.rb
|
141
|
-
describe host('host002') do
|
142
|
-
it "is resolvable" do
|
143
|
-
expect(subject).to be_resolvable
|
144
|
-
end
|
145
|
-
end
|
146
|
-
```
|
147
|
-
|
148
|
-
```ruby
|
149
|
-
# test/compound/hosts_file_management/host002/resolving_spec.rb
|
150
|
-
describe host('host001') do
|
151
|
-
it "is resolvable" do
|
152
|
-
expect(subject).to be_resolvable
|
153
|
-
end
|
154
|
-
end
|
155
|
-
```
|
156
|
-
|
157
|
-
Compound will take the appropriate actions to make sure your tests are run on
|
158
|
-
the correct hosts, you don't need to worry about SSH keys, passwords or ports.
|
159
|
-
|
160
|
-
### Running the tests
|
161
|
-
|
162
|
-
Since we've asked Compound to define rake tasks above, we can run those. The tasks generated are based on the directory names we use in the tests. The above test can be run like this:
|
163
|
-
|
164
|
-
$ bundle exec rake compound:hosts_file_management
|
165
|
-
|
166
|
-
You can see a list of all tests by asking Rake to list them:
|
167
|
-
|
168
|
-
$ bundle exec rake -T
|
169
|
-
|
170
|
-
## Development
|
171
|
-
|
172
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
173
|
-
|
174
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
175
|
-
|
176
|
-
## Contributing
|
177
|
-
|
178
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/barking_iguana-compound.
|
3
|
+
For docs see [`docs/index.md`][0].
|
179
4
|
|
5
|
+
[0]: https://barkingiguana.github.io/compound/
|
data/docs/CHANGELOG.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# CHANGELOG
|
2
|
+
|
3
|
+
Entries are in reverse chronological order.
|
4
|
+
|
5
|
+
## *0.1.5* (Current Development)
|
6
|
+
|
7
|
+
Add release notes here, as things are added to the project.
|
8
|
+
|
9
|
+
## *0.1.4* (2016-12-27)
|
10
|
+
|
11
|
+
* Bug fix: allow connecting to VMs when we generate their IP address on
|
12
|
+
the fly.
|
13
|
+
|
14
|
+
* Bug fix: correctly expand the staging directory when searching for
|
15
|
+
the stages `playbook.yml` or `inventory` file.
|
16
|
+
|
17
|
+
## *0.1.3* (2016-12-27)
|
18
|
+
|
19
|
+
* Various bug fixes - mostly typos that would stop the full test run
|
20
|
+
happening.
|
21
|
+
|
22
|
+
* Add a command line tool, so we don't need the `Rakefile`. Currently this
|
23
|
+
only handles running the entire test suite.
|
24
|
+
|
25
|
+
* Stop requiring that the inventory files specify an IP address. If no
|
26
|
+
IP address is provided for a VM it will be assigned an unallocated one.
|
27
|
+
|
28
|
+
* Simplify asking Compound to define Rake tasks by allowing it to guess
|
29
|
+
the directories involved. These can still be overridden, but they
|
30
|
+
shouldn't normally need to be.
|
31
|
+
|
32
|
+
* Stop supporting the concept of 'simple' tests - tests which have only
|
33
|
+
one implicit stage. Compound feels most useful when focussing on
|
34
|
+
lifecycle style tests which have several stages.
|
35
|
+
|
36
|
+
## *0.1.2* (2016-12-16)
|
37
|
+
|
38
|
+
Allow a default inventory and playbook for each test, living in the test
|
39
|
+
directory instead of in each stage. This is less likely to be useful for
|
40
|
+
the playbook, but a default inventory will reduce duplication.
|
41
|
+
|
42
|
+
## *0.1.1* (2016-12-16)
|
43
|
+
|
44
|
+
Symlink wrapper playbooks instead of writing temporary playbooks with
|
45
|
+
includes.
|
46
|
+
|
47
|
+
## *0.1.0* (2016-12-11)
|
48
|
+
|
49
|
+
Initial release.
|
data/docs/TODO.md
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# TODO
|
2
|
+
|
3
|
+
* We can add machines to different networks because even though we require
|
4
|
+
`10.8.*`, the networks created are `/24`'s. These should either be `/16` to
|
5
|
+
avoid surprises, or we should make the CIDR configurable, or we should give an
|
6
|
+
example showing this behaviour.
|
7
|
+
|
8
|
+
* Tests. So ironic that a testing tool has no tests. For release 1.0.0 we need tests.
|
9
|
+
|
10
|
+
* A host doesn't have a `uri`, it's got an IP address.
|
11
|
+
|
12
|
+
* We should support the ansible remote user attributes in the inventory, for each host.
|
data/docs/_config.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
theme: jekyll-theme-minimal
|
data/docs/index.md
ADDED
@@ -0,0 +1,232 @@
|
|
1
|
+
# BarkingIguana::Compound
|
2
|
+
|
3
|
+
Compound testing for Ansible atoms.
|
4
|
+
|
5
|
+
Sometimes we need to test the interaction of several roles and/or playbooks,
|
6
|
+
especially over the expected lifetime of our tech stack.
|
7
|
+
|
8
|
+
Say we have a typical three tier web app; web, app and database tiers.
|
9
|
+
|
10
|
+
When an app server is put into maintenance mode, does the reverse proxy on the
|
11
|
+
web tier pick that up? Does it reconfigure the pool so that requests no longer
|
12
|
+
hit that app server? Does it do so in a reasonable time?
|
13
|
+
|
14
|
+
Previously we could test that the configuration of the reverse proxy was what we
|
15
|
+
expected to see on disk, but testing that it behaved in the way that we thought
|
16
|
+
it would was a case of deploying to a test servers and poking at the result
|
17
|
+
manually. Error prone, slow, not terribly maintainable, and it can in many cases
|
18
|
+
block uses of a server that it typically a shared resource. Very not fun.
|
19
|
+
|
20
|
+
With Compound, we can write Ansible inventories to represent real world
|
21
|
+
situations, run playbooks against them to install and configure the software we
|
22
|
+
want to test or simulate real world behaviour such as turning on maintenance
|
23
|
+
mode, and then assert the behaviour we expect using serverspec style tests.
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
|
27
|
+
I assume you have Ruby and `bundler` already installed, because I'm a Ruby
|
28
|
+
developer and this is a Ruby project. If you don't, you'll need to install
|
29
|
+
them now.
|
30
|
+
|
31
|
+
If you don't already have one, create a file called `Gemfile` in the root of
|
32
|
+
your Ansible control repository.
|
33
|
+
|
34
|
+
Add this line to your `Gemfile`:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
gem 'barking_iguana-compound'
|
38
|
+
```
|
39
|
+
|
40
|
+
And then execute:
|
41
|
+
|
42
|
+
$ bundle
|
43
|
+
|
44
|
+
Or install it yourself by running:
|
45
|
+
|
46
|
+
$ gem install barking_iguana-compound
|
47
|
+
|
48
|
+
You'll also need Vagrant, and VirtualBox, installed where you'd like to run
|
49
|
+
your tests.
|
50
|
+
|
51
|
+
## Usage
|
52
|
+
|
53
|
+
Install it, as per above.
|
54
|
+
|
55
|
+
Normally you'll use Compond as part of a Rake task, so include it in a
|
56
|
+
`Rakefile` in the root of your project.
|
57
|
+
|
58
|
+
Tell Compound to define Rake tasks for your compound tests:
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
require 'barking_iguana/compound'
|
62
|
+
BarkingIguana::Compound::TestSuite.define_rake_tasks
|
63
|
+
```
|
64
|
+
|
65
|
+
Now let's define a test managing hosts files. It's a trivial test, but it
|
66
|
+
suffices to demonstrate the capacilities of Compound.
|
67
|
+
|
68
|
+
### Writing a compound test
|
69
|
+
|
70
|
+
Create a directory for the test:
|
71
|
+
|
72
|
+
$ mkdir -p test/compound/hosts_file_management
|
73
|
+
|
74
|
+
Each test will have several _stages_, which represent parts of the expected
|
75
|
+
lifecycle of your servers. For example, you set up your servers using Ansible,
|
76
|
+
to you probably have a setup stage. You may be setting up a HA cluster, so
|
77
|
+
perhaps you have a stage where the master in the cluster fails.
|
78
|
+
|
79
|
+
Each stage involves several actions. We'll cover them in detail below, but in
|
80
|
+
summary:
|
81
|
+
|
82
|
+
The `setup` action runs first. For each stage this action decides which virtual
|
83
|
+
machines should be powered on or off during the rest of the stage.
|
84
|
+
|
85
|
+
Next, the `converge` action runs the playbook for that stage, if one is provided
|
86
|
+
by you, to allow things to happen. The playbook may apply roles or kill
|
87
|
+
processes, whatever you need to simulate what's happening to your servers at
|
88
|
+
that part of the lifecycle.
|
89
|
+
|
90
|
+
Finally, the `verify` stage runs some serverspec tests which you will define
|
91
|
+
against the results of the `converge` action. This allows you to check what
|
92
|
+
happens after the tasks in the playbook have been run e.g. you may verify that a
|
93
|
+
standby server has become the master.
|
94
|
+
|
95
|
+
Each stage lives in a directory inside the test. They're executed in
|
96
|
+
alphabetical order, and you are encouraged to prefix each stage with numbers to
|
97
|
+
make it very clear which one should execute in which order.
|
98
|
+
|
99
|
+
We'll cover each of those actions in greater detail now, for the setup stage of
|
100
|
+
our hosts file example.
|
101
|
+
|
102
|
+
#### Test Stages
|
103
|
+
|
104
|
+
Start by creating the directory for that stage:
|
105
|
+
|
106
|
+
$ mkdir -p test/compound/hosts_file_management/000-setup
|
107
|
+
|
108
|
+
##### The Setup Action
|
109
|
+
|
110
|
+
This action controls which virtual machines are available for you to work with,
|
111
|
+
by looking at an Ansible inventory file in the stage directory and starting all
|
112
|
+
the hosts it finds.
|
113
|
+
|
114
|
+
Let's create a simple inventory file with 2 hosts and 1 group.
|
115
|
+
|
116
|
+
The inventory file for each stage lives in the stage directory in a file called
|
117
|
+
`inventory`, so in our example so for that's `test/compound/hosts_file_management/000-setup/inventory`.
|
118
|
+
|
119
|
+
These are normal Ansible inventory files:
|
120
|
+
|
121
|
+
```
|
122
|
+
[all:children]
|
123
|
+
web
|
124
|
+
application
|
125
|
+
database
|
126
|
+
|
127
|
+
[web]
|
128
|
+
host001
|
129
|
+
|
130
|
+
[application]
|
131
|
+
host002
|
132
|
+
|
133
|
+
[database]
|
134
|
+
host003
|
135
|
+
```
|
136
|
+
|
137
|
+
When the setup action for a stage with this inventory is run, Compound will
|
138
|
+
launch three virtual machines for you to test with, `host001`, `host002`, and
|
139
|
+
`host003`. It will assign them IP addresses in the `10.8.42/24` range.
|
140
|
+
|
141
|
+
##### The Converge Action
|
142
|
+
|
143
|
+
Now that virtual machines are available to use, the converge action can run
|
144
|
+
the playbook for your stage against them.
|
145
|
+
|
146
|
+
Playbooks live in the stage directory, in a file called `playbook.yml`. I'm
|
147
|
+
inventive like that. For our example stage that's `test/compound/hosts_file_management/000-setup/playbook.yml`.
|
148
|
+
|
149
|
+
For our setup action we'll apply the `hosts` role to all hosts.
|
150
|
+
|
151
|
+
```
|
152
|
+
---
|
153
|
+
- hosts: all
|
154
|
+
roles:
|
155
|
+
- hosts
|
156
|
+
```
|
157
|
+
|
158
|
+
After a converge, Compound will attempt to verify whatever you ask it to.
|
159
|
+
Onwards, to the verify action.
|
160
|
+
|
161
|
+
##### The Verify Action
|
162
|
+
|
163
|
+
Now we run automated tests to assert that our virtual machines behave like we
|
164
|
+
think they should, after the stage playbook has been applied in the converge
|
165
|
+
action.
|
166
|
+
|
167
|
+
The verify action is based around serverspec tests, arranged around the name of
|
168
|
+
the host which they test.
|
169
|
+
|
170
|
+
For example, to test `host001` we'd create tests under a `test/compound/hosts_file_management/host001/`
|
171
|
+
directory. Each test file must be suffixed with `_spec.rb`. A simple example
|
172
|
+
would be checking that the other host of the pair is resolvable now that the
|
173
|
+
hosts role has been applied to the hosts:
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
# test/compound/hosts_file_management/host001/resolving_spec.rb
|
177
|
+
describe host('host002') do
|
178
|
+
it "is resolvable" do
|
179
|
+
expect(subject).to be_resolvable
|
180
|
+
end
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
# test/compound/hosts_file_management/host002/resolving_spec.rb
|
186
|
+
describe host('host001') do
|
187
|
+
it "is resolvable" do
|
188
|
+
expect(subject).to be_resolvable
|
189
|
+
end
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
193
|
+
Compound will take the appropriate actions to make sure your tests are run on
|
194
|
+
the correct hosts, you don't need to worry about SSH keys, passwords or ports.
|
195
|
+
|
196
|
+
### Running the tests
|
197
|
+
|
198
|
+
Since we've asked Compound to define rake tasks above, we can run those. The
|
199
|
+
tasks generated are based on the directory names we use in the tests. The above
|
200
|
+
test can be run like this:
|
201
|
+
|
202
|
+
$ bundle exec rake compound:hosts_file_management
|
203
|
+
|
204
|
+
You can see a list of all tests by asking Rake to list them:
|
205
|
+
|
206
|
+
$ bundle exec rake -T
|
207
|
+
|
208
|
+
## Development
|
209
|
+
|
210
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
211
|
+
`rake spec` to run the tests. You can also run `bin/console` for an interactive
|
212
|
+
prompt that will allow you to experiment.
|
213
|
+
|
214
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
215
|
+
release a new version, update the version number in `version.rb`, and then run
|
216
|
+
`bundle exec rake release`, which will create a git tag for the version, push
|
217
|
+
git commits and tags, and push the `.gem` file to [rubygems.org][0].
|
218
|
+
|
219
|
+
## Contributing
|
220
|
+
|
221
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/barkingiguana/compound.
|
222
|
+
|
223
|
+
If you'd like to contribute features, please do discuss them by opening an issue on GitHub.
|
224
|
+
|
225
|
+
Some things I'd like to tackle are listed in [docs/TODO.md][1].
|
226
|
+
|
227
|
+
Please keep [docs/CHANGELOG.md][2] updated as you add/remove/change things.
|
228
|
+
|
229
|
+
|
230
|
+
[0]: https://rubygems.org
|
231
|
+
[1]: http://barkingiguana.com/compound/TODO
|
232
|
+
[2]: http://barkingiguana.com/compound/CHANGELOG
|
data/exe/compound
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module BarkingIguana
|
2
|
+
module Compound
|
3
|
+
module Ansible
|
4
|
+
class InventoryWriter
|
5
|
+
attr_accessor :path
|
6
|
+
private :path=
|
7
|
+
|
8
|
+
attr_accessor :hosts
|
9
|
+
private :hosts=, :hosts
|
10
|
+
|
11
|
+
def initialize path = nil
|
12
|
+
self.path = path || generate_random_filename
|
13
|
+
self.hosts = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_host host
|
17
|
+
hosts << host
|
18
|
+
hosts.uniq! &:ip_address
|
19
|
+
end
|
20
|
+
|
21
|
+
def generate_random_filename
|
22
|
+
name = [
|
23
|
+
'inventory',
|
24
|
+
Process.pid,
|
25
|
+
Time.now.to_i,
|
26
|
+
rand(9_999_999_999).to_s.rjust(10, '0')
|
27
|
+
].join('-')
|
28
|
+
end
|
29
|
+
|
30
|
+
def write_file
|
31
|
+
File.open path, 'w' do |inventory|
|
32
|
+
hosts.sort_by(&:name).each do |h|
|
33
|
+
inventory.puts "#{h.inventory_name} ansible_host=#{h.ip_address} ansible_user=#{h.ssh_username} ansible_ssh_private_key_path=#{h.ssh_key} ansible_ssh_extra_args=\"#{h.ssh_extra_args}\""
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -2,13 +2,14 @@ module BarkingIguana
|
|
2
2
|
module Compound
|
3
3
|
module Ansible
|
4
4
|
class Playbook
|
5
|
-
attr_accessor :file, :
|
5
|
+
attr_accessor :file, :inventory_paths, :limit_pattern, :run_from, :user_name, :private_key_file, :io, :output_verbosity, :show_diff
|
6
6
|
|
7
7
|
def initialize file, run_from: nil
|
8
8
|
self.file = file
|
9
9
|
self.run_from = run_from
|
10
10
|
self.output_verbosity = 0
|
11
11
|
self.show_diff = false
|
12
|
+
self.inventory_paths = []
|
12
13
|
end
|
13
14
|
|
14
15
|
def verbosity n
|
@@ -27,7 +28,7 @@ module BarkingIguana
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def inventory name
|
30
|
-
self.
|
31
|
+
self.inventory_paths << name
|
31
32
|
self
|
32
33
|
end
|
33
34
|
|
@@ -67,17 +68,13 @@ module BarkingIguana
|
|
67
68
|
|
68
69
|
# TODO: Symlink the playbook to wrapper_playbook.path
|
69
70
|
# so we can use the group_vars, host_vars, etc.
|
70
|
-
def
|
71
|
+
def playbook_paths
|
71
72
|
return file unless run_from
|
72
73
|
tempfile = wrapper_playbook
|
73
74
|
FileUtils.symlink file, tempfile
|
74
75
|
tempfile
|
75
76
|
end
|
76
77
|
|
77
|
-
def wrapper_playbook_content
|
78
|
-
"---\n- include: #{file}"
|
79
|
-
end
|
80
|
-
|
81
78
|
def wrapper_playbook
|
82
79
|
@wrapper_playbook ||= begin
|
83
80
|
name = [
|
@@ -91,13 +88,16 @@ module BarkingIguana
|
|
91
88
|
end
|
92
89
|
|
93
90
|
def command
|
94
|
-
c = "env ANSIBLE_RETRY_FILES_ENABLED=no ansible-playbook #{
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
c << "
|
99
|
-
c << "
|
100
|
-
c
|
91
|
+
c = ["env ANSIBLE_RETRY_FILES_ENABLED=no ansible-playbook #{playbook_paths}"]
|
92
|
+
inventory_paths.each do |i|
|
93
|
+
c << "-i #{i}"
|
94
|
+
end
|
95
|
+
c << "-l #{limit_pattern}," unless limit_pattern.nil?
|
96
|
+
c << "-u #{user_name}" unless user_name.nil?
|
97
|
+
c << "-#{'v' * output_verbosity}" if output_verbosity > 0
|
98
|
+
c << "--diff" if show_diff
|
99
|
+
c << "--private-key=#{private_key_file}" unless private_key_file.nil?
|
100
|
+
c.join ' '
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module BarkingIguana
|
2
|
+
module Compound
|
3
|
+
class CommandLineClient
|
4
|
+
attr_accessor :argv
|
5
|
+
private :argv=, :argv
|
6
|
+
|
7
|
+
def initialize argv
|
8
|
+
self.argv = argv
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
# TODO: Make it possible to select the test to run if desired
|
13
|
+
test_suite.run
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_suite
|
17
|
+
@test_suite ||= TestSuite.new('test/compound', control_directory: working_directory)
|
18
|
+
end
|
19
|
+
|
20
|
+
def working_directory
|
21
|
+
Dir.pwd
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,14 +1,27 @@
|
|
1
1
|
module BarkingIguana
|
2
2
|
module Compound
|
3
3
|
class Host
|
4
|
-
attr_accessor :
|
4
|
+
attr_accessor :inventory_name, :ip_address, :state
|
5
5
|
|
6
|
-
def initialize(name:,
|
7
|
-
self.
|
8
|
-
self.
|
6
|
+
def initialize(name:, ip_address:)
|
7
|
+
self.inventory_name = name
|
8
|
+
self.ip_address = ip_address
|
9
9
|
self.state = 'unknown'
|
10
10
|
end
|
11
11
|
|
12
|
+
def name
|
13
|
+
@name ||= inventory_name.gsub(/[^a-z0-9\-]/, '-')
|
14
|
+
end
|
15
|
+
|
16
|
+
def <=> other
|
17
|
+
name <=> other.name
|
18
|
+
end
|
19
|
+
include Comparable
|
20
|
+
|
21
|
+
def assign_ip_address new_ip_address
|
22
|
+
self.ip_address = new_ip_address
|
23
|
+
end
|
24
|
+
|
12
25
|
def ssh_key
|
13
26
|
"#{ENV['HOME']}/.vagrant.d/insecure_private_key"
|
14
27
|
end
|
@@ -16,6 +29,10 @@ module BarkingIguana
|
|
16
29
|
def ssh_username
|
17
30
|
'vagrant'
|
18
31
|
end
|
32
|
+
|
33
|
+
def ssh_extra_args
|
34
|
+
'-o StrictHostKeyChecking=no'
|
35
|
+
end
|
19
36
|
end
|
20
37
|
end
|
21
38
|
end
|
@@ -12,9 +12,6 @@ module BarkingIguana
|
|
12
12
|
|
13
13
|
def initialize hosts = [], implementation_options = {}
|
14
14
|
self.hosts = hosts
|
15
|
-
if hosts.any? { |h| h.uri !~ /^10\.8\./ }
|
16
|
-
raise "Your hosts must be in the 10.8/16 CIDR to be managed by me"
|
17
|
-
end
|
18
15
|
self.implementation = Vagrant.new self, implementation_options
|
19
16
|
implementation.prepare
|
20
17
|
end
|
@@ -20,17 +20,13 @@ module BarkingIguana
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def stages
|
23
|
-
return [ TestStage.new(self, directory) ] if simple_test?
|
24
23
|
Dir[directory + '/*'].select { |d| File.directory? d }.map { |s| TestStage.new self, File.basename(s) }
|
25
24
|
end
|
26
25
|
|
27
|
-
def simple_test?
|
28
|
-
File.exists? "#{directory}/playbook.yml"
|
29
|
-
end
|
30
|
-
|
31
26
|
def run
|
32
27
|
benchmark "test #{name}" do
|
33
28
|
begin
|
29
|
+
logger.debug { "#{name}: found #{stages.size} stages: #{stages.map(&:name).map(&:inspect).join(', ')}" }
|
34
30
|
stages.each &:run
|
35
31
|
ensure
|
36
32
|
teardown
|
@@ -60,7 +56,11 @@ module BarkingIguana
|
|
60
56
|
end
|
61
57
|
|
62
58
|
def host_manager
|
63
|
-
@host_manger ||=
|
59
|
+
@host_manger ||= begin
|
60
|
+
# FIXME: Implement uniqueness operators on Host
|
61
|
+
hosts = stages.map(&:hosts).flatten.uniq(&:ip_address).sort
|
62
|
+
HostManager.new(hosts, driver_options)
|
63
|
+
end
|
64
64
|
end
|
65
65
|
|
66
66
|
def driver_options
|
@@ -76,8 +76,7 @@ module BarkingIguana
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def hosts
|
79
|
-
|
80
|
-
stages.map(&:hosts).flatten.uniq { |h| h.uri }
|
79
|
+
host_manager.all
|
81
80
|
end
|
82
81
|
end
|
83
82
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module BarkingIguana
|
2
2
|
module Compound
|
3
3
|
class TestStage
|
4
|
-
attr_accessor :test, :
|
4
|
+
attr_accessor :test, :directory
|
5
5
|
|
6
|
-
def initialize test,
|
6
|
+
def initialize test, directory
|
7
7
|
self.test = test
|
8
|
-
self.
|
8
|
+
self.directory = directory
|
9
9
|
end
|
10
10
|
|
11
11
|
def actions
|
@@ -26,34 +26,68 @@ module BarkingIguana
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def name
|
29
|
-
|
30
|
-
stage_directory
|
29
|
+
directory
|
31
30
|
end
|
32
31
|
|
33
32
|
def display_name
|
34
|
-
|
35
|
-
test.name + ' stage ' + stage_directory
|
33
|
+
test.name + ' stage ' + directory
|
36
34
|
end
|
37
35
|
|
38
36
|
def inventory_path
|
39
|
-
|
37
|
+
@inventory_path ||= stage_file_with_fallback('inventory')
|
40
38
|
end
|
41
39
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
40
|
+
def stage_directory
|
41
|
+
File.expand_path directory, test.directory
|
42
|
+
end
|
43
|
+
|
44
|
+
def stage_file_with_fallback file_name
|
45
|
+
logger.debug { "Searching for #{file_name.inspect}" }
|
46
|
+
stage_file = File.expand_path file_name, stage_directory
|
47
|
+
logger.debug { "Checking #{stage_file.inspect}" }
|
48
|
+
if File.exists? stage_file
|
49
|
+
logger.debug { "Found #{file_name.inspect} at #{stage_file.inspect}" }
|
50
|
+
return stage_file
|
51
|
+
end
|
52
|
+
test_file = File.expand_path file_name, test.directory
|
53
|
+
logger.debug { "Assuming it'll be at #{test_file.inspect}" }
|
54
|
+
test_file
|
45
55
|
end
|
46
56
|
|
47
57
|
def playbook_path
|
48
|
-
|
58
|
+
@playbook_path ||= stage_file_with_fallback('playbook.yml')
|
49
59
|
end
|
50
60
|
|
51
|
-
def
|
61
|
+
def original_inventory
|
52
62
|
Ansible.inventory inventory_path
|
53
63
|
end
|
54
64
|
|
65
|
+
def generated_inventory
|
66
|
+
@generated_inventory ||= generate_inventory
|
67
|
+
end
|
68
|
+
|
69
|
+
def generate_inventory
|
70
|
+
benchmark "#{name}: generating inventory for test stage" do
|
71
|
+
Dir.mktmpdir('inventory').tap do |d|
|
72
|
+
logger.debug { "#{name}: inventory directory is #{d.inspect}" }
|
73
|
+
connection_file = File.expand_path 'connection', d
|
74
|
+
Ansible::InventoryWriter.new(connection_file).tap do |i|
|
75
|
+
benchmark "#{name}: generating connection inventory at #{connection_file}" do
|
76
|
+
test.hosts.each do |h|
|
77
|
+
i.add_host h
|
78
|
+
end
|
79
|
+
i.write_file
|
80
|
+
end
|
81
|
+
end
|
82
|
+
original_file = File.expand_path 'original', d
|
83
|
+
logger.debug { "#{name}: copying original inventory to #{original_file} from #{inventory_path}" }
|
84
|
+
FileUtils.copy inventory_path, original_file
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
55
89
|
def hosts
|
56
|
-
|
90
|
+
original_inventory.hosts
|
57
91
|
end
|
58
92
|
|
59
93
|
def control_directory
|
@@ -61,7 +95,7 @@ module BarkingIguana
|
|
61
95
|
end
|
62
96
|
|
63
97
|
def playbook
|
64
|
-
Ansible.playbook(playbook_path, run_from: control_directory).inventory(
|
98
|
+
Ansible.playbook(playbook_path, run_from: control_directory).inventory(generated_inventory).stream_to(logger).verbosity(2).diff
|
65
99
|
end
|
66
100
|
|
67
101
|
def suite
|
@@ -69,9 +103,9 @@ module BarkingIguana
|
|
69
103
|
end
|
70
104
|
|
71
105
|
def setup
|
72
|
-
desired_hosts =
|
106
|
+
desired_hosts = original_inventory.hosts.sort.map(&:name)
|
73
107
|
logger.debug { "Desired hosts for #{display_name}: #{desired_hosts.join(', ')}" }
|
74
|
-
active_hosts = host_manager.active.map
|
108
|
+
active_hosts = host_manager.active.sort.map(&:name)
|
75
109
|
logger.debug { "Active hosts for #{display_name}: #{active_hosts.join(', ')}" }
|
76
110
|
hosts_to_launch = desired_hosts - active_hosts
|
77
111
|
logger.debug { "Launch hosts for #{display_name}: #{hosts_to_launch.join(', ')}" }
|
@@ -86,8 +120,14 @@ module BarkingIguana
|
|
86
120
|
end
|
87
121
|
|
88
122
|
def converge
|
89
|
-
|
123
|
+
unless File.exists? playbook_path
|
124
|
+
logger.debug { "Not running anything because #{playbook_path.inspect} does not exist" }
|
125
|
+
return
|
126
|
+
end
|
90
127
|
playbook.run
|
128
|
+
ensure
|
129
|
+
logger.debug { "Removing generated inventory from #{generated_inventory}" }
|
130
|
+
# FileUtils.rm_r generated_inventory
|
91
131
|
end
|
92
132
|
|
93
133
|
def verify
|
@@ -1,6 +1,10 @@
|
|
1
1
|
module BarkingIguana
|
2
2
|
module Compound
|
3
3
|
class TestSuite
|
4
|
+
def self.define_rake_tasks
|
5
|
+
new.define_rake_tasks
|
6
|
+
end
|
7
|
+
|
4
8
|
def define_rake_tasks
|
5
9
|
Rake::Task.define_task name do
|
6
10
|
run
|
@@ -11,29 +15,20 @@ module BarkingIguana
|
|
11
15
|
test.run
|
12
16
|
end.add_description "Run #{test.name} test from #{name} suite"
|
13
17
|
|
14
|
-
Rake::Task.define_task "#{name}:#{test.name}:destroy" do
|
15
|
-
test.teardown
|
16
|
-
end.add_description "Tear down #{test.name} test from #{name} suite"
|
17
|
-
|
18
|
-
test.stages.each do |stage|
|
19
|
-
stage.actions.each do |action|
|
20
|
-
Rake::Task.define_task "#{name}:#{test.name}:#{action}" do
|
21
|
-
stage.public_send action
|
22
|
-
end.add_description "Run action #{action} of the #{test.name} test from #{name} suite"
|
23
|
-
end
|
24
|
-
end if test.simple_test?
|
25
|
-
|
26
18
|
test.stages.each do |stage|
|
27
19
|
Rake::Task.define_task "#{name}:#{test.name}:#{stage.name}" do
|
28
20
|
stage.run
|
29
|
-
end.add_description "Run
|
30
|
-
|
21
|
+
end.add_description "Run #{stage.name} stage of the #{test.name} test from #{name} suite"
|
31
22
|
stage.actions.each do |action|
|
32
23
|
Rake::Task.define_task "#{name}:#{test.name}:#{stage.name}:#{action}" do
|
33
24
|
stage.public_send action
|
34
|
-
end.add_description "Run action #{action}
|
25
|
+
end.add_description "Run action #{action} for #{stage.name} stage of the #{test.name} test from #{name} suite"
|
35
26
|
end
|
36
|
-
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Rake::Task.define_task "#{name}:#{test.name}:destroy" do
|
30
|
+
test.teardown
|
31
|
+
end.add_description "Tear down #{test.name} test from #{name} suite"
|
37
32
|
end
|
38
33
|
end
|
39
34
|
|
@@ -43,9 +38,17 @@ module BarkingIguana
|
|
43
38
|
attr_accessor :directory
|
44
39
|
private :directory=
|
45
40
|
|
46
|
-
def initialize
|
47
|
-
self.
|
48
|
-
self.
|
41
|
+
def initialize(directory = nil, control_directory: nil)
|
42
|
+
self.control_directory = control_directory || guess_directory
|
43
|
+
self.directory = directory || File.expand_path("test/compound", control_directory)
|
44
|
+
end
|
45
|
+
|
46
|
+
def guess_directory
|
47
|
+
caller.detect do |trace|
|
48
|
+
path = trace.split(/:/, 2)[0]
|
49
|
+
file = File.basename path
|
50
|
+
break File.dirname path if file == 'Rakefile'
|
51
|
+
end
|
49
52
|
end
|
50
53
|
|
51
54
|
def tests
|
@@ -42,13 +42,26 @@ class Vagrant
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
45
|
+
def write_vagrant_file
|
46
46
|
logger.debug { "Writing Vagrantfile to #{vagrant_file_path}" }
|
47
47
|
File.open vagrant_file_path, 'w' do |f|
|
48
48
|
f.puts vagrant_file_content
|
49
49
|
end
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
|
+
def assign_ip_addresses
|
53
|
+
hosts.each do |h|
|
54
|
+
next if valid_ip_address? h.ip_address
|
55
|
+
ip_address = next_available_ip_address
|
56
|
+
logger.debug { "Assigning #{h.name} an IP address: #{ip_address}" }
|
57
|
+
h.assign_ip_address ip_address
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def prepare
|
62
|
+
assign_ip_addresses
|
63
|
+
write_vagrant_file
|
64
|
+
end
|
52
65
|
|
53
66
|
def vagrant_file_content
|
54
67
|
ERB.new(vagrant_file_template).result binding
|
@@ -73,7 +86,25 @@ class Vagrant
|
|
73
86
|
end
|
74
87
|
end
|
75
88
|
|
89
|
+
private
|
90
|
+
|
76
91
|
def hosts
|
77
92
|
manager.hosts
|
78
93
|
end
|
94
|
+
|
95
|
+
def next_available_ip_address
|
96
|
+
unassigned_ip_addresses.shift
|
97
|
+
end
|
98
|
+
|
99
|
+
def valid_ip_address? ip_address
|
100
|
+
ip_address =~ /^10\.8\./
|
101
|
+
end
|
102
|
+
|
103
|
+
def unassigned_ip_addresses
|
104
|
+
@unassigned_ip_addresses ||= begin
|
105
|
+
assignable_ip_addresses = (10..200).to_a.map { |dd| "10.8.42.#{dd}" }
|
106
|
+
assigned_ip_addresses = hosts.map(&:ip_address)
|
107
|
+
assignable_ip_addresses - assigned_ip_addresses
|
108
|
+
end
|
109
|
+
end
|
79
110
|
end
|
@@ -11,12 +11,15 @@ require 'barking_iguana/compound/version'
|
|
11
11
|
require 'barking_iguana/compound/ansible'
|
12
12
|
require 'barking_iguana/compound/ansible/inventory'
|
13
13
|
require 'barking_iguana/compound/ansible/inventory_parser'
|
14
|
+
require 'barking_iguana/compound/ansible/inventory_writer'
|
14
15
|
require 'barking_iguana/compound/ansible/playbook'
|
16
|
+
require 'barking_iguana/compound/command_line_client'
|
17
|
+
require 'barking_iguana/compound/host_manager'
|
18
|
+
require 'barking_iguana/compound/host'
|
19
|
+
require 'barking_iguana/compound/server_spec'
|
15
20
|
require 'barking_iguana/compound/test_stage'
|
16
21
|
require 'barking_iguana/compound/test'
|
17
22
|
require 'barking_iguana/compound/test_suite'
|
18
|
-
require 'barking_iguana/compound/host_manager'
|
19
|
-
require 'barking_iguana/compound/host'
|
20
23
|
require 'barking_iguana/compound/vagrant'
|
21
24
|
|
22
25
|
module BarkingIguana
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: barking_iguana-compound
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Craig R Webster
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-12-
|
11
|
+
date: 2016-12-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -167,7 +167,8 @@ dependencies:
|
|
167
167
|
description: Compound testing of Ansible playbooks
|
168
168
|
email:
|
169
169
|
- craig@barkingiguana.com
|
170
|
-
executables:
|
170
|
+
executables:
|
171
|
+
- compound
|
171
172
|
extensions: []
|
172
173
|
extra_rdoc_files: []
|
173
174
|
files:
|
@@ -179,11 +180,18 @@ files:
|
|
179
180
|
- barking_iguana-compound.gemspec
|
180
181
|
- bin/console
|
181
182
|
- bin/setup
|
183
|
+
- docs/CHANGELOG.md
|
184
|
+
- docs/TODO.md
|
185
|
+
- docs/_config.yml
|
186
|
+
- docs/index.md
|
187
|
+
- exe/compound
|
182
188
|
- lib/barking_iguana/compound.rb
|
183
189
|
- lib/barking_iguana/compound/ansible.rb
|
184
190
|
- lib/barking_iguana/compound/ansible/inventory.rb
|
185
191
|
- lib/barking_iguana/compound/ansible/inventory_parser.rb
|
192
|
+
- lib/barking_iguana/compound/ansible/inventory_writer.rb
|
186
193
|
- lib/barking_iguana/compound/ansible/playbook.rb
|
194
|
+
- lib/barking_iguana/compound/command_line_client.rb
|
187
195
|
- lib/barking_iguana/compound/host.rb
|
188
196
|
- lib/barking_iguana/compound/host_manager.rb
|
189
197
|
- lib/barking_iguana/compound/server_spec.rb
|