kitchen-vmpool 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.DS_Store +0 -0
- data/.gitignore +3 -1
- data/.kitchen.yml +30 -0
- data/CHANGELOG.md +4 -1
- data/Gemfile.lock +77 -0
- data/README.md +166 -36
- data/Rakefile +7 -0
- data/lib/kitchen/driver/vmpool.rb +11 -7
- data/lib/kitchen/driver/vmpool_stores/base_store.rb +17 -4
- data/lib/kitchen/driver/vmpool_stores/file_base_store.rb +33 -20
- data/lib/kitchen/driver/vmpool_stores/file_store.rb +3 -0
- data/lib/kitchen/driver/vmpool_stores/vmpooler_store.rb +147 -0
- data/lib/kitchen-vmpool/version.rb +2 -2
- data/vmpool.yaml +1 -2
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a292b2bf0b4134c9da983f663e16acbaa2f9582f
|
4
|
+
data.tar.gz: 1b41ec5d409d1fb0446036b1368a62b6354bfab2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8dd58b8dff118a7a95c3cee1e41859a3196eb2fb05d72cf04e0d105001cb05f68598b6d28af4d3d08c30e388d1c16632ff171abe373bb711e4e9bc88301b497
|
7
|
+
data.tar.gz: 7b934a2d02593fc3ba9942b3e16e90e342079b61c5fcf8ab204377eed615a405a81eba81ce17eb15c54af90f0712660af4b8dd62ad7ddfadb8073dabfdde1f9d
|
data/.DS_Store
ADDED
Binary file
|
data/.gitignore
CHANGED
data/.kitchen.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
|
3
|
+
# Example kitchen file configured to use vmpooler
|
4
|
+
# if using this please ensure you have vmpooler running on localhost:8080
|
5
|
+
# You can start a dummy instance via docker
|
6
|
+
# docker run -e VMPOOLER_DEBUG=true -p 8080:4567 \
|
7
|
+
# -e VMPOOLER_LOG='/var/log/vmpooler/vmpooler.log' -it --rm --name pooler nwops/vmpooler
|
8
|
+
driver:
|
9
|
+
name: vmpool
|
10
|
+
state_store: vmpooler
|
11
|
+
store_options:
|
12
|
+
user: 'jdoe'
|
13
|
+
pass: 'jdoe123'
|
14
|
+
token: 'token'
|
15
|
+
host_url: 'http://localhost:8080'
|
16
|
+
platforms:
|
17
|
+
- name: debian-7-x86_64
|
18
|
+
driver:
|
19
|
+
pool_name: debian-7-x86_64
|
20
|
+
- name: debian-7-i386
|
21
|
+
driver:
|
22
|
+
pool_name: debian-7-i386
|
23
|
+
|
24
|
+
provisioner:
|
25
|
+
name: shell
|
26
|
+
|
27
|
+
suites:
|
28
|
+
- name: default
|
29
|
+
run_list:
|
30
|
+
attributes:
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## 0.4.0
|
6
|
+
* Adds vmpooler store for the ultimate in pool stores
|
7
|
+
|
5
8
|
## v0.3.2
|
6
9
|
* Destroy process does not output correct hostname
|
7
10
|
* Delete old instances from used_instances pool
|
@@ -18,4 +21,4 @@
|
|
18
21
|
|
19
22
|
## v0.2.0
|
20
23
|
* adds a gitlab commit store
|
21
|
-
* Fix accounting bugs with used and pool instances
|
24
|
+
* Fix accounting bugs with used and pool instances
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
kitchen-vmpool (0.4.0)
|
5
|
+
gitlab (~> 4.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
artifactory (2.8.2)
|
11
|
+
coderay (1.1.1)
|
12
|
+
diff-lcs (1.3)
|
13
|
+
gitlab (4.2.0)
|
14
|
+
httparty
|
15
|
+
terminal-table
|
16
|
+
httparty (0.15.6)
|
17
|
+
multi_xml (>= 0.5.2)
|
18
|
+
method_source (0.8.2)
|
19
|
+
mixlib-install (2.1.12)
|
20
|
+
artifactory
|
21
|
+
mixlib-shellout
|
22
|
+
mixlib-versioning
|
23
|
+
thor
|
24
|
+
mixlib-shellout (2.3.2)
|
25
|
+
mixlib-versioning (1.2.2)
|
26
|
+
multi_xml (0.6.0)
|
27
|
+
net-scp (1.2.1)
|
28
|
+
net-ssh (>= 2.6.5)
|
29
|
+
net-ssh (4.1.0)
|
30
|
+
net-ssh-gateway (1.3.0)
|
31
|
+
net-ssh (>= 2.6.5)
|
32
|
+
pry (0.10.4)
|
33
|
+
coderay (~> 1.1.0)
|
34
|
+
method_source (~> 0.8.1)
|
35
|
+
slop (~> 3.4)
|
36
|
+
rake (12.0.0)
|
37
|
+
rspec (3.6.0)
|
38
|
+
rspec-core (~> 3.6.0)
|
39
|
+
rspec-expectations (~> 3.6.0)
|
40
|
+
rspec-mocks (~> 3.6.0)
|
41
|
+
rspec-core (3.6.0)
|
42
|
+
rspec-support (~> 3.6.0)
|
43
|
+
rspec-expectations (3.6.0)
|
44
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
45
|
+
rspec-support (~> 3.6.0)
|
46
|
+
rspec-mocks (3.6.0)
|
47
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
48
|
+
rspec-support (~> 3.6.0)
|
49
|
+
rspec-support (3.6.0)
|
50
|
+
safe_yaml (1.0.4)
|
51
|
+
slop (3.6.0)
|
52
|
+
terminal-table (1.8.0)
|
53
|
+
unicode-display_width (~> 1.1, >= 1.1.1)
|
54
|
+
test-kitchen (1.16.0)
|
55
|
+
mixlib-install (>= 1.2, < 3.0)
|
56
|
+
mixlib-shellout (>= 1.2, < 3.0)
|
57
|
+
net-scp (~> 1.1)
|
58
|
+
net-ssh (>= 2.9, < 5.0)
|
59
|
+
net-ssh-gateway (~> 1.2)
|
60
|
+
safe_yaml (~> 1.0)
|
61
|
+
thor (~> 0.19, < 0.19.2)
|
62
|
+
thor (0.19.1)
|
63
|
+
unicode-display_width (1.3.0)
|
64
|
+
|
65
|
+
PLATFORMS
|
66
|
+
ruby
|
67
|
+
|
68
|
+
DEPENDENCIES
|
69
|
+
gitlab
|
70
|
+
kitchen-vmpool!
|
71
|
+
pry
|
72
|
+
rake
|
73
|
+
rspec
|
74
|
+
test-kitchen
|
75
|
+
|
76
|
+
BUNDLED WITH
|
77
|
+
1.15.4
|
data/README.md
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
# Kitchen
|
1
|
+
# Kitchen-Vmpool
|
2
2
|
|
3
3
|
Ever wished you didn't have to wait for test-kitchen to create a test node? With kitchen-vmpool you can create your test nodes
|
4
4
|
ahead of time and simply populate the pool with the list of hostnames. During the create step test kitchen will select a member
|
5
5
|
of the test pool and you will instantly have a system to work with.
|
6
6
|
|
7
7
|
Kitchen-vmpool allows external scripts or programs to populate the pool so it is not tied to any single vm/container technology.
|
8
|
-
Additionaly, kitchen-vmpool contains a pluggable storage backend so that you can store the pool information in just about anything.
|
8
|
+
Additionaly, kitchen-vmpool contains a pluggable storage backend so that you can store the pool information in just about anything.
|
9
|
+
|
10
|
+
Increase your perceived test kitchen host creation time by 200x!
|
9
11
|
|
10
12
|
## Installation
|
11
13
|
|
@@ -24,13 +26,37 @@ Or install it yourself as:
|
|
24
26
|
|
25
27
|
## Usage
|
26
28
|
|
27
|
-
To use setup the kitchen driver to use the vmpool store and configure one of the stores if required.
|
29
|
+
To use setup the kitchen driver to use the vmpool store and configure one of the stores if required.
|
30
|
+
|
31
|
+
## Intro
|
32
|
+
### Adding members to the pool
|
33
|
+
Kitchen-vmpool is agnostic to any vm/container technology. This means you can use whatever provisioning mechanism you need to create a system or container.
|
34
|
+
However, it is expected that you will be provisioning hosts outside of your normal test-kitchen workflow.
|
35
|
+
So all those cool fancy kitchen driver plugins cannot be used with vmpool. But if you are reading this, that means your drivers take too long to spin up
|
36
|
+
an instance anyways. So this means that you will need an external process that spins up instances.
|
37
|
+
|
38
|
+
In fact there is a tool that helps immensely with this already: [VMPooler](https://github.com/puppetlabs/vmpooler)
|
39
|
+
|
40
|
+
As I mentioned about you can use vmpooler or create a script to initially and constly generate your pool members.
|
41
|
+
Expect a scheduled/cron job to constant keep pool members in the pool.
|
42
|
+
|
43
|
+
An example working script I created for usage with VMware VRA can be found in the exe folder of this gem. However, I will
|
44
|
+
later be transitioning to [VMPooler](https://github.com/puppetlabs/vmpooler).
|
45
|
+
|
46
|
+
### State Stores
|
47
|
+
When we are working with vmpools we need a way to store metadata about that pools and how many instances are required and currently available for each pool. The easiest way to do this at first is to put that data in a file.
|
48
|
+
However, most people need things like central locking and management layers in front of that data. So Kitchen-vmpool creates a framework for many other
|
49
|
+
state stores to be used. Vmpool even allows the user to create their own state store and package as a separate gem.
|
50
|
+
Test kitchen will use these state stores to get members of the pools in order to test against.
|
51
|
+
|
52
|
+
## Currently Available State stores
|
28
53
|
|
29
54
|
### Gitlab Commit Store Example
|
30
55
|
If you have no central place to store files the gitlab_commit store may be an option for you.
|
31
|
-
If you have a simple setup
|
56
|
+
If you have a simple setup this may suffice. If you plan to expand your usage of test kitchen to
|
32
57
|
include multiple parallel test runs, you may run into race conditions due to the lack of a central queue
|
33
58
|
to handle reads and writes. Perfect for simple setups. Later on you can move to a different state store.
|
59
|
+
Due to a gitlab bug I recommend naming your file without a file extension.
|
34
60
|
|
35
61
|
```yaml
|
36
62
|
driver:
|
@@ -40,7 +66,7 @@ driver:
|
|
40
66
|
store_options:
|
41
67
|
pool_file: 'vmpool'
|
42
68
|
project_id: 329302
|
43
|
-
|
69
|
+
|
44
70
|
platforms:
|
45
71
|
- name: rhel6
|
46
72
|
driver:
|
@@ -48,13 +74,14 @@ platforms:
|
|
48
74
|
- name: windows10
|
49
75
|
driver:
|
50
76
|
pool_name: windows10_pool
|
51
|
-
|
77
|
+
|
52
78
|
```
|
53
79
|
|
54
80
|
### Gitlab Snippet Store Example
|
55
81
|
I don't recommend using the snippet store because gitlab's permission's model only allows a single
|
56
82
|
user to make changes. Therefore this is a bad central place to keep things if only a single user can update.
|
57
|
-
Use this as an example to create your own store providers.
|
83
|
+
Use this as an example to create your own store providers. This originally sounded like a good idea, but later turned out
|
84
|
+
to be bad idea due to the permissions model in Gitlab.
|
58
85
|
|
59
86
|
```yaml
|
60
87
|
driver:
|
@@ -65,7 +92,7 @@ driver:
|
|
65
92
|
pool_file: 'vmpool'
|
66
93
|
project_id: 329302
|
67
94
|
snippet_id: 49
|
68
|
-
|
95
|
+
|
69
96
|
platforms:
|
70
97
|
- name: rhel6
|
71
98
|
driver:
|
@@ -85,7 +112,29 @@ driver:
|
|
85
112
|
state_store: file
|
86
113
|
store_options:
|
87
114
|
pool_file: 'vmpool'
|
88
|
-
|
115
|
+
|
116
|
+
platforms:
|
117
|
+
- name: rhel6
|
118
|
+
driver:
|
119
|
+
pool_name: base_rhel6_pool
|
120
|
+
- name: windows10
|
121
|
+
driver:
|
122
|
+
pool_name: windows10_pool
|
123
|
+
```
|
124
|
+
|
125
|
+
### Vmpooler store
|
126
|
+
Consider the Vmpooler state store to be the ultimate backend for kitchen-vmpool. You can now setup
|
127
|
+
[vmpooler](https://github.com/puppetlabs/vmpooler) to continuously regenerate virtual machines in the background.
|
128
|
+
|
129
|
+
```yaml
|
130
|
+
driver:
|
131
|
+
name: vmpool
|
132
|
+
state_store: vmpooler
|
133
|
+
store_options:
|
134
|
+
user: 'jdoe'
|
135
|
+
pass: 'jdoe123'
|
136
|
+
token: 'token'
|
137
|
+
host_url: 'http://localhost:8080'
|
89
138
|
platforms:
|
90
139
|
- name: rhel6
|
91
140
|
driver:
|
@@ -95,8 +144,22 @@ platforms:
|
|
95
144
|
pool_name: windows10_pool
|
96
145
|
```
|
97
146
|
|
98
|
-
|
99
|
-
|
147
|
+
You will notice that the `reuse_instances` and `pool_file` options are not needed in this driver config. However, we do need to supply some new options. The vmpooler store relies on an external service, so we need to provide an address
|
148
|
+
(`host_url`) and authentication credentials (`token` or `user` and `pass`).
|
149
|
+
|
150
|
+
For the platforms you will want to supply the pool_name that matches with your vmpooler's pools.
|
151
|
+
|
152
|
+
You can create a test dummy instance of vmpooler to play around with:
|
153
|
+
|
154
|
+
`docker run -e VMPOOLER_DEBUG=true -p 8080:4567 -e VMPOOLER_LOG='/var/log/vmpooler/vmpooler.log' -it --rm --name pooler nwops/vmpooler`
|
155
|
+
|
156
|
+
Vmpooler should start running on http://localhost:8080
|
157
|
+
|
158
|
+
### File based pool data structure
|
159
|
+
File based state stores require a file to store the data. Duh! In order to have
|
160
|
+
a common format between all the file based state stores you should use the data structure
|
161
|
+
below. If you make your own state store you can do whatever you desire. All state stores in this gem will use the
|
162
|
+
format below.
|
100
163
|
|
101
164
|
```yaml
|
102
165
|
base_rhel6_pool:
|
@@ -105,27 +168,20 @@ base_rhel6_pool:
|
|
105
168
|
pool_instances: []
|
106
169
|
requests: []
|
107
170
|
used_instances: []
|
108
|
-
|
171
|
+
|
109
172
|
windows10_pool:
|
110
173
|
payload_file: windows10_payload.json
|
111
174
|
size: 1
|
112
175
|
pool_instances: []
|
113
176
|
requests: []
|
114
177
|
used_instances: []
|
115
|
-
|
178
|
+
|
116
179
|
```
|
117
180
|
|
118
|
-
The payload_file key is not required and was used for other purposes outside of kitchen-vmpool in order to create the instances.
|
181
|
+
The `payload_file` key is not required and was used for other purposes outside of kitchen-vmpool in order to create the vra instances.
|
119
182
|
It can be expected that some users will throw extra metadata in these pools for their own purposes. So care must be
|
120
|
-
taken to not wipe out this data when creating a store.
|
121
|
-
|
122
|
-
### Puppet's VMpooler
|
123
|
-
Consider the VMpooler state store to be the ultimate backend for kitchen-vmpool. While vmpool doesn't currently support vmpooler
|
124
|
-
it is on the roadmap to support.
|
125
|
-
|
126
|
-
https://github.com/puppetlabs/vmpooler
|
183
|
+
taken to not wipe out this data when creating a new state store.
|
127
184
|
|
128
|
-
Once the vmpooler state store is implemented this kitchen plugin might be pretty popular.
|
129
185
|
|
130
186
|
## Development
|
131
187
|
|
@@ -140,13 +196,13 @@ Puppet's vmpooler will handle the maintenance of the pool state which is probabl
|
|
140
196
|
|
141
197
|
In order to create a new state store you must do the following:
|
142
198
|
|
143
|
-
1.
|
199
|
+
1. Inherit from the BaseStore or a subclass of the BaseStore
|
144
200
|
2. Implement the following methods:
|
145
|
-
* initialize(options = {})
|
201
|
+
* initialize(options = {})
|
146
202
|
* take_pool_member
|
147
|
-
*
|
148
|
-
|
149
|
-
|
203
|
+
* cleanup
|
204
|
+
|
205
|
+
3. All other methods used with your store must be private
|
150
206
|
|
151
207
|
You must be careful to overwrite the entire pool data. It is expected that some users
|
152
208
|
will put other metadata in the pool file for other purposes. So when you write your data
|
@@ -168,15 +224,18 @@ module Kitchen
|
|
168
224
|
return member
|
169
225
|
end
|
170
226
|
|
171
|
-
# @param
|
172
|
-
# @
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
227
|
+
# @param pool_member [String] a VM instance
|
228
|
+
# @param pool_name [String] a VM pool
|
229
|
+
# @param reuse_instances [Boolean] whether or not to mark used VM instances as unused
|
230
|
+
def cleanup(pool_member:, pool_name:, reuse_instances:, &block)
|
231
|
+
used_status = 'used'
|
232
|
+
|
233
|
+
if reuse_instances
|
234
|
+
mark_unused(pool_member, pool_name)
|
235
|
+
used_status = 'unused'
|
177
236
|
end
|
178
|
-
|
179
|
-
|
237
|
+
|
238
|
+
block.call(pool_member, used_status)
|
180
239
|
end
|
181
240
|
end
|
182
241
|
end
|
@@ -191,14 +250,85 @@ You can pass configuration to your store by setting the `store_options` in the d
|
|
191
250
|
that allows the user to pass in required settings if your store requires configuration. It is up to you
|
192
251
|
use these options since not every store will require configuration.
|
193
252
|
|
253
|
+
### Custom State Store Distrubtion
|
254
|
+
If you think many will benefit from your new state store please create a PR and have it merged to the kithen-vmpool core.
|
255
|
+
We want to limit dependencies so if your fancy state store has some dependencies it would probably be best to create your own gem.
|
256
|
+
|
257
|
+
It is easiest to create a gem using bundler. Please use the naming convention kitchen-vmpool-storename when creating your gem.
|
258
|
+
|
259
|
+
1. `bundler gem --test=rspec kitchen-vmpool-fancystore`
|
260
|
+
2. Create a kitchen/driver directory
|
261
|
+
3. Move the vmpool directory underneath kitchen/driver directory
|
262
|
+
4. rename vmpool directory to vmpool_stores
|
263
|
+
```
|
264
|
+
lib
|
265
|
+
└── kitchen
|
266
|
+
└── driver
|
267
|
+
└── vmpool_stores
|
268
|
+
├── fancystore
|
269
|
+
│ └── version.rb
|
270
|
+
└── fancystore.rb --> your code goes in this file
|
271
|
+
|
272
|
+
```
|
273
|
+
|
274
|
+
There is much more to bundling this into a gem but the procedures are the same for every gem. So add gem dependencies if required
|
275
|
+
and be sure to add unit tests for your state store.
|
194
276
|
|
195
277
|
### Development Setup
|
196
278
|
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.
|
197
279
|
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).
|
198
280
|
|
281
|
+
|
282
|
+
## VMware VRA Pool creation scripts
|
283
|
+
I had a need to interface kitchen-vmpool with VRA in order to build catalog items. So I ended up creating some basic scripts that utilize the [vmware-vra gem](https://github.com/chef-partners/vmware-vra-gem)
|
284
|
+
to help build the catalog items. I am also using the gitlab_commit store as my storage backend. Don't ask why it is all I had.
|
285
|
+
|
286
|
+
Warning: this code is very custom for my own needs and utilizes the gitlab_commit store for state storage.
|
287
|
+
|
288
|
+
### Usage
|
289
|
+
|
290
|
+
Gems required:
|
291
|
+
- hashdiff
|
292
|
+
- vmware-vra
|
293
|
+
- gitlab
|
294
|
+
- highline
|
295
|
+
|
296
|
+
Help: `vra_create_pool -h`
|
297
|
+
Create a pool: `vra_create_pool -f vmpool -p 1234`
|
298
|
+
|
299
|
+
Requires the basic file based vmpool file
|
300
|
+
|
301
|
+
```yaml
|
302
|
+
base_rhel6_pool:
|
303
|
+
payload_file: base_rhel6_payload.json
|
304
|
+
size: 1
|
305
|
+
pool_instances: []
|
306
|
+
requests: []
|
307
|
+
used_instances: []
|
308
|
+
|
309
|
+
```
|
310
|
+
|
311
|
+
I have some PRs open to help make using VRA payloads files easier with vmware-vra.
|
312
|
+
|
313
|
+
See:
|
314
|
+
- https://github.com/chef-partners/vmware-vra-gem/pull/57
|
315
|
+
- https://github.com/chef-partners/vmware-vra-gem/pull/56
|
316
|
+
|
317
|
+
You should be able to easily swap out the store for something else too. Just edit the vra_create_pool file here.
|
318
|
+
|
319
|
+
```ruby
|
320
|
+
# @return [Hash] - a store hash that contains one or more pools
|
321
|
+
# @option project_id [Integer] - the project id in gitlab
|
322
|
+
# @option pool_file [String] - the snipppet file name
|
323
|
+
def store(store_options = options)
|
324
|
+
# create a new instance of the store with the provided options
|
325
|
+
@store ||= Kitchen::Driver::VmpoolStores::GitlabCommitStore.new(store_options)
|
326
|
+
end
|
327
|
+
|
328
|
+
```
|
199
329
|
## Contributing
|
200
330
|
|
201
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
331
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/nwops/kitchen-vmpool. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
202
332
|
|
203
333
|
## License
|
204
334
|
|
data/Rakefile
CHANGED
@@ -49,13 +49,18 @@ module Kitchen
|
|
49
49
|
|
50
50
|
# (see Base#destroy)
|
51
51
|
def destroy(state)
|
52
|
-
return
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
52
|
+
return unless state[:hostname]
|
53
|
+
|
54
|
+
opts = {
|
55
|
+
pool_member: state[:hostname],
|
56
|
+
pool_name: config[:pool_name],
|
57
|
+
reuse_instances: config[:reuse_instances],
|
58
|
+
}
|
59
|
+
|
60
|
+
store.cleanup(**opts) do |host, used_status|
|
61
|
+
info("Marking pool member #{host} as #{used_status}")
|
58
62
|
end
|
63
|
+
|
59
64
|
state.delete(:hostname)
|
60
65
|
end
|
61
66
|
|
@@ -79,7 +84,6 @@ module Kitchen
|
|
79
84
|
klass.send(:new, store_opts)
|
80
85
|
end
|
81
86
|
end
|
82
|
-
|
83
87
|
end
|
84
88
|
end
|
85
89
|
end
|
@@ -4,10 +4,22 @@ require 'kitchen'
|
|
4
4
|
module Kitchen
|
5
5
|
module Driver
|
6
6
|
class PoolMemberNotFound < Exception; end
|
7
|
+
class PoolNotFound < Exception; end
|
8
|
+
class PoolIsEmpty < Exception; end
|
9
|
+
class PoolMemberUnavailable < Exception; end
|
10
|
+
class TokenNotCreated < Exception; end
|
11
|
+
class AuthenticationRequired < Exception; end
|
12
|
+
class InvalidCredentials < Exception; end
|
13
|
+
class PoolMemberNotDestroyed < Exception; end
|
14
|
+
class InvalidUrl < Exception; end
|
7
15
|
|
8
16
|
module VmpoolStores
|
9
17
|
class BaseStore
|
10
18
|
|
19
|
+
def initialize(options = {})
|
20
|
+
|
21
|
+
end
|
22
|
+
|
11
23
|
# @return [String] - a random host from the list of systems
|
12
24
|
# mark them used so nobody else can use it
|
13
25
|
# @param pool_name [String] - the name of the pool to yank the memeber from
|
@@ -15,10 +27,11 @@ module Kitchen
|
|
15
27
|
raise NotImplemented
|
16
28
|
end
|
17
29
|
|
18
|
-
# @param
|
19
|
-
# @param pool_name [String] - the name of the pool
|
20
|
-
# @
|
21
|
-
|
30
|
+
# @param pool_member [String] - the name of the VM
|
31
|
+
# @param pool_name [String] - the name of the pool
|
32
|
+
# @param reuse_instances [Boolean] - whether or not the VM should be discarded when used
|
33
|
+
# a callback that executes when a pool member has been run
|
34
|
+
def cleanup(pool_member: nil, pool_name: nil, reuse_instances: false, &block)
|
22
35
|
raise NotImplemented
|
23
36
|
end
|
24
37
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "kitchen/driver/vmpool_stores/base_store"
|
2
|
+
require 'kitchen/logging'
|
2
3
|
|
3
4
|
module Kitchen
|
4
5
|
module Driver
|
@@ -15,18 +16,20 @@ module Kitchen
|
|
15
16
|
member = pool_hosts(pool_name).sample
|
16
17
|
raise Kitchen::Driver::PoolMemberNotFound.new("No pool members exist for #{pool_name}, please create some pool members") unless member
|
17
18
|
mark_used(member, pool_name)
|
18
|
-
|
19
|
+
member
|
19
20
|
end
|
20
21
|
|
21
|
-
# @param
|
22
|
-
# @
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
# @param pool_member [String] a VM instance
|
23
|
+
# @param pool_name [String] a VM pool
|
24
|
+
# @param reuse_instances [Boolean] whether or not to mark used VM instances as unused
|
25
|
+
def cleanup(pool_member:, pool_name:, reuse_instances:)
|
26
|
+
used_status = 'used'
|
27
|
+
|
28
|
+
if reuse_instances
|
29
|
+
mark_unused(pool_member, pool_name)
|
30
|
+
used_status = 'unused'
|
27
31
|
end
|
28
|
-
|
29
|
-
pool_hosts(pool_name)
|
32
|
+
yield(pool_member, used_status) if block_given?
|
30
33
|
end
|
31
34
|
|
32
35
|
def pool_data(refresh = false)
|
@@ -63,6 +66,27 @@ module Kitchen
|
|
63
66
|
|
64
67
|
private
|
65
68
|
|
69
|
+
# @param name [String] - the hostname to mark used
|
70
|
+
# @return Array[String] - list of used instances
|
71
|
+
def mark_used(name, pool_name)
|
72
|
+
# ideally the member should not already be in this array
|
73
|
+
# but just in case we will protect against that
|
74
|
+
pool_hosts(pool_name).delete(name)
|
75
|
+
used_hosts(pool_name) << name unless used_hosts(pool_name).include?(name)
|
76
|
+
save
|
77
|
+
used_hosts(pool_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param name [String] - the hostname to mark not used
|
81
|
+
# @return Array[String] - list of unused instances
|
82
|
+
def mark_unused(name, pool_name)
|
83
|
+
used_hosts(pool_name).delete(name)
|
84
|
+
pool_hosts(pool_name) << name unless pool_hosts(pool_name).include?(name)
|
85
|
+
save
|
86
|
+
pool_hosts(pool_name)
|
87
|
+
end
|
88
|
+
|
89
|
+
|
66
90
|
# @return Array[String] - a list of pool names
|
67
91
|
def pool_names
|
68
92
|
pool_data.keys
|
@@ -89,17 +113,6 @@ module Kitchen
|
|
89
113
|
pool(pool_name)['used_instances'] ||= []
|
90
114
|
end
|
91
115
|
|
92
|
-
# @param name [String] - the hostname to mark used
|
93
|
-
# @return Array[String] - list of used instances
|
94
|
-
def mark_used(name, pool_name)
|
95
|
-
# ideally the member should not already be in this array
|
96
|
-
# but just in case we will protect against that
|
97
|
-
pool_hosts(pool_name).delete(name)
|
98
|
-
used_hosts(pool_name) << name unless used_hosts(pool_name).include?(name)
|
99
|
-
save
|
100
|
-
used_hosts(pool_name)
|
101
|
-
end
|
102
|
-
|
103
116
|
def read_content
|
104
117
|
raise NotImplementedError
|
105
118
|
end
|
@@ -1,8 +1,11 @@
|
|
1
1
|
require "kitchen/driver/vmpool_stores/file_base_store"
|
2
|
+
require 'kitchen/logging'
|
3
|
+
|
2
4
|
module Kitchen
|
3
5
|
module Driver
|
4
6
|
module VmpoolStores
|
5
7
|
class FileStore < FileBaseStore
|
8
|
+
include Kitchen::Logging
|
6
9
|
|
7
10
|
# @option pool_file [String] - the file path that holds the pool information
|
8
11
|
def initialize(options = nil)
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'timeout'
|
3
|
+
require 'json'
|
4
|
+
require_relative 'base_store'
|
5
|
+
|
6
|
+
module Kitchen
|
7
|
+
module Driver
|
8
|
+
module VmpoolStores
|
9
|
+
class VmpoolerStore < BaseStore
|
10
|
+
attr_reader :vmpooler_url, :token, :tags, :lifetime
|
11
|
+
|
12
|
+
# @param [Hash] opts
|
13
|
+
# @option opts [String] :host_url The hostname to use in http requests.
|
14
|
+
# @option opts [String] :user (nil) A username for authentication. Optional only if token is valid.
|
15
|
+
# @option opts [String] :password (nil) A password for authentication. Optional only if token is valid.
|
16
|
+
# @option opts [String] :token (nil) A preloaded token to use in requests. Optional only if
|
17
|
+
# user and pass are valid.
|
18
|
+
# @raise [InvalidUrl] if vmpooler url is invalid
|
19
|
+
def initialize(opts = {})
|
20
|
+
host_url = opts.fetch('host_url')
|
21
|
+
user = opts.fetch('user', nil)
|
22
|
+
pass = opts.fetch('pass', nil)
|
23
|
+
token = opts.fetch('token', nil)
|
24
|
+
@tags = opts.fetch('tags', { purpose: 'vmpooler-default' })
|
25
|
+
@lifetime = opts.fetch('lifetime', nil)
|
26
|
+
@vmpooler_url = URI.join(host_url, '/api/v1/').to_s
|
27
|
+
raise Kitchen::Driver::InvalidUrl.new("Bad url: #{vmpooler_url}") unless valid_url?(URI.join(vmpooler_url, 'vm'))
|
28
|
+
@token = valid_token?(token) ? token : create_token(user, pass)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param pool_name [String] the pool to take from
|
32
|
+
# @return [String] a pool member from the pool
|
33
|
+
def take_pool_member(pool_name)
|
34
|
+
fetch_pool_member(pool_name)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param pool_member [String] the pool member to destroy
|
38
|
+
def cleanup(pool_member:, pool_name: nil, reuse_instances: false)
|
39
|
+
used_status = 'destroyed'
|
40
|
+
destroy_pool_member(pool_member)
|
41
|
+
yield(pool_member, used_status) if block_given?
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# @param token [String] the token to validate
|
47
|
+
# @return [true] if the token url is valid
|
48
|
+
# @return [false] if the token url is invalid
|
49
|
+
def valid_token?(token)
|
50
|
+
if token and token.length > 5
|
51
|
+
token_url = URI.join(vmpooler_url, 'token', token).to_s
|
52
|
+
valid_url?(token_url)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param url [String] a url to validate
|
57
|
+
# @return [true] if the http response code is 200
|
58
|
+
# @return [false] if the http response code is not 200
|
59
|
+
def valid_url?(url)
|
60
|
+
if url
|
61
|
+
uri = URI(url)
|
62
|
+
response = Net::HTTP.get_response(uri)
|
63
|
+
response.code == '200'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param pool_member [String] a VM instance to destroy
|
68
|
+
# @raise [PoolMemberNotFound] if the pool member was not found
|
69
|
+
# @raise [PoolMemberNotDestroyed] if the pool member was not destroyed
|
70
|
+
def destroy_pool_member(pool_member)
|
71
|
+
uri = URI.join(vmpooler_url, 'vm/', pool_member)
|
72
|
+
request = Net::HTTP::Delete.new(uri)
|
73
|
+
request.add_field('X-AUTH-TOKEN', token)
|
74
|
+
|
75
|
+
req_options = {
|
76
|
+
use_ssl: uri.scheme == 'https',
|
77
|
+
}
|
78
|
+
|
79
|
+
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
80
|
+
http.request(request)
|
81
|
+
end
|
82
|
+
|
83
|
+
case
|
84
|
+
when response.code == '404'
|
85
|
+
raise Kitchen::Driver::PoolMemberNotFound.new("Pool member #{pool_member} was not found")
|
86
|
+
when response.code != '200'
|
87
|
+
raise Kitchen::Driver::PoolMemberNotDestroyed.new("Error destroying pool member: code #{response.code}, message #{response.body}")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# @param user [String]
|
92
|
+
# @param pass [String]
|
93
|
+
# @return [String] a new token if the http response code is 200
|
94
|
+
# @raise [InvalidCredentials] if the http response code is 401
|
95
|
+
# @raise [TokenNotCreated] if the http response code is otherwise not 200
|
96
|
+
def create_token(user, pass)
|
97
|
+
uri = URI.join(vmpooler_url, 'token')
|
98
|
+
request = Net::HTTP::Post.new(uri)
|
99
|
+
request.basic_auth(user, pass)
|
100
|
+
|
101
|
+
req_options = {
|
102
|
+
use_ssl: uri.scheme == 'https',
|
103
|
+
}
|
104
|
+
|
105
|
+
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
106
|
+
http.request(request)
|
107
|
+
end
|
108
|
+
|
109
|
+
return JSON.parse(response.body)['token'] if response.code == '200'
|
110
|
+
raise Kitchen::Driver::InvalidCredentials.new("Username or password are invalid") if response.code == '401'
|
111
|
+
raise Kitchen::Driver::TokenNotCreated.new("Unable to create token, got response code: #{response.code}")
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return Array[String] - array of hostnames
|
115
|
+
# @param pool_name [String] - the name of the pool to get hostnames from
|
116
|
+
# @param number_of_vms [Integer] - a postive number of vms to fetch
|
117
|
+
def fetch_pool_member(pool_name, number_of_vms = 1)
|
118
|
+
uri = URI.join(vmpooler_url, 'vm')
|
119
|
+
request = Net::HTTP::Post.new(uri)
|
120
|
+
request.body = JSON.dump({ pool_name => number_of_vms.to_s })
|
121
|
+
request.add_field('X-AUTH-TOKEN', token)
|
122
|
+
|
123
|
+
req_options = {
|
124
|
+
use_ssl: uri.scheme == 'https',
|
125
|
+
}
|
126
|
+
|
127
|
+
response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
|
128
|
+
http.request(request)
|
129
|
+
end
|
130
|
+
|
131
|
+
case response.code
|
132
|
+
when '200'
|
133
|
+
data = JSON.parse(response.body)
|
134
|
+
data[pool_name]['hostname']
|
135
|
+
when '503'
|
136
|
+
msg = "Pool #{pool_name} does not have enough active pool members, please try later."
|
137
|
+
raise Kitchen::Driver::PoolMemberUnavailable.new(msg)
|
138
|
+
when '404'
|
139
|
+
raise Kitchen::Driver::PoolNotFound.new("Pool #{pool_name} was not found")
|
140
|
+
else
|
141
|
+
raise Exception.new("Error: code #{response.code}, message #{response.body}")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -1,3 +1,3 @@
|
|
1
1
|
module KitchenVmpool
|
2
|
-
VERSION = "0.
|
3
|
-
end
|
2
|
+
VERSION = "0.4.0"
|
3
|
+
end
|
data/vmpool.yaml
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kitchen-vmpool
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Corey Osman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gitlab
|
@@ -75,12 +75,15 @@ executables:
|
|
75
75
|
extensions: []
|
76
76
|
extra_rdoc_files: []
|
77
77
|
files:
|
78
|
+
- ".DS_Store"
|
78
79
|
- ".gitignore"
|
80
|
+
- ".kitchen.yml"
|
79
81
|
- ".rspec"
|
80
82
|
- ".travis.yml"
|
81
83
|
- CHANGELOG.md
|
82
84
|
- CODE_OF_CONDUCT.md
|
83
85
|
- Gemfile
|
86
|
+
- Gemfile.lock
|
84
87
|
- LICENSE.txt
|
85
88
|
- README.md
|
86
89
|
- Rakefile
|
@@ -97,6 +100,7 @@ files:
|
|
97
100
|
- lib/kitchen/driver/vmpool_stores/gitlab_base_store.rb
|
98
101
|
- lib/kitchen/driver/vmpool_stores/gitlab_commit_store.rb
|
99
102
|
- lib/kitchen/driver/vmpool_stores/gitlab_snippet_store.rb
|
103
|
+
- lib/kitchen/driver/vmpool_stores/vmpooler_store.rb
|
100
104
|
- vmpool.yaml
|
101
105
|
homepage: https://gitlab.com/nwops/kitchen-vmpool
|
102
106
|
licenses:
|