vanagon 0.16.1 → 0.17.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
  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