vanagon 0.16.1 → 0.17.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
  SHA256:
3
- metadata.gz: 29954dadaa5567df164293efed2f042795e1cf1a54096cb066e2a898782800c9
4
- data.tar.gz: cd8ee9e9c6e88c650449a062577ed10d7520642e94a73082e4fd0708b8ab2dfd
3
+ metadata.gz: e09a1563ed504da68687d85b7c7a70e62028b7d1ba24568476d5cae338ae1ebf
4
+ data.tar.gz: 9da1804bfbf40c86ac845ccf60a76c133b217357117dcd22d43d97c35299aa1d
5
5
  SHA512:
6
- metadata.gz: f3dd4b2c8be84d868ef66e56aa31ab841b050a72bd6ee52cea7e55851b117ac6de2f638697aaa2b2fab0b34c1cb3b31d743d4759021e814878410855817c9144
7
- data.tar.gz: cafda3c0792d436bc133fe7fb064b0c3d685a7112040ea4d46d893690e8e9d539a84a9eabf97059621e3d23a1e557df239f3d91f83a98f8be4fda7c278b18e6d
6
+ metadata.gz: 19591339a372853aa653311214de761ead6dfd173218d68c0086691ede75da8d981079408afaf04e5fd362e2c9250c72ac9b1cb8cc3fdbeb16006a6510707176
7
+ data.tar.gz: 74e995f55e05778820809bbed4c2dcd501896a8d4894e1a38921bb8057cee049d62c42e136e8929c84c9609790738d77be356ed55b499974b3399c6a032d724c
data/README.md CHANGED
@@ -45,9 +45,10 @@ Also, Vanagon ships with a number of engines which may include additional option
45
45
 
46
46
  ### Local Host:
47
47
 
48
- - [Ruby](https://www.ruby-lang.org/en/) (Ruby 2.1.x is the miniumum supported version)
48
+ - [Ruby](https://www.ruby-lang.org/en/) (Ruby 2.3.x is the miniumum supported version)
49
49
  - [fustigit](https://github.com/mckern/fustigit)
50
50
  - [ruby-git](https://github.com/schacon/ruby-git)
51
+ - [docopt](https://github.com/docopt/docopt.rb)
51
52
  - The command line tool `ssh` ([homepage](https://www.openssh.com/)) available on the local `${PATH}` (any modern version should suffice)
52
53
  - The command line tool `rsync` ([homepage](https://rsync.samba.org/)) available on the local `${PATH}` (At least rsync 2.6.x)
53
54
  - The command line tool `git` ([homepage](https://git-scm.com/)) available on the local `${PATH}` (Vanagon is tested against Git version 1.8.x but should work with any newer version)
@@ -84,10 +85,28 @@ wheezy and build my project against it.
84
85
  For more detailed examples of the DSLs available, please see the
85
86
  [examples](examples) directory and the YARD documentation for Vanagon.
86
87
 
87
- ### `build` usage
88
+ ### CLI changes and deprecations (from version 0.16.0)
89
+
90
+ Prior to 0.16.0, the vanagon command line contained these commands
91
+
92
+ * `build`
93
+ * `build_host_info`
94
+ * `build_requirements`
95
+ * `inspect`
96
+ * `render`
97
+ * `repo`
98
+ * `ship`
99
+ * `sign`
100
+
101
+ With the exception of `repo`, which calls `packaging` methods, the remaining commands
102
+ have been moved to a git-like pattern of `vanagon <subcommand>`. `vangon build` replaced `build`, `vanagon ship` replaced `ship` and so forth.
103
+
104
+ The older calling usage is deprecated.
105
+
106
+ ### `vanagon build` usage
88
107
  The build command has positional arguments and position independent flags.
89
108
 
90
- #### Arguments (position dependent)
109
+ #### Positional arguments
91
110
 
92
111
  ##### project name
93
112
  The name of the project to build; a file named `<project_name>.rb` must be
@@ -114,7 +133,8 @@ Build machines should be cleaned between builds.
114
133
 
115
134
  #### Flagged arguments
116
135
 
117
- **Note:** command flags can be used anywhere in the command.
136
+ **Note:** command flags currently can be used anywhere in the command. Recommended usages
137
+ is to use flagged arguments before the positional arguments.
118
138
 
119
139
  ##### -w DIR, --workdir DIR
120
140
  Specifies a directory on the local host where the sources should be placed and builds performed.
@@ -134,9 +154,10 @@ Currently supported engines are:
134
154
  * `base` - Pure ssh backend; no teardown currently defined
135
155
  * `local` - Build on the local machine; platform name must match the local machine
136
156
  * `docker` - Builds in a docker container
137
- * `pooler` - Selects a vm from Puppet Labs' vm pooler to build on
157
+ * `pooler` - Selects a vm from Puppet's internal vmpooler to build on
138
158
  * `hardware` - Build on a specific taget and lock it in redis
139
159
  * `ec2` - Build on a specific AWS instance.
160
+ * `ABS` - Selects a vm from Puppet's internal Always-be-scheduling service to build on
140
161
 
141
162
  #### Flags
142
163
 
@@ -147,9 +168,6 @@ Indicates that the host used for building the project should be left intact
147
168
  after the build instead of destroyed. The host is usually destroyed after a
148
169
  successful build, or left after a failed build.
149
170
 
150
- ##### -v, --verbose
151
- (Reserved for future implementation) Will increase the verbosity of output, when implemented.
152
-
153
171
  ##### -h, --help
154
172
  Display command-line help.
155
173
 
@@ -195,16 +213,16 @@ integer value these components to fail after the `VANAGON_TIMEOUT` count is reac
195
213
  Note that this value is expected to be in seconds.
196
214
 
197
215
  #### Example usage
198
- `build --preserve puppet-agent el-6-i386` will build the puppet-agent project
216
+ `vanagon build --preserve puppet-agent el-6-i386` will build the puppet-agent project
199
217
  on the el-6-i386 platform and leave the host intact afterward.
200
218
 
201
- `build --engine=docker puppet-agent el-6-i386` will build the puppet-agent
219
+ `vanagon build --engine=docker puppet-agent el-6-i386` will build the puppet-agent
202
220
  project on the el-6-i386 platform using the docker engine (the platform must
203
221
  have a docker\_image defined in its config).
204
222
 
205
223
  ---
206
224
 
207
- ### `inspect` usage
225
+ ### `vanagon inspect` usage
208
226
 
209
227
  The `inspect` command has positional arguments and position independent flags. It
210
228
  mirrors the `build` command, but exits with success after loading and interpolating
@@ -212,7 +230,7 @@ all of the components in the given project. No attempt is made to actually build
212
230
  the given project; instead, a JSON formatted array of hashes is returned and printed
213
231
  to `stdout`. This JSON array can be further processed by external tooling, such as `jq`.
214
232
 
215
- #### Arguments (position dependent)
233
+ #### Positional arguments
216
234
 
217
235
  ##### project name
218
236
  The name of the project to build, and a file named \<project\_name\>.rb must be
@@ -227,7 +245,8 @@ Platform can also be a comma separated list of platforms such as platform1,platf
227
245
 
228
246
  #### Flagged arguments
229
247
 
230
- **Note:** command flags can be used anywhere in the command.
248
+ **Note:** command flags currently can be used anywhere in the command. Recommended usages
249
+ is to use flagged arguments before the positional arguments.
231
250
 
232
251
  ##### -w DIR, --workdir DIR
233
252
  Specifies a directory where the sources should be placed and builds performed.
@@ -239,29 +258,24 @@ Specifies where project configuration is found. Defaults to $pwd/configs.
239
258
  ##### -e ENGINE, --engine ENGINE
240
259
  Choose a different virtualization engine to use to select the build target.
241
260
  Engines are respected, but only insofar as components and projects are
242
- rendered -- the `inspect` command performs no compilation.
261
+ rendered -- the `vanagon inspect` command performs no compilation.
243
262
 
244
- Supported engines are the same as the `build` command.
263
+ Supported engines are the same as the `vanagon build` command.
245
264
 
246
265
  #### Flags
247
266
 
248
- **Note:** command flags can be used anywhere in the command.
249
-
250
- ##### -v, --verbose (not yet implemented)
251
- Increase verbosity of output.
252
-
253
267
  ##### -h, --help
254
268
  Display command-line help.
255
269
 
256
270
  #### Environment variables
257
271
 
258
272
  Environment variables are respected, but only insofar as components and projects are
259
- rendered -- the `inspect` command has no behavior to alter.
273
+ rendered -- the `vanagon inspect` command has no behavior to alter.
260
274
 
261
- Supported environment variables are the same as the `build` command.
275
+ Supported environment variables are the same as the `vanagon build` command.
262
276
 
263
277
  #### Example usage
264
- `inspect puppet-agent el-6-i386` will load the puppet-agent project
278
+ `vanagon inspect puppet-agent el-6-i386` will load the puppet-agent project
265
279
  on the el-6-i386 platform and print the resulting list of dependencies,
266
280
  build-time configuration, environment variables, and expected artifacts.
267
281
 
@@ -297,6 +311,16 @@ platform "el-7-x86_64" do |plat|
297
311
  end
298
312
  ```
299
313
 
314
+ ### ABS (internal)
315
+ When using the ABS engine, there is a variety of ways you can specify your token:
316
+ - the environment variable ABS_TOKEN
317
+ - or vanagon token file ~/.vanagon-token (note this is the same file read by the pooler engine)
318
+ - or [vmfloaty](https://github.com/puppetlabs/vmfloaty)'s config file ~/.vmfloaty.yml
319
+
320
+ In order to modify or track the VM via floaty or bit-bar you can optionally add the vmpooler token (different from the ABS token) via
321
+ - VMPOOLER_TOKEN
322
+ - or as a second line to the file ~/.vanagon-token
323
+ - or by using the existing mechanism in floaty using a vmpooler_fallback
300
324
 
301
325
  Contributing
302
326
  ---
@@ -310,3 +334,4 @@ See [LICENSE](LICENSE) file.
310
334
 
311
335
  ## Maintainers
312
336
  See [MAINTAINERS](MAINTAINERS) file.
337
+
@@ -1,5 +1,6 @@
1
- require 'vanagon/engine/base'
2
1
  require 'json'
2
+ require 'vanagon/engine/base'
3
+ require 'yaml'
3
4
 
4
5
  class Vanagon
5
6
  class Engine
@@ -82,9 +83,15 @@ class Vanagon
82
83
  # {"name":"aix-53-ppc","engine":"always_be_scheduling"}
83
84
  # ```
84
85
  class AlwaysBeScheduling < Base
86
+ attr_reader :token
87
+ attr_reader :token_vmpooler
88
+
85
89
  def initialize(platform, target, **opts)
86
90
  super
87
91
 
92
+ @available_abs_endpoint = "https://abs-prod.k8s.infracore.puppet.net/api/v2"
93
+ @token_vmpooler = ENV['VMPOOLER_TOKEN']
94
+ @token = load_token
88
95
  Vanagon::Driver.logger.debug "AlwaysBeScheduling engine invoked."
89
96
  end
90
97
 
@@ -94,6 +101,7 @@ class Vanagon
94
101
  end
95
102
 
96
103
  # return the platform name as the "host" name
104
+ # order of preference: abs_resource_name, vmpooler_template or name
97
105
  def build_host_name
98
106
  if @platform.abs_resource_name
99
107
  @platform.abs_resource_name
@@ -103,6 +111,268 @@ class Vanagon
103
111
  @platform.name
104
112
  end
105
113
  end
114
+
115
+ # Retrieve the ABS token from an environment variable
116
+ # ("ABS_TOKEN") or from a number of potential configuration
117
+ # files (~/.vanagon-token or ~/.vmfloaty.yml).
118
+ # @return [String, nil] token for use with the vmpooler
119
+ def load_token
120
+ ENV['ABS_TOKEN'] || token_from_file
121
+ end
122
+
123
+ # a wrapper method around retrieving a token,
124
+ # with an explicitly ordered preference for a Vanagon-specific
125
+ # token file or a preexisting vmfoaty yaml file.
126
+ #
127
+ # @return [String, nil] token for use with the vmpooler
128
+ def token_from_file
129
+ read_vanagon_token || read_vmfloaty_token
130
+ end
131
+ private :token_from_file
132
+
133
+ # Read an ABS/vmpooler token from the plaintext vanagon-token file,
134
+ # as outlined in the project README.
135
+ # The first line should be the ABS token
136
+ # and the second (optional) line should be the vmpooler token
137
+ # Saves the second line into the @token_vmpooler variable
138
+ #
139
+ # @return [String, nil] the vanagon vmpooler token value
140
+ def read_vanagon_token(path = "~/.vanagon-token")
141
+ absolute_path = File.expand_path(path)
142
+ return nil unless File.exist?(absolute_path)
143
+
144
+ warn "Reading ABS token from: #{path}"
145
+ contents = File.read(absolute_path).chomp
146
+ lines = contents.each_line.map(&:chomp)
147
+
148
+ abs = lines.shift
149
+ @token_vmpooler = lines.shift
150
+
151
+ warn "Please add a second line with the vmpooler token to be able to modify or see the VM in floaty/bit-bar" if @token_vmpooler.nil?
152
+ return abs
153
+ end
154
+ private :read_vanagon_token
155
+
156
+ # Read a vmpooler token from the yaml formatted vmfloaty config,
157
+ # as outlined by the vmfloaty project:
158
+ # https://github.com/puppetlabs/vmfloaty
159
+ #
160
+ # It returns the top-level token value or the first 'abs' service
161
+ # token value it finds in the services list
162
+ # it sets @token_vmpooler with the optional vmpooler token
163
+ # if the abs services has a vmpooler_fallback, and that service
164
+ # has a token eg
165
+ #
166
+ # TOP-LEVEL DEFINED => would get only the abs token
167
+ # user: 'jdoe'
168
+ # url: 'https://abs.example.net'
169
+ # token: '456def789'
170
+ #
171
+ # MULTIPLE SERVICES DEFINED => would get the abs token in 'abs-prod' and the vmpooler token in 'vmpooler-prod'
172
+ # user: 'jdoe'
173
+ # services:
174
+ # abs-prod:
175
+ # type: 'abs'
176
+ # url: 'https://abs.example.net/api/v2'
177
+ # token: '123abc456'
178
+ # vmpooler_fallback: 'vmpooler-prod'
179
+ # vmpooler-dev:
180
+ # type: 'vmpooler'
181
+ # url: 'https://vmpooler-dev.example.net'
182
+ # token: '987dsa654'
183
+ # vmpooler-prod:
184
+ # type: 'vmpooler'
185
+ # url: 'https://vmpooler.example.net'
186
+ # token: '456def789'
187
+ #
188
+ # @return [String, nil] the vmfloaty vmpooler token value
189
+ def read_vmfloaty_token(path = "~/.vmfloaty.yml") # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
190
+ absolute_path = File.expand_path(path)
191
+ return nil unless File.exist?(absolute_path)
192
+
193
+ yaml_config = YAML.load_file(absolute_path)
194
+ abs_token = nil
195
+ abs_service_name = nil
196
+ if yaml_config['token'] # top level
197
+ abs_token = yaml_config['token']
198
+ elsif yaml_config['services']
199
+ yaml_config['services'].each do |name, value|
200
+ if value['type'] == "abs" && value['token']
201
+ abs_token = value['token']
202
+ abs_service_name = name
203
+ vmpooler_fallback = value['vmpooler_fallback']
204
+ unless vmpooler_fallback.nil? || yaml_config['services'][vmpooler_fallback].nil? || yaml_config['services'][vmpooler_fallback]['token'].nil?
205
+ @token_vmpooler = yaml_config['services'][vmpooler_fallback]['token']
206
+ end
207
+ break
208
+ end
209
+ end
210
+ end
211
+ message = "Reading ABS token from: #{path}"
212
+ if abs_service_name
213
+ message.concat(" for service named #{abs_service_name}")
214
+ if @token_vmpooler.nil?
215
+ message.concat(" but there was no vmpooler_fallback value, please add one if you want to be able to modify the VM via bit-bar/floaty")
216
+ else
217
+ message.concat(" with a vmpooler_fallback token")
218
+ end
219
+ end
220
+ warn message
221
+ return abs_token
222
+ end
223
+ private :read_vmfloaty_token
224
+
225
+ # This method is used to obtain a vm to build upon using Puppet's internal
226
+ # ABS (https://github.com/puppetlabs/always-be-scheduling) which is a level of abstraction for other
227
+ # engines using similar APIs
228
+ # @raise [Vanagon::Error] if a target cannot be obtained
229
+ def select_target
230
+ @pooler = select_target_from(@available_abs_endpoint)
231
+ raise Vanagon::Error, "Something went wrong getting a target vm to build on" if @pooler.empty?
232
+ end
233
+
234
+ # Attempt to provision a host from a specific pooler.
235
+ def select_target_from(pooler) # rubocop:disable Metrics/AbcSize
236
+ request_object = build_request_object
237
+
238
+ warn "Requesting VMs with job_id: #{@saved_job_id}. Will poll for up to an hour."
239
+ #the initial request is always replied with "come back again"
240
+ response = Vanagon::Utilities.http_request_generic(
241
+ "#{pooler}/request",
242
+ 'POST',
243
+ request_object.to_json,
244
+ { 'X-AUTH-TOKEN' => @token }
245
+ )
246
+
247
+ unless response.code == "202"
248
+ warn "failed to request ABS with code #{response.code}"
249
+ if valid_json?(response.body)
250
+ response_json = JSON.parse(response.body)
251
+ warn "reason: #{response_json['reason']}"
252
+ end
253
+ return ''
254
+ end
255
+ response_body = check_queue(pooler, request_object)
256
+
257
+ return '' unless response_body["ok"]
258
+ @target = response_body[build_host_name]['hostname']
259
+ Vanagon::Driver.logger.info "Reserving #{@target} (#{build_host_name}) [#{@token ? 'token used' : 'no token used'}]"
260
+ return pooler
261
+ end
262
+
263
+ # main loop where the status of the request is checked, to see if the request
264
+ # has been allocated
265
+ def check_queue(pooler, request_object)
266
+ retries = 360 # ~ one hour
267
+ response_body = nil
268
+ begin
269
+ (1..retries).each do |i|
270
+ response = Vanagon::Utilities.http_request_generic(
271
+ "#{pooler}/request",
272
+ 'POST',
273
+ request_object.to_json,
274
+ { 'X-AUTH-TOKEN' => @token }
275
+ )
276
+ response_body = validate_queue_status_response(response.code, response.body)
277
+ break if response_body
278
+
279
+ sleep_seconds = 10 if i >= 10
280
+ sleep_seconds = i if i < 10
281
+ warn "Waiting #{sleep_seconds} seconds to check if ABS request has been filled. (x#{i})"
282
+
283
+ sleep(sleep_seconds)
284
+ end
285
+ rescue SystemExit, Interrupt
286
+ warn "\nVanagon interrupted during mains ABS polling. Make sure you delete the requested job_id #{@saved_job_id}"
287
+ raise
288
+ end
289
+ response_body = translated(response_body, @saved_job_id)
290
+ response_body
291
+ end
292
+
293
+ def validate_queue_status_response(status_code, body)
294
+ case status_code
295
+ when "200"
296
+ return JSON.parse(body) unless body.empty? || !valid_json?(body)
297
+ when "202"
298
+ return nil
299
+ when "401"
300
+ raise Vanagon::Error, "HTTP #{status_code}: The token provided could not authenticate.\n#{body}"
301
+ when "503"
302
+ return nil
303
+ else
304
+ raise Vanagon::Error, "HTTP #{status_code}: request to ABS failed!\n#{body}"
305
+ end
306
+ end
307
+
308
+ # This method is used to tell the ABS to delete the job_id requested
309
+ # otherwise the resources will eventually get allocated asynchronously
310
+ # and will keep running until the end of their lifetime.
311
+ def teardown # rubocop:disable Metrics/AbcSize
312
+ request_object = {
313
+ 'job_id' => @saved_job_id,
314
+ }
315
+
316
+ response = Vanagon::Utilities.http_request_generic(
317
+ "#{@available_abs_endpoint}/return",
318
+ "POST",
319
+ request_object.to_json,
320
+ { 'X-AUTH-TOKEN' => @token }
321
+ )
322
+ if response && response.body == 'OK'
323
+ Vanagon::Driver.logger.info "#{@saved_job_id} has been scheduled for removal"
324
+ warn "#{@saved_job_id} has been scheduled for removal"
325
+ else
326
+ Vanagon::Driver.logger.info "#{@saved_job_id} could not be scheduled for removal: #{response.body}"
327
+ warn "#{@saved_job_id} could not be scheduled for removal"
328
+ end
329
+ rescue Vanagon::Error => e
330
+ Vanagon::Driver.logger.info "#{@saved_job_id} could not be scheduled for removal (#{e.message})"
331
+ warn "#{@saved_job_id} could not be scheduled for removal (#{e.message})"
332
+ end
333
+
334
+ private
335
+
336
+ def translated(response_body, job_id)
337
+ vmpooler_formatted_body = { 'job_id' => job_id }
338
+
339
+ response_body.each do |host| # in this context there should be only one host
340
+ vmpooler_formatted_body[host['type']] = { 'hostname' => host['hostname'] }
341
+ end
342
+ vmpooler_formatted_body['ok'] = true
343
+
344
+ vmpooler_formatted_body
345
+ end
346
+
347
+ def build_request_object
348
+ user = ENV['USER'] || ENV['USERNAME'] || 'unknown'
349
+
350
+ @saved_job_id = user + "-" + DateTime.now.strftime('%Q')
351
+ request_object = {
352
+ :resources => { build_host_name => 1 },
353
+ :job => {
354
+ :id => @saved_job_id,
355
+ :tags => {
356
+ :jenkins_build_url => ENV['BUILD_URL'],
357
+ :project => ENV['JOB_NAME'] || 'vanagon',
358
+ :created_by => user,
359
+ :user => user
360
+ },
361
+ },
362
+ :priority => 1, # DO NOT use priority 1 in automated CI runs
363
+ }
364
+ unless @token_vmpooler.nil?
365
+ request_object[:vm_token] = @token_vmpooler
366
+ end
367
+ request_object
368
+ end
369
+
370
+ def valid_json?(json)
371
+ JSON.parse(json)
372
+ return true
373
+ rescue TypeError, JSON::ParserError
374
+ return false
375
+ end
106
376
  end
107
377
  end
108
378
  end
@@ -1,6 +1,10 @@
1
1
  require 'vanagon/engine/base'
2
2
  require 'yaml'
3
3
 
4
+ ### Note this class is deprecated in favor of using the ABS Engine. The pooler has changed it's API with regards to
5
+ # getting VMs for ondemand provisioning and would need to be updated here.
6
+ # See DIO-1066
7
+
4
8
  class Vanagon
5
9
  class Engine
6
10
  class Pooler < Base
@@ -10,7 +14,7 @@ class Vanagon
10
14
  def initialize(platform, target = nil, **opts)
11
15
  super
12
16
 
13
- @available_poolers = ["http://vmpooler.delivery.puppetlabs.net", "https://nspooler-service-prod-1.delivery.puppetlabs.net"]
17
+ @available_poolers = ["https://vmpooler.delivery.puppetlabs.net", "https://nspooler-service-prod-1.delivery.puppetlabs.net"]
14
18
  @token = load_token
15
19
  @required_attributes << "vmpooler_template"
16
20
  end
@@ -63,7 +67,7 @@ class Vanagon
63
67
 
64
68
  # Read a vmpooler token from the yaml formatted vmfloaty config,
65
69
  # as outlined by the vmfloaty project:
66
- # https://github.com/briancain/vmfloaty
70
+ # https://github.com/puppetlabs/vmfloaty
67
71
  #
68
72
  # @return [String, nil] the vmfloaty vmpooler token value
69
73
  def read_vmfloaty_token(path = "~/.vmfloaty.yml")
@@ -75,7 +79,7 @@ class Vanagon
75
79
  end
76
80
  private :read_vmfloaty_token
77
81
 
78
- # This method is used to obtain a vm to build upon using the Puppet Labs'
82
+ # This method is used to obtain a vm to build upon using Puppet's internal
79
83
  # vmpooler (https://github.com/puppetlabs/vmpooler) or other pooler technologies
80
84
  # leveraging the same API
81
85
  # @raise [Vanagon::Error] if a target cannot be obtained
@@ -39,17 +39,16 @@ class Vanagon
39
39
  end
40
40
 
41
41
  # Simple wrapper around Net::HTTP. Will make a request of the given type to
42
- # the given url and return the body as parsed by JSON.
42
+ # the given url and return the response object
43
43
  #
44
44
  # @param url [String] The url to make the request against (needs to be parsable by URI
45
45
  # @param type [String] One of the supported request types (currently 'get', 'post', 'delete')
46
46
  # @param payload [String] The request body data payload used for POST and PUT
47
47
  # @param header [Hash] Send additional information in the HTTP request header
48
- # @return [Hash] The response body is parsed by JSON and returned
48
+ # @return [Net::HTTPAccepted] The response object
49
49
  # @raise [RuntimeError, Vanagon::Error] an exception is raised if the
50
- # action is not supported, or if there is a problem with the http request,
51
- # or if the response is not JSON
52
- def http_request(url, type, payload = {}.to_json, header = nil) # rubocop:disable Metrics/AbcSize
50
+ # action is not supported, or if there is a problem with the http request
51
+ def http_request_generic(url, type, payload = {}.to_json, header = nil) # rubocop:disable Metrics/AbcSize
53
52
  uri = URI.parse(url)
54
53
  http = Net::HTTP.new(uri.host, uri.port)
55
54
  http.use_ssl = true if uri.scheme == 'https'
@@ -77,14 +76,37 @@ class Vanagon
77
76
  end
78
77
 
79
78
  response = http.request(request)
80
-
81
- JSON.parse(response.body)
79
+ response
82
80
  rescue Errno::ETIMEDOUT, Timeout::Error, Errno::EINVAL, Errno::ECONNRESET,
83
81
  EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
84
82
  Net::ProtocolError => e
85
83
  raise Vanagon::Error.wrap(e, "Problem reaching #{url}. Is #{uri.host} down?")
84
+ end
85
+
86
+ # uses http_request_generic and returns the body as parsed by JSON.
87
+ # @param url [String] The url to make the request against (needs to be parsable by URI
88
+ # @param type [String] One of the supported request types (currently 'get', 'post', 'delete')
89
+ # @param payload [String] The request body data payload used for POST and PUT
90
+ # @param header [Hash] Send additional information in the HTTP request header
91
+ # @return [Hash] The response in JSON format
92
+ # @raise [RuntimeError, Vanagon::Error] an exception is raised if the response
93
+ # body cannot be parsed as JSON
94
+ def http_request(url, type, payload = {}.to_json, header = nil)
95
+ response = http_request_generic(url, type, payload, header)
96
+ JSON.parse(response.body)
86
97
  rescue JSON::ParserError => e
87
- raise Vanagon::Error.wrap(e, "#{uri.host} handed us a response that doesn't look like JSON.")
98
+ raise Vanagon::Error.wrap(e, "#{url} handed us a response that doesn't look like JSON.")
99
+ end
100
+
101
+ # uses http_request_generic and returns the response code.
102
+ # @param url [String] The url to make the request against (needs to be parsable by URI
103
+ # @param type [String] One of the supported request types (currently 'get', 'post', 'delete')
104
+ # @param payload [String] The request body data payload used for POST and PUT
105
+ # @param header [Hash] Send additional information in the HTTP request header
106
+ # @return [String] The response code eg 202, 200 etc
107
+ def http_request_code(url, type, payload = {}.to_json, header = nil)
108
+ response = http_request_generic(url, type, payload, header)
109
+ response.code
88
110
  end
89
111
 
90
112
  # Similar to rake's sh, the passed command will be executed and an
@@ -2,6 +2,7 @@ require 'vanagon/engine/always_be_scheduling'
2
2
  require 'vanagon/driver'
3
3
  require 'vanagon/platform'
4
4
  require 'logger'
5
+ require 'spec_helper'
5
6
 
6
7
  class Vanagon
7
8
  class Driver
@@ -45,6 +46,8 @@ describe 'Vanagon::Engine::AlwaysBeScheduling' do
45
46
  end")
46
47
  plat._platform
47
48
  }
49
+ let(:pooler_token_file) { File.expand_path('~/.vanagon-token') }
50
+ let(:floaty_config) { File.expand_path('~/.vmfloaty.yml') }
48
51
 
49
52
  describe '#validate_platform' do
50
53
  it 'returns true if the platform has the required attributes' do
@@ -81,4 +84,113 @@ describe 'Vanagon::Engine::AlwaysBeScheduling' do
81
84
  .to eq('always_be_scheduling')
82
85
  end
83
86
  end
84
- end
87
+
88
+ describe '#read_vanagon_token' do
89
+ it 'takes the first line for abs token, second line is optional' do
90
+ token_value = 'decade'
91
+ allow(File).to receive(:exist?)
92
+ .with(pooler_token_file)
93
+ .and_return(true)
94
+
95
+ allow(File).to receive(:read)
96
+ .with(pooler_token_file)
97
+ .and_return(token_value)
98
+
99
+ abs_service = Vanagon::Engine::AlwaysBeScheduling.new(platform, nil)
100
+ expect(abs_service.token).to eq('decade')
101
+ expect(abs_service.token_vmpooler).to eq(nil)
102
+ end
103
+ it 'takes the second line as vmpooler token' do
104
+ token_value = "decade\nanddaycade"
105
+ allow(File).to receive(:exist?)
106
+ .with(pooler_token_file)
107
+ .and_return(true)
108
+
109
+ allow(File).to receive(:read)
110
+ .with(pooler_token_file)
111
+ .and_return(token_value)
112
+
113
+ abs_service = Vanagon::Engine::AlwaysBeScheduling.new(platform, nil)
114
+ expect(abs_service.token).to eq('decade')
115
+ expect(abs_service.token_vmpooler).to eq('anddaycade')
116
+ end
117
+ end
118
+
119
+ describe '#read_vmfloaty_token' do
120
+ before :each do
121
+ allow(File).to receive(:exist?)
122
+ .with(pooler_token_file)
123
+ .and_return(false)
124
+
125
+ allow(File).to receive(:exist?)
126
+ .with(floaty_config)
127
+ .and_return(true)
128
+ end
129
+ token_value = 'decade'
130
+ it %(reads a token from '~/.vmfloaty.yml at the top level') do
131
+ allow(YAML).to receive(:load_file)
132
+ .with(floaty_config)
133
+ .and_return({'token' => token_value})
134
+
135
+ abs_service = Vanagon::Engine::AlwaysBeScheduling.new(platform, nil)
136
+ expect(abs_service.token).to eq(token_value)
137
+ expect(abs_service.token_vmpooler).to eq(nil)
138
+ end
139
+ it %(reads a token from '~/.vmfloaty.yml in the abs service') do
140
+ allow(YAML).to receive(:load_file)
141
+ .with(floaty_config)
142
+ .and_return({'services' =>
143
+ {'MYabs' => {'type'=>'abs', 'token'=>token_value, 'url'=>'foo'}}
144
+ })
145
+
146
+ abs_service = Vanagon::Engine::AlwaysBeScheduling.new(platform, nil)
147
+ expect(abs_service.token).to eq(token_value)
148
+ expect(abs_service.token_vmpooler).to eq(nil)
149
+ end
150
+ it %(reads a token from '~/.vmfloaty.yml in the abs service and includes the vmpooler token') do
151
+ vmp_token_value = 'deecade'
152
+ allow(YAML).to receive(:load_file)
153
+ .with(floaty_config)
154
+ .and_return({'services' =>
155
+ {'MYabs' => {'type'=>'abs', 'token'=>token_value, 'url'=>'foo', 'vmpooler_fallback' => 'myvmp'},
156
+ 'myvmp' => {'token'=>vmp_token_value, 'url'=>'bar'}}
157
+ })
158
+
159
+ abs_service = Vanagon::Engine::AlwaysBeScheduling.new(platform, nil)
160
+ expect(abs_service.token).to eq(token_value)
161
+ expect(abs_service.token_vmpooler).to eq(vmp_token_value)
162
+ end
163
+ end
164
+ describe '#select_target_from' do
165
+ it 'runs successfully' do
166
+ hostname = 'faint-whirlwind.puppet.com'
167
+ stub_request(:post, "https://foobar/request").
168
+ to_return({status: 202, body: "", headers: {}},{status: 200, body: '[{"hostname":"'+hostname+'","type":"aix-6.1-ppc","engine":"nspooler"}]', headers: {}})
169
+ abs_service = Vanagon::Engine::AlwaysBeScheduling.new(platform, nil)
170
+ abs_service.select_target_from("https://foobar")
171
+ expect(abs_service.target).to eq(hostname)
172
+ end
173
+ it 'returns a warning if the first request is not a 202' do
174
+ hostname = 'fainter-whirlwind.puppet.com'
175
+ stub_request(:post, "https://foobar/request").
176
+ to_return({status: 404, body: "", headers: {}},{status: 200, body: '[{"hostname":"'+hostname+'","type":"aix-6.1-ppc","engine":"nspooler"}]', headers: {}})
177
+ allow_any_instance_of(Object).to receive(:warn)
178
+ expect_any_instance_of(Object).to receive(:warn).with("failed to request ABS with code 404")
179
+ abs_service = Vanagon::Engine::AlwaysBeScheduling.new(platform, nil)
180
+ pooler = abs_service.select_target_from("https://foobar")
181
+ expect(pooler).to eq('')
182
+ end
183
+ it 'returns a warning and retries until request is a 200' do
184
+ hostname = 'faintest-whirlwind.puppet.com'
185
+ stub_request(:post, "https://foobar/request").
186
+ to_return({status: 202, body: "", headers: {}},
187
+ {status: 503, body: "", headers: {}},
188
+ {status: 200, body: '[{"hostname":"'+hostname+'","type":"aix-6.1-ppc","engine":"nspooler"}]', headers: {}})
189
+ allow_any_instance_of(Object).to receive(:warn)
190
+ expect_any_instance_of(Object).to receive(:warn).with(/Waiting 1 seconds to check if ABS request has been filled/)
191
+ abs_service = Vanagon::Engine::AlwaysBeScheduling.new(platform, nil)
192
+ abs_service.select_target_from("https://foobar")
193
+ expect(abs_service.target).to eq(hostname)
194
+ end
195
+ end
196
+ end
@@ -27,6 +27,8 @@ if defined? ::Aws
27
27
  end
28
28
 
29
29
  it 'returns "ec2" name' do
30
+ stub_request(:get, "http://169.254.169.254/latest/meta-data/iam/security-credentials/").
31
+ to_return(status: 200, body: "", headers: {})
30
32
  expect(Vanagon::Engine::Ec2.new(platform_ec2).name).to eq('ec2')
31
33
  end
32
34
  end
@@ -6,7 +6,7 @@ describe 'Vanagon::Engine::Pooler' do
6
6
  let (:platform_with_vcloud_name) {
7
7
  plat = Vanagon::Platform::DSL.new('debian-6-i386')
8
8
  plat.instance_eval("platform 'debian-6-i386' do |plat|
9
- plat.vcloud_name 'debian-6-i386'
9
+ plat.vmpooler_template 'debian-6-i386'
10
10
  end")
11
11
  plat._platform
12
12
  }
@@ -19,6 +19,7 @@ end
19
19
 
20
20
  require 'tmpdir'
21
21
  require 'vanagon'
22
+ require 'webmock/rspec'
22
23
 
23
24
  RSpec.configure do |c|
24
25
  c.before do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vanagon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.1
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet Labs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-19 00:00:00.000000000 Z
11
+ date: 2020-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docopt
@@ -288,41 +288,41 @@ signing_key:
288
288
  specification_version: 3
289
289
  summary: All of your packages will fit into this van with this one simple trick.
290
290
  test_files:
291
+ - spec/lib/vanagon/project/dsl_spec.rb
292
+ - spec/lib/vanagon/extensions/string_spec.rb
293
+ - spec/lib/vanagon/extensions/ostruct/json_spec.rb
294
+ - spec/lib/vanagon/extensions/set/json_spec.rb
295
+ - spec/lib/vanagon/platform/solaris_11_spec.rb
296
+ - spec/lib/vanagon/platform/dsl_spec.rb
291
297
  - spec/lib/vanagon/platform/rpm/aix_spec.rb
292
- - spec/lib/vanagon/platform/osx_spec.rb
293
298
  - spec/lib/vanagon/platform/windows_spec.rb
299
+ - spec/lib/vanagon/platform/osx_spec.rb
300
+ - spec/lib/vanagon/platform/solaris_10_spec.rb
294
301
  - spec/lib/vanagon/platform/rpm_spec.rb
295
302
  - spec/lib/vanagon/platform/deb_spec.rb
296
- - spec/lib/vanagon/platform/solaris_11_spec.rb
297
- - spec/lib/vanagon/platform/solaris_10_spec.rb
298
- - spec/lib/vanagon/platform/dsl_spec.rb
299
- - spec/lib/vanagon/driver_spec.rb
300
- - spec/lib/vanagon/project/dsl_spec.rb
301
- - spec/lib/vanagon/utilities_spec.rb
302
- - spec/lib/vanagon/cli_spec.rb
303
- - spec/lib/vanagon/environment_spec.rb
304
- - spec/lib/vanagon/component/rules_spec.rb
305
- - spec/lib/vanagon/component/source_spec.rb
306
- - spec/lib/vanagon/component/source/local_spec.rb
303
+ - spec/lib/vanagon/project_spec.rb
304
+ - spec/lib/vanagon/component/source/rewrite_spec.rb
307
305
  - spec/lib/vanagon/component/source/git_spec.rb
306
+ - spec/lib/vanagon/component/source/local_spec.rb
308
307
  - spec/lib/vanagon/component/source/http_spec.rb
309
- - spec/lib/vanagon/component/source/rewrite_spec.rb
308
+ - spec/lib/vanagon/component/rules_spec.rb
310
309
  - spec/lib/vanagon/component/dsl_spec.rb
311
- - spec/lib/vanagon/utilities/shell_utilities_spec.rb
312
- - spec/lib/vanagon/extensions/set/json_spec.rb
313
- - spec/lib/vanagon/extensions/string_spec.rb
314
- - spec/lib/vanagon/extensions/ostruct/json_spec.rb
310
+ - spec/lib/vanagon/component/source_spec.rb
311
+ - spec/lib/vanagon/common/user_spec.rb
312
+ - spec/lib/vanagon/common/pathname_spec.rb
315
313
  - spec/lib/vanagon/component_spec.rb
316
- - spec/lib/vanagon/platform_spec.rb
317
- - spec/lib/vanagon/engine/local_spec.rb
318
314
  - spec/lib/vanagon/engine/hardware_spec.rb
319
- - spec/lib/vanagon/engine/always_be_scheduling_spec.rb
320
- - spec/lib/vanagon/engine/pooler_spec.rb
321
- - spec/lib/vanagon/engine/base_spec.rb
322
315
  - spec/lib/vanagon/engine/ec2_spec.rb
316
+ - spec/lib/vanagon/engine/local_spec.rb
323
317
  - spec/lib/vanagon/engine/docker_spec.rb
324
- - spec/lib/vanagon/common/user_spec.rb
325
- - spec/lib/vanagon/common/pathname_spec.rb
326
- - spec/lib/vanagon/project_spec.rb
318
+ - spec/lib/vanagon/engine/pooler_spec.rb
319
+ - spec/lib/vanagon/engine/base_spec.rb
320
+ - spec/lib/vanagon/engine/always_be_scheduling_spec.rb
321
+ - spec/lib/vanagon/environment_spec.rb
322
+ - spec/lib/vanagon/cli_spec.rb
323
+ - spec/lib/vanagon/driver_spec.rb
324
+ - spec/lib/vanagon/utilities_spec.rb
325
+ - spec/lib/vanagon/platform_spec.rb
326
+ - spec/lib/vanagon/utilities/shell_utilities_spec.rb
327
327
  - spec/lib/makefile_spec.rb
328
328
  - spec/lib/git/rev_list_spec.rb