kitchen-vmpool 0.1.1 → 0.2.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: 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