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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 65858fcfc101cec7b6e0f8a6ee346eec09dac93e
4
- data.tar.gz: 433d4c11777002d5239e0b095c6863a66df324a9
3
+ metadata.gz: a292b2bf0b4134c9da983f663e16acbaa2f9582f
4
+ data.tar.gz: 1b41ec5d409d1fb0446036b1368a62b6354bfab2
5
5
  SHA512:
6
- metadata.gz: a5bdd70bea4907ec7f6c14c1696f018e9bd23636e7cb6062f186cf34dddef52a51cb1284826341c5011810aec778957e8511501d6f4e4e54ec939a8850d55b81
7
- data.tar.gz: adc49044d3030ed33a1576c3c3ab950c552f5210b750793037e7d0add19f8ebee3ecfada9eca7bcbb09dcfc29c07854ac6303b25d5a03c89230cdc003d357571
6
+ metadata.gz: c8dd58b8dff118a7a95c3cee1e41859a3196eb2fb05d72cf04e0d105001cb05f68598b6d28af4d3d08c30e388d1c16632ff171abe373bb711e4e9bc88301b497
7
+ data.tar.gz: 7b934a2d02593fc3ba9942b3e16e90e342079b61c5fcf8ab204377eed615a405a81eba81ce17eb15c54af90f0712660af4b8dd62ad7ddfadb8073dabfdde1f9d
data/.DS_Store ADDED
Binary file
data/.gitignore CHANGED
@@ -1,6 +1,6 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
3
+ #/Gemfile.lock
4
4
  /_yardoc/
5
5
  /coverage/
6
6
  /doc/
@@ -10,3 +10,5 @@
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+ .kitchen/
14
+ .kitchen.local.yml
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::Vmpool
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 thus may suffice. If you plan to expand your usage of test kitchen to
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
- ### Pool data structure
99
- The basic structure of the pool data can be found below. This is the current format that each state store will follow.
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. inherit from the BaseStore or a subclass of the BaseStore
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
- * mark_unused
148
-
149
- 4. All other methods used with your store must be private
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 name [String] - the hostname to mark not used
172
- # @return Array[String] - list of unused instances
173
- def mark_unused(name, pool_name, reuse = false)
174
- if reuse
175
- used_hosts(pool_name).delete(name)
176
- pool_hosts(pool_name) << name unless pool_hosts(pool_name).include?(name)
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
- save
179
- pool_hosts(pool_name)
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/logicminds/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.
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
@@ -4,3 +4,10 @@ require "rspec/core/rake_task"
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  task :default => :spec
7
+
8
+ begin
9
+ require 'kitchen/rake_tasks'
10
+ Kitchen::RakeTasks.new
11
+ rescue LoadError
12
+ puts '>>>>> Kitchen gem not loaded, omitting tasks' unless ENV['CI']
13
+ end
@@ -49,13 +49,18 @@ module Kitchen
49
49
 
50
50
  # (see Base#destroy)
51
51
  def destroy(state)
52
- return if state[:hostname].nil?
53
- store.mark_unused(state[:hostname], config[:pool_name], config[:reuse_instances])
54
- if config[:reuse_instances]
55
- info("Marking pool member #{state[:hostname]} as unused")
56
- else
57
- info("Marking pool member #{state[:hostname]} as used")
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 name [String] - the hostname to mark not used
19
- # @param pool_name [String] - the name of the pool to yank the memeber from
20
- # @return Array[String] - list of unused instances
21
- def mark_unused(name, pool_name, reuse = false)
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
- return member
19
+ member
19
20
  end
20
21
 
21
- # @param name [String] - the hostname to mark not used
22
- # @return Array[String] - list of unused instances
23
- def mark_unused(name, pool_name, reuse = false)
24
- if reuse
25
- used_hosts(pool_name).delete(name)
26
- pool_hosts(pool_name) << name unless pool_hosts(pool_name).include?(name)
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
- save
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.2"
3
- end
2
+ VERSION = "0.4.0"
3
+ end
data/vmpool.yaml CHANGED
@@ -1,8 +1,7 @@
1
1
  ---
2
2
  base_rhel6_pool:
3
- pool_name: base_rhel6
4
3
  payload_file: base_rhel6_payload.json
5
- instances: 1
4
+ size: 1
6
5
  pool_instances: []
7
6
  requests: []
8
7
  used_instances: []
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.3.2
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-09-11 00:00:00.000000000 Z
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: