kitchen-vmpool 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ba43fce6af2f6b5d3e8169ce88c85b23264cf443
4
- data.tar.gz: 47cfe3bc7f7d189cbe64a9208d0e21fcb06a42d4
3
+ metadata.gz: 9e4fff8380ee9f18fec8ef01a1d609b54ebfbfc4
4
+ data.tar.gz: fdaa0e13db8f1efaeca8c49c0a41382f2a793457
5
5
  SHA512:
6
- metadata.gz: dc037d0dfb9125d2ebaba3d776e17b8ff8ac5954358e2e5e90f1d17ec0d827ba7eb7eacdb1b06a145482c1c4421b89dc408468a64971ae02a33cdb6ddaf31e2c
7
- data.tar.gz: ccc435a6f48212d3b3cfc8cf6d743aacca971d134900d2dbe20b25aabb684a3ca42fb2f5be94c681592af68ea0d19149c6ba2d716e9472cf1280b8a8adba2cb8
6
+ metadata.gz: b3ca03be5000e386e92defb694ac0c9d0efec5778124427c607c190c26b61284e7b33a85447907236955426820059afd313b65cc8b6bcfbf943e1278e56e393b
7
+ data.tar.gz: 5dc30b54179d657966a1ffdf12b071a58117d25df8533f44e3b72afa631744fa6790a6ab0aef68074a3cb2c3299251d7b6463fb939ccc18ca751476cd776624f
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
1
  # Kitchen::Vmpool
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/kitchen/vmpool`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Provides the ability to select a memeber of a pool to use during a kitchen test. This allows you to instantly startup
4
+ a test instead of waiting for a machine to provision.
4
5
 
5
- TODO: Delete this and the text above, and describe your gem
6
+ Pluggable backend is used for storing pool information.
6
7
 
7
8
  ## Installation
8
9
 
data/exe/vra_create.rb ADDED
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env ruby
2
+ require 'vra'
3
+ require 'fileutils'
4
+ require 'erb'
5
+ require 'highline/import'
6
+ require 'openssl'
7
+ require 'json'
8
+ require 'yaml'
9
+
10
+ # Purpose: Submits a single request to VRA for vm creation
11
+
12
+
13
+ # monkey patch strings to symbols until we can patch upstream
14
+ module Vra
15
+ class CatalogRequest
16
+ attr_accessor :template_payload
17
+
18
+ def template_payload
19
+ @template_payload ||= dump_template(@catalog_id)
20
+ end
21
+
22
+ def template_payload=(payload)
23
+ @template_payload = payload
24
+ end
25
+
26
+ def write_template(id, filename = nil)
27
+ filename ||= "#{id}.json"
28
+ begin
29
+ puts "Writing file #{filename}"
30
+ contents = dump_template(id)
31
+ data = JSON.parse(contents)
32
+ pretty_contents = JSON.pretty_generate(data)
33
+ File.write(filename, pretty_contents)
34
+ rescue Vra::Exception::HTTPError => e
35
+ puts e.message
36
+ end
37
+ end
38
+
39
+ def dump_template(id)
40
+ response = client.http_get("/catalog-service/api/consumer/entitledCatalogItems/#{id}/requests/template")
41
+ response.body
42
+ end
43
+
44
+ def merged_payload
45
+ merge_payload(template_payload)
46
+ end
47
+
48
+ def submit
49
+ validate_params!
50
+
51
+ begin
52
+ post_response = client.http_post("/catalog-service/api/consumer/entitledCatalogItems/#{@catalog_id}/requests", merged_payload)
53
+ rescue Vra::Exception::HTTPError => e
54
+ raise Vra::Exception::RequestError, "Unable to submit request: #{e.errors.join(', ')}"
55
+ rescue
56
+ raise
57
+ end
58
+
59
+ request_id = JSON.parse(post_response.body)["id"]
60
+ Vra::Request.new(client, request_id)
61
+ end
62
+
63
+ end
64
+ class RequestParameters
65
+ def set_parameters(key, value_data, parent = nil)
66
+ value_type = value_data[:type] || value_data['type']
67
+ data_value = value_data[:value] || value_data['value']
68
+ if value_type
69
+ if parent.nil?
70
+ set(key, value_type, data_value)
71
+ else
72
+ parent.add_child(Vra::RequestParameter.new(key, value_type, data_value))
73
+ end
74
+ else
75
+ if parent.nil?
76
+ p = set(key, nil, nil)
77
+ else
78
+ p = Vra::RequestParameter.new(key, nil, nil)
79
+ parent.add_child(p)
80
+ end
81
+
82
+ value_data.each do |k, data|
83
+ set_parameters(k, data, p)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ module VraUtilities
91
+ def classification
92
+ ENV['VRA_CLASSIFY']
93
+ end
94
+
95
+ def branch
96
+ `git rev-parse --abbrev-ref HEAD`.chomp if ENV['USE_BRANCH']
97
+ end
98
+
99
+ def sandbox
100
+ branch || 'dev'
101
+ end
102
+
103
+ def datacenter
104
+ ENV['DATACENTER']
105
+ end
106
+
107
+ def vra_email
108
+ @vra_email ||= ENV['VRA_EMAIL'] || ask('What is your frit email for VRA notifications')
109
+ end
110
+
111
+ def subtenant_id
112
+ ENV['VRA_SUB_TENANT_ID']
113
+ end
114
+
115
+ def vra_user
116
+ @vra_user ||= ENV['VRA_USER'] || ask('Enter User: ') {|q| q.echo = true}
117
+ end
118
+
119
+ def vra_pass
120
+ @vra_pass ||= ENV['VRA_PASS'] || ask('Enter VRA Password: ') {|q| q.echo = 'x'}
121
+ end
122
+
123
+ def base_url
124
+ @server ||= ENV['VRA_URL']
125
+ end
126
+
127
+ # @return [VRA::Client] - creates a new client object and returns it
128
+ def client
129
+ @client ||= Vra::Client.new(
130
+ username: vra_user,
131
+ password: vra_pass,
132
+ tenant: 'vsphere.local',
133
+ base_url: base_url,
134
+ verify_ssl: false,
135
+ )
136
+ end
137
+
138
+ # @return Array[String] - returns an array of catalog items
139
+ def catalog_items
140
+ client.catalog.all_items.map {|i| {name: i.name, id: i.id}}
141
+ end
142
+
143
+ def template_data
144
+ @template_data ||= JSON.parse(File.read(@payload_file))
145
+ end
146
+
147
+ def request_options
148
+ {
149
+ cpus: template_data['data']['Machine']['data']['cpu'] || 2,
150
+ memory: template_data['data']['Machine']['data']['memory'] || 4096,
151
+ requested_for: ENV['VRA_USER']
152
+ lease_days: 2,
153
+ notes: 'VRA Server Pool Test',
154
+ subtenant_id: template_data['businessGroupId']
155
+ }
156
+ end
157
+
158
+ def catalog_request
159
+ blueprint = template_data['catalogItemId']
160
+ cr = client.catalog.request(blueprint, request_options)
161
+ cr.template_payload = File.read(@payload_file)
162
+ cr
163
+ end
164
+
165
+ # @return [Vra::Request] - returns a request item
166
+ def submit_new_request(file)
167
+ @payload_file = File.expand_path(file)
168
+ unless @payload_file and File.exist?(@payload_file)
169
+ puts "The payload file: #{@payload_file} does not exist"
170
+ exit -1
171
+ end
172
+ cr = catalog_request
173
+ cr.submit
174
+ end
175
+
176
+ require 'optparse'
177
+
178
+ def dump_templates(dir_name = 'vra7_templates')
179
+ FileUtils.mkdir(dir_name) unless File.exist?(dir_name)
180
+ catalog_items.each do |c|
181
+ cr = catalog_request
182
+ filename = File.join(dir_name, "#{c[:name]}.json".gsub(' ', '_')).downcase
183
+ cr.write_template(c[:id], filename)
184
+ end
185
+ end
186
+
187
+
188
+ def run
189
+ cli_options = {}
190
+ o = OptionParser.new do |opts|
191
+ opts.program_name = 'vra-pool'
192
+ opts.on_head(<<-EOF
193
+
194
+ Summary: A tool used to provision systems in VRA
195
+ EOF
196
+ )
197
+ opts.on('-n', '--node-file FILE', "Load the request data from this file and create it") do |c|
198
+ cli_options[:node_file] = c
199
+ end
200
+ opts.on('-t', '--dump-templates', "Dump all catalog templates") do |c|
201
+ cli_options[:dump_templates] = true
202
+ end
203
+ end.parse!
204
+ @payload_file = cli_options[:node_file]
205
+ dump_templates if cli_options[:dump_templates]
206
+ submit_new_request(@payload_file)
207
+ end
208
+ end
209
+ include VraUtilities
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
-
3
+ require 'hashdiff'
4
4
  require 'resolv'
5
5
  require 'optparse'
6
6
  require 'yaml'
7
7
  require_relative 'vra_create'
8
- require 'kitchen/driver/vmpool_stores/gitlab_store'
8
+ require 'kitchen/driver/vmpool_stores/gitlab_commit_store'
9
9
  require 'socket'
10
10
  require 'timeout'
11
11
 
@@ -20,34 +20,62 @@ def options
20
20
  end
21
21
 
22
22
  def create_pool(pool_data)
23
- (1..pool_data['instances']).map {|num| submit_new_request(pool_data['payload_file']).id }
23
+ (1..pool_data['instances']).map do |num|
24
+ puts pool_data['payload_file']
25
+ submit_new_request(pool_data['payload_file']).id
26
+ end
24
27
  end
25
28
 
26
29
  # return hostnames or false
30
+ # @param [VRA::Request] - a request object
27
31
  def resolve_vm_name(request)
28
- r = client.requests.by_id(request)
32
+ r = request
29
33
  return r.resources.map(&:name) if r.successful? and r.completed?
30
34
  return false
31
35
  end
32
36
 
37
+ def req_obj(id)
38
+ client.requests.by_id(id)
39
+ end
40
+
41
+ # compares the before and after state of the store hashes
42
+ def store_changed?(before, after)
43
+ HashDiff.diff(before, after).count > 0
44
+ end
45
+
33
46
  # @return [Hash] - a store hash that contains one or more pools
34
47
  # @option project_id [Integer] - the project id in gitlab
35
- # @option snippet_id [Integer] - the snipppet id in the gitlab project
36
48
  # @option pool_file [String] - the snipppet file name
37
49
  def store(store_options = options)
38
50
  # create a new instance of the store with the provided options
39
- @store ||= Kitchen::Driver::VmpoolStores::GitlabStore.new(store_options)
51
+ @store ||= Kitchen::Driver::VmpoolStores::GitlabCommitStore.new(store_options)
40
52
  end
41
53
 
42
54
  # creates the number of instances defined in the pool data
43
55
  def create_pools
56
+ @old_store = store.dup
44
57
  pools.map do |key, value|
45
58
  # convert the requests to vm names
46
59
  pools[key]['requests'] = value['requests'].find_all do |req|
47
- if hostnames = resolve_vm_name(req)
48
- pools[key]['pool_instances'] = value['pool_instances'] + hostnames
49
- false
50
- else
60
+ puts "Checking request: #{req}"
61
+ r = req_obj(req)
62
+ if r.completed?
63
+ puts "The request #{req} has completed, getting hostname"
64
+ hostnames = resolve_vm_name(r)
65
+ # remove request from pool file by not returning anything
66
+ # if hostname does not exist but request completed don't update pool
67
+ if ! hostnames
68
+ puts "Provisioning seemed to have failed for #{req}"
69
+ puts "Removing request #{req} from pool #{key}"
70
+ false
71
+ else
72
+ pools[key]['pool_instances'] = value['pool_instances'] + hostnames
73
+ false
74
+ end
75
+ else
76
+ # has not completed
77
+ # keep the request, since it is not finished
78
+ puts "The request #{req} is still running"
51
79
  req
52
80
  end
53
81
  end
@@ -63,7 +91,8 @@ def create_pools
63
91
  pools[key]['requests'] = reqs
64
92
  end
65
93
  end
66
- store.save
94
+ # prevents updates from occuring when they are not required
95
+ store.save if store_changed?(@old_store, store)
67
96
  end
68
97
 
69
98
  # @return [Boolean] - true if the host is alive and listening
@@ -107,22 +136,18 @@ end
107
136
 
108
137
  # @return [Boolean] - true if options are valid
109
138
  def valid_options?
110
- options['pool_file'] &&
111
- File.exist?(options['pool_file']) &&
112
- options['project_id'].to_i > 0 &&
113
- options['snippet_id'].to_i > 0
139
+ options['project_id'].to_i > 0
114
140
  end
115
141
 
116
142
  ## main entry point
117
143
 
118
144
  OptionParser.new do |opts|
119
- opts.program_name = 'create-pool'
120
145
  opts.on_head(<<-EOF
121
146
 
122
147
  Summary: A tool used to create a pool of vra systems
123
148
 
124
149
  Example:
125
- #{__FILE__} -f pool_file.yaml -p 33 -s 103
150
+ #{__FILE__} -f pool_file.yaml -p 33 -f vmpool
126
151
 
127
152
  If you wish to store the config in a file, this script will read vmpool_config.yaml
128
153
  for the same configs.
@@ -135,9 +160,6 @@ Summary: A tool used to create a pool of vra systems
135
160
  opts.on('-p', '--project-id ID', "The gitlab project id") do |c|
136
161
  options['project_id'] = c.to_i
137
162
  end
138
- opts.on('-s', '--snippet-id ID', "The gitlab project snippet id") do |c|
139
- options['snippet_id'] = c.to_i
140
- end
141
163
 
142
164
  end.parse!
143
165
 
@@ -1,10 +1,11 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path("../lib", __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'kitchen-vmpool/version'
4
5
 
5
6
  Gem::Specification.new do |spec|
6
7
  spec.name = "kitchen-vmpool"
7
- spec.version = "0.1.1"
8
+ spec.version = KitchenVmpool::VERSION
8
9
  spec.authors = ["Corey Osman"]
9
10
  spec.email = ["corey@nwops.io"]
10
11
 
@@ -19,17 +19,21 @@
19
19
  #
20
20
  require 'kitchen'
21
21
  require "kitchen/version"
22
+ require 'kitchen/logging'
22
23
  require 'kitchen/driver/base'
24
+ require 'kitchen-vmpool/version'
25
+
23
26
  module Kitchen
24
27
  module Driver
25
28
 
26
29
  class PoolMemberNotFound < Exception; end
27
30
 
28
31
  class Vmpool < Kitchen::Driver::Base
29
- plugin_version "0.1.1"
32
+ include Kitchen::Logging
33
+
34
+ plugin_version KitchenVmpool::VERSION
30
35
 
31
36
  default_config :pool_name, 'pool1'
32
- default_config :pool_file, 'vmpool.yaml'
33
37
  default_config :state_store, 'file'
34
38
  default_config :store_options, {}
35
39
  default_config :reuse_instances, false
@@ -40,7 +44,7 @@ module Kitchen
40
44
 
41
45
  # (see Base#create)
42
46
  def create(state)
43
- state[:hostname] = pool_member
47
+ state[:hostname] = take_pool_member
44
48
  end
45
49
 
46
50
  # (see Base#destroy)
@@ -53,11 +57,12 @@ module Kitchen
53
57
  private
54
58
 
55
59
  # @return [String] - a random host from the list of systems
56
- def pool_member
57
- sample = pool_hosts.sample
58
- raise PoolMemberNotFound.new("No pool members exist for #{config[:pool_name]}, please create some pool members") unless sample
59
- member = pool_hosts.delete(sample)
60
+ # mark them used so nobody else can use it
61
+ def take_pool_member
62
+ member = pool_hosts.sample
63
+ raise PoolMemberNotFound.new("No pool members exist for #{config[:pool_name]}, please create some pool members") unless member
60
64
  mark_used(member)
65
+ info("Pool member #{member} was selected")
61
66
  return member
62
67
  end
63
68
 
@@ -80,37 +85,46 @@ module Kitchen
80
85
 
81
86
  # @return Array[String] - a list of host names in the pool
82
87
  def pool_hosts
83
- if config[:reuse_instances]
84
- pool['pool_instances'] + pool['used_instances'].to_a
85
- else
86
- pool['pool_instances']
87
- end
88
+ pool['pool_instances'] ||= []
89
+ end
90
+
91
+ # @return Array[String] - a list of used host names in the pool
92
+ def used_hosts
93
+ pool['used_instances'] ||= []
88
94
  end
89
95
 
90
96
  # @param name [String] - the hostname to mark not used
91
97
  # @return Array[String] - list of unused instances
92
98
  def mark_unused(name)
93
- pool['pool_instances'] = [] unless pool['pool_instances']
94
- pool['pool_instances'] << name
99
+ if config[:reuse_instances]
100
+ info("Marking pool member #{name} as unused")
101
+ used_hosts.delete(name)
102
+ pool_hosts << name unless pool_hosts.include?(name)
103
+ end
95
104
  store.save
96
- pool['pool_instances']
105
+ pool_hosts
97
106
  end
98
107
 
99
108
  # @param name [String] - the hostname to mark used
100
109
  # @return Array[String] - list of used instances
101
110
  def mark_used(name)
102
- pool['used_instances'] = [] unless pool['used_instances']
103
- pool['used_instances'] << name
111
+ debug("Marking pool member #{name} as used")
112
+ # ideally the member should not already be in this array
113
+ # but just in case we will protect against that
114
+ pool_hosts.delete(name)
115
+ used_hosts << name unless used_hosts.include?(name)
104
116
  store.save
105
- pool['used_instances']
117
+ used_hosts
106
118
  end
107
119
 
108
120
  # @return [Hash] - a store hash that contains one or more pools
109
121
  def store
110
122
  @store ||= begin
111
123
  # load the store adapter and create a new instance of the store
112
- store = sprintf("%s%s", config[:state_store].capitalize, 'Store')
113
- require "kitchen/driver/vmpool_stores/#{config[:state_store]}_store"
124
+ name = config[:state_store].split('_').map(&:capitalize).join('')
125
+ store = sprintf("%s%s", name, 'Store')
126
+ store_file = "#{config[:state_store]}_store"
127
+ require "kitchen/driver/vmpool_stores/#{store_file}"
114
128
  klass = Object.const_get("Kitchen::Driver::VmpoolStores::#{store}")
115
129
  # create a new instance of the store with the provided options
116
130
  store_opts = config[:store_options]
@@ -125,3 +139,24 @@ module Kitchen
125
139
  end
126
140
  end
127
141
  end
142
+
143
+ require 'gitlab'
144
+ # monkey patch error in error code until it is fixed upstream
145
+ module Gitlab
146
+ module Error
147
+ class ResponseError
148
+ # Human friendly message.
149
+ #
150
+ # @return [String]
151
+ private
152
+ def build_error_message
153
+ parsed_response = @response.parsed_response
154
+ message = parsed_response.respond_to?(:message) ? parsed_response.message : parsed_response['message']
155
+ message = parsed_response.error unless message
156
+ "Server responded with code #{@response.code}, message: " \
157
+ "#{handle_message(message)}. " \
158
+ "Request URI: #{@response.request.base_uri}#{@response.request.path}"
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,53 @@
1
+ require 'yaml'
2
+ require 'kitchen/logger'
3
+ require 'kitchen'
4
+ require 'kitchen/logging'
5
+
6
+ module Kitchen
7
+ module Driver
8
+ module VmpoolStores
9
+ class BaseStore
10
+ attr_reader :pool_file, :pool_data
11
+ include Kitchen::Logging
12
+
13
+ def update(content = nil)
14
+ #info("Updating vmpool data")
15
+ write_content(content)
16
+ read
17
+ end
18
+
19
+ def create
20
+ #info("Creating new vmpool data")
21
+ write_content(base_content)
22
+ read
23
+ end
24
+
25
+ def read
26
+ #info("Reading vmpool data")
27
+ read_content
28
+ end
29
+
30
+ def reread
31
+ pool_data(true)
32
+ end
33
+
34
+ def save
35
+ #info("Saving vmpool data")
36
+ write_content
37
+ read
38
+ end
39
+
40
+ def pool_data(refresh = false)
41
+ @pool_data = nil if refresh
42
+ @pool_data ||= YAML.load(read_content)
43
+ end
44
+
45
+ private
46
+
47
+ def read_content
48
+ raise NotImplementedError
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,11 +1,9 @@
1
1
  require 'yaml'
2
-
2
+ require "kitchen/driver/vmpool_stores/base_store"
3
3
  module Kitchen
4
4
  module Driver
5
5
  module VmpoolStores
6
- class FileStore
7
-
8
- attr_reader :pool_file
6
+ class FileStore < BaseStore
9
7
 
10
8
  # @option pool_file [String] - the file path that holds the pool information
11
9
  def initialize(options = nil)
@@ -14,30 +12,6 @@ module Kitchen
14
12
  @pool_file = options['pool_file']
15
13
  end
16
14
 
17
- def update(content)
18
- write_content(content)
19
- read
20
- end
21
-
22
- def create
23
- write_content(base_content)
24
- read
25
- end
26
-
27
- def read
28
- puts "Reading snippet"
29
- read_content
30
- end
31
-
32
- def save
33
- write_content
34
- read
35
- end
36
-
37
- def pool_data
38
- @pool_data ||= YAML.load(read_content)
39
- end
40
-
41
15
  private
42
16
 
43
17
  def base_content
@@ -0,0 +1,82 @@
1
+ require 'gitlab'
2
+ require "kitchen/driver/vmpool_stores/base_store"
3
+ require 'yaml'
4
+
5
+ module Kitchen
6
+ module Driver
7
+ module VmpoolStores
8
+ class GitlabCommitStore < BaseStore
9
+
10
+ attr_accessor :project_id
11
+ attr_reader :pool_file, :branch
12
+
13
+ # @option project_id [Integer] - the project id in gitlab
14
+ # @option commit_id [Integer] - the snipppet id in the gitlab project
15
+ # @option pool_file [String] - the snipppet file name
16
+ def initialize(options = nil)
17
+ options ||= { project_id: nil, pool_file: 'vmpool.yaml'}
18
+ raise ArgumentError.new("You must pass the project_id option") unless options['project_id'].to_i > 0
19
+ @project_id = options['project_id'] #ie. 89
20
+ @pool_file = options['pool_file'] || 'vmpool.yaml'
21
+ @branch = 'master'
22
+ end
23
+
24
+ def update(content = nil)
25
+ #info("Updating vmpool data")
26
+ update_file
27
+ read
28
+ end
29
+
30
+ def create
31
+ #info("Creating new vmpool data commit")
32
+ create_file unless file_exists?
33
+ read
34
+ end
35
+
36
+ def save
37
+ # info("Saving vmpool data")
38
+ update_file
39
+ read
40
+ end
41
+
42
+ def file_exists?(project = project_id, file = pool_file)
43
+ read_content(project, file)
44
+ end
45
+
46
+ private
47
+
48
+ def client
49
+ @client ||= Gitlab.client
50
+ end
51
+
52
+ def create_file(project = project_id)
53
+ actions = [{
54
+ "action" => "create",
55
+ "file_path" => pool_file,
56
+ "content" => {}.to_yaml
57
+ }]
58
+ client.create_commit(project, branch, "update vmpool data", actions)
59
+ end
60
+
61
+
62
+ def update_file(project = project_id)
63
+ actions = [{
64
+ "action" => "update",
65
+ "file_path" => pool_file,
66
+ "content" => pool_data.to_yaml
67
+ }]
68
+ client.create_commit(project, branch, "update vmpool data", actions)
69
+ end
70
+
71
+ def read_content(project = project_id, file = pool_file)
72
+ begin
73
+ client.file_contents(project, file, branch)
74
+ rescue Gitlab::Error::NotFound
75
+ false
76
+ end
77
+ end
78
+
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,8 +1,10 @@
1
1
  require 'gitlab'
2
+ require "kitchen/driver/vmpool_stores/base_store"
3
+ require 'yaml'
2
4
  module Kitchen
3
5
  module Driver
4
6
  module VmpoolStores
5
- class GitlabStore
7
+ class GitlabSnippetStore < BaseStore
6
8
 
7
9
  attr_accessor :project_id, :snippet_id
8
10
  attr_reader :pool_file
@@ -11,42 +13,32 @@ module Kitchen
11
13
  # @option snippet_id [Integer] - the snipppet id in the gitlab project
12
14
  # @option pool_file [String] - the snipppet file name
13
15
  def initialize(options = nil)
14
- options ||= { project_id: nil, snippet_id: nil, pool_file: 'vmpool.yaml'}
16
+ options ||= { project_id: nil, snippet_id: nil, pool_file: 'vmpool'}
15
17
  raise ArgumentError.new("You must pass the project_id option") unless options['project_id'].to_i > 0
16
- raise ArgumentError.new("You must pass the snippet_id option") unless options['snippet_id'].to_i > 0
17
18
  @snippet_id = options['snippet_id'] #ie. 630
18
19
  @project_id = options['project_id'] #ie. 89
19
20
  @pool_file = options['pool_file']
20
21
  end
21
22
 
22
- def update
23
+ def update(content = nil)
24
+ #info("Updating vmpool data")
23
25
  update_snippet
24
26
  read
25
27
  end
26
28
 
27
29
  def create
28
- create_snippet
30
+ #info("Creating new vmpool data snippet")
31
+ snippet = create_snippet
32
+ @snippet_id = snippet.id
29
33
  read
30
34
  end
31
35
 
32
- def read
33
- puts "Reading snippet"
34
- pool_content
35
- end
36
-
37
- def pool_data
38
- @pool_data ||= YAML.load(pool_content)
39
- end
40
-
41
36
  def save
37
+ #info("Saving vmpool data")
42
38
  update_snippet
43
39
  read
44
40
  end
45
41
 
46
- def pool_content
47
- read_snippet
48
- end
49
-
50
42
  private
51
43
 
52
44
  def client
@@ -54,19 +46,20 @@ module Kitchen
54
46
  end
55
47
 
56
48
  def snippet_exists?(project = project_id)
49
+ return false unless snippet_id
57
50
  client.snippets(project, {
58
51
  title: 'Virtual Machine Pools',
59
52
  visibility: 'public',
60
53
  file_name: pool_file,
61
- code: pool_content})
54
+ code: {}.to_yaml})
62
55
  end
63
56
 
64
57
  def create_snippet(project = project_id)
65
- client.create_snippet(project, {
58
+ client.create_commit(project, {
66
59
  title: 'Virtual Machine Pools',
67
60
  visibility: 'public',
68
61
  file_name: pool_file,
69
- code: pool_content
62
+ code: {}.to_yaml
70
63
  })
71
64
  end
72
65
 
@@ -83,7 +76,7 @@ module Kitchen
83
76
  client.snippets(project).map {|s| s.id }
84
77
  end
85
78
 
86
- def read_snippet(project = project_id, id = snippet_id)
79
+ def read_content(project = project_id, id = snippet_id)
87
80
  client.snippet_content(project, id)
88
81
  end
89
82
 
@@ -0,0 +1,3 @@
1
+ module KitchenVmpool
2
+ VERSION = "0.2.0"
3
+ end
data/vmpool.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  base_rhel6_pool:
3
3
  pool_name: base_rhel6
4
- payload_file: base_rhel6_payload.yaml
4
+ payload_file: base_rhel6_payload.json
5
5
  instances: 1
6
6
  pool_instances: []
7
7
  requests: []
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.1.1
4
+ version: 0.2.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-08-10 00:00:00.000000000 Z
11
+ date: 2017-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gitlab
@@ -69,7 +69,9 @@ dependencies:
69
69
  description: When you need to create pools of vms and manage them with test kitchen
70
70
  email:
71
71
  - corey@nwops.io
72
- executables: []
72
+ executables:
73
+ - vra_create.rb
74
+ - vra_create_pool
73
75
  extensions: []
74
76
  extra_rdoc_files: []
75
77
  files:
@@ -83,13 +85,16 @@ files:
83
85
  - Rakefile
84
86
  - bin/console
85
87
  - bin/setup
88
+ - exe/vra_create.rb
89
+ - exe/vra_create_pool
86
90
  - kitchen-vmpool.gemspec
91
+ - lib/kitchen-vmpool/version.rb
87
92
  - lib/kitchen/driver/vmpool.rb
93
+ - lib/kitchen/driver/vmpool_stores/base_store.rb
88
94
  - lib/kitchen/driver/vmpool_stores/file_store.rb
89
- - lib/kitchen/driver/vmpool_stores/gitlab_store.rb
95
+ - lib/kitchen/driver/vmpool_stores/gitlab_commit_store.rb
96
+ - lib/kitchen/driver/vmpool_stores/gitlab_snippet_store.rb
90
97
  - vmpool.yaml
91
- - vra_create.rb
92
- - vra_create_pool
93
98
  homepage: https://gitlab.com/nwops/kitchen-vmpool
94
99
  licenses:
95
100
  - MIT
data/vra_create.rb DELETED
@@ -1,156 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'vra'
3
- require 'erb'
4
- require 'highline/import'
5
- require 'openssl'
6
- require 'json'
7
- require 'yaml'
8
-
9
- # Purpose: Submits a single request to VRA for vm creation
10
- module Vra
11
- class Client
12
- # monkey patch the init method to accept token
13
- def initialize(opts)
14
- @base_url = opts[:base_url]
15
- @username = opts[:username]
16
- @password = PasswordMasker.new(opts[:password])
17
- @tenant = opts[:tenant]
18
- @verify_ssl = opts.fetch(:verify_ssl, true)
19
- @bearer_token = PasswordMasker.new(nil)
20
- @page_size = opts.fetch(:page_size, 20)
21
-
22
- validate_client_options!
23
- end
24
- end
25
-
26
- # monkey patch the init method to accept additional params
27
- class CatalogRequest
28
- def initialize(client, catalog_id, opts = {})
29
- @client = client
30
- @catalog_id = catalog_id
31
- @cpus = opts[:cpus]
32
- @memory = opts[:memory]
33
- @requested_for = opts[:requested_for]
34
- @lease_days = opts[:lease_days]
35
- @notes = opts[:notes]
36
- @subtenant_id = opts[:subtenant_id]
37
- @additional_params = opts[:additional_params] || Vra::RequestParameters.new
38
- @catalog_item = Vra::CatalogItem.new(client, id: catalog_id)
39
- end
40
- end
41
- end
42
-
43
- module VraUtilities
44
- def classification
45
- ENV['VRA_CLASSIFY']
46
- end
47
-
48
- def branch
49
- `git rev-parse --abbrev-ref HEAD`.chomp if ENV['USE_BRANCH']
50
- end
51
-
52
- def sandbox
53
- branch || 'dev'
54
- end
55
-
56
- def datacenter
57
- ENV['DATACENTER'] || 'Eroc'
58
- end
59
-
60
- def vra_email
61
- @vra_email ||= ENV['VRA_EMAIL'] || ask('What is your frit email for VRA notifications')
62
- end
63
-
64
- def subtenant_id
65
- ENV['VRA_SUB_TENANT_ID']
66
- end
67
-
68
- def vra_user
69
- @vra_user ||= ENV['VRA_USER'] || ask('Enter User: ') {|q| q.echo = true}
70
- end
71
-
72
- def vra_pass
73
- @vra_pass ||= ENV['VRA_PASS'] || ask('Enter VRA Password: ') {|q| q.echo = 'x'}
74
- end
75
-
76
- def base_url
77
- @server ||= ENV['VRA_URL']
78
- end
79
-
80
-
81
- # @return [VRA::Client] - creates a new client object and returns it
82
- def client
83
- @client ||= Vra::Client.new(
84
- username: vra_user,
85
- password: vra_pass,
86
- tenant: 'vsphere.local',
87
- base_url: base_url,
88
- verify_ssl: false,
89
- )
90
- end
91
-
92
- # @return Array[String] - returns an array of catalog items
93
- def catalog_items
94
- client.catalog.all_items.map {|i| {name: i.name, id: i.id}}
95
- end
96
-
97
- def request_options
98
- {
99
- cpus: 1,
100
- memory: 4096,
101
- requested_for: 'someone@localhost',
102
- lease_days: 2,
103
- additional_params: request_params,
104
- notes: 'Corey Test',
105
- subtenant_id: request_data['organization']['subtenantRef']
106
- }
107
- end
108
-
109
- def request_data
110
- @request_data ||= YAML.load_file(@payload_file)
111
- end
112
-
113
- def parameters
114
- request_data['requestData']['entries'].map {|item| [item['key'], item['value'].values].flatten }
115
- end
116
-
117
- def request_params
118
- unless @request_params
119
- @request_params = Vra::RequestParameters.new
120
- parameters.each { |p| @request_params.set(*p)}
121
- end
122
- @request_params
123
- end
124
-
125
- def request_item
126
- blueprint = request_data['catalogItemRef']['id']
127
- client.catalog.request(blueprint, request_options)
128
- end
129
-
130
- # @return [Vra::Request] - returns a request item
131
- def submit_new_request(payload_file)
132
- @payload_file = payload_file
133
- request_item.submit
134
- end
135
-
136
- require 'optparse'
137
-
138
- def run
139
- options = {}
140
- OptionParser.new do |opts|
141
- opts.program_name = 'vra-pool'
142
- opts.on_head(<<-EOF
143
-
144
- Summary: A tool used to provision systems in VRA
145
- EOF
146
- )
147
- opts.on('-n', '--node-file FILE', "Load the request data from this file and create it") do |c|
148
- options[:node_file] = c
149
- @payload_file = c
150
- submit_new_request if File.exist?(@payload_file) # create the request
151
- end
152
- end.parse!
153
- end
154
- end
155
-
156
- include VraUtilities