vanagon 0.16.0 → 0.19.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +48 -23
  3. data/bin/build +3 -1
  4. data/bin/build_host_info +3 -1
  5. data/bin/build_requirements +3 -1
  6. data/bin/inspect +3 -1
  7. data/bin/render +3 -1
  8. data/bin/repo +3 -1
  9. data/bin/ship +3 -1
  10. data/bin/sign +3 -1
  11. data/extras/completions/vanagon.bash +38 -0
  12. data/extras/completions/vanagon.zsh +41 -0
  13. data/lib/vanagon/cli.rb +12 -2
  14. data/lib/vanagon/cli/build.rb +12 -3
  15. data/lib/vanagon/cli/build_host_info.rb +12 -3
  16. data/lib/vanagon/cli/build_requirements.rb +13 -5
  17. data/lib/vanagon/cli/completion.rb +44 -0
  18. data/lib/vanagon/cli/inspect.rb +12 -3
  19. data/lib/vanagon/cli/list.rb +74 -0
  20. data/lib/vanagon/cli/render.rb +11 -2
  21. data/lib/vanagon/cli/ship.rb +6 -5
  22. data/lib/vanagon/cli/sign.rb +3 -2
  23. data/lib/vanagon/component.rb +11 -9
  24. data/lib/vanagon/component/dsl.rb +6 -5
  25. data/lib/vanagon/component/source.rb +2 -1
  26. data/lib/vanagon/component/source/git.rb +7 -6
  27. data/lib/vanagon/component/source/http.rb +3 -2
  28. data/lib/vanagon/component/source/local.rb +2 -1
  29. data/lib/vanagon/component/source/rewrite.rb +3 -2
  30. data/lib/vanagon/driver.rb +35 -34
  31. data/lib/vanagon/engine/always_be_scheduling.rb +272 -1
  32. data/lib/vanagon/engine/docker.rb +2 -1
  33. data/lib/vanagon/engine/ec2.rb +5 -4
  34. data/lib/vanagon/engine/hardware.rb +4 -3
  35. data/lib/vanagon/engine/pooler.rb +13 -8
  36. data/lib/vanagon/environment.rb +3 -2
  37. data/lib/vanagon/logger.rb +31 -0
  38. data/lib/vanagon/platform.rb +6 -5
  39. data/lib/vanagon/platform/deb.rb +2 -0
  40. data/lib/vanagon/platform/dsl.rb +5 -4
  41. data/lib/vanagon/platform/windows.rb +3 -1
  42. data/lib/vanagon/project.rb +20 -14
  43. data/lib/vanagon/project/dsl.rb +6 -5
  44. data/lib/vanagon/utilities.rb +35 -12
  45. data/resources/deb/control.erb +1 -1
  46. data/resources/osx/postinstall.erb +5 -1
  47. data/resources/rpm/project.spec.erb +1 -1
  48. data/resources/solaris/10/depend.erb +2 -2
  49. data/resources/solaris/10/postinstall.erb +10 -2
  50. data/resources/solaris/11/p5m.erb +2 -2
  51. data/spec/lib/vanagon/cli_spec.rb +143 -0
  52. data/spec/lib/vanagon/component/dsl_spec.rb +9 -2
  53. data/spec/lib/vanagon/component_spec.rb +3 -2
  54. data/spec/lib/vanagon/driver_spec.rb +1 -1
  55. data/spec/lib/vanagon/engine/always_be_scheduling_spec.rb +113 -1
  56. data/spec/lib/vanagon/engine/ec2_spec.rb +2 -0
  57. data/spec/lib/vanagon/engine/pooler_spec.rb +1 -1
  58. data/spec/spec_helper.rb +1 -0
  59. metadata +32 -27
@@ -1,4 +1,5 @@
1
1
  require 'fustigit'
2
+ require 'vanagon/logger'
2
3
  require 'vanagon/component/source/http'
3
4
  require 'vanagon/component/source/git'
4
5
  require 'vanagon/component/source/local'
@@ -66,7 +67,7 @@ class Vanagon
66
67
  timeout = 5
67
68
  if Vanagon::Component::Source::Git.valid_remote?(uri, timeout)
68
69
  if uri =~ /^http/
69
- warn "Passing git URLs as http(s) addresses is deprecated! Please prefix your source URL with `git:`"
70
+ VanagonLogger.info "Passing git URLs as http(s) addresses is deprecated! Please prefix your source URL with `git:`"
70
71
  end
71
72
  return :git
72
73
  end
@@ -1,5 +1,6 @@
1
1
  require 'vanagon/utilities'
2
2
  require 'vanagon/errors'
3
+ require 'vanagon/logger'
3
4
  # This stupid library requires a capital 'E' in its name
4
5
  # but it provides a wealth of useful constants
5
6
  require 'English'
@@ -81,7 +82,7 @@ class Vanagon
81
82
  # There is no md5 to manually verify here, so this is a noop.
82
83
  def verify
83
84
  # nothing to do here, so just tell users that and return
84
- warn "Nothing to verify for '#{dirname}' (using Git reference '#{ref}')"
85
+ VanagonLogger.info "Nothing to verify for '#{dirname}' (using Git reference '#{ref}')"
85
86
  end
86
87
 
87
88
  # The dirname to reference when building from the repo
@@ -131,8 +132,8 @@ class Vanagon
131
132
  # Clone a remote repo, make noise about it, and fail entirely
132
133
  # if we're unable to retrieve the remote repo
133
134
  def clone!
134
- warn "Cloning Git repo '#{url}'"
135
- warn "Successfully cloned '#{dirname}'" if clone
135
+ VanagonLogger.info "Cloning Git repo '#{url}'"
136
+ VanagonLogger.info "Successfully cloned '#{dirname}'" if clone
136
137
  rescue ::Git::GitExecuteError
137
138
  raise Vanagon::InvalidRepo, "Unable to clone from '#{url}'"
138
139
  end
@@ -141,7 +142,7 @@ class Vanagon
141
142
  # Checkout desired ref/sha, make noise about it, and fail
142
143
  # entirely if we're unable to checkout that given ref/sha
143
144
  def checkout!
144
- warn "Checking out '#{ref}' from Git repo '#{dirname}'"
145
+ VanagonLogger.info "Checking out '#{ref}' from Git repo '#{dirname}'"
145
146
  clone.checkout(ref)
146
147
  rescue ::Git::GitExecuteError
147
148
  raise Vanagon::CheckoutFailed, "unable to checkout #{ref} from '#{url}'"
@@ -151,7 +152,7 @@ class Vanagon
151
152
  # Attempt to update submodules, and do not panic
152
153
  # if there are no submodules to initialize
153
154
  def update_submodules
154
- warn "Attempting to update submodules for repo '#{dirname}'"
155
+ VanagonLogger.info "Attempting to update submodules for repo '#{dirname}'"
155
156
  clone.update_submodules(init: true)
156
157
  end
157
158
  private :update_submodules
@@ -163,7 +164,7 @@ class Vanagon
163
164
  def describe
164
165
  clone.describe(ref, tags: true)
165
166
  rescue ::Git::GitExecuteError
166
- warn "Directory '#{dirname}' cannot be versioned by Git. Maybe it hasn't been tagged yet?"
167
+ VanagonLogger.info "Directory '#{dirname}' cannot be versioned by Git. Maybe it hasn't been tagged yet?"
167
168
  end
168
169
  private :describe
169
170
  end
@@ -1,4 +1,5 @@
1
1
  require 'vanagon/utilities'
2
+ require 'vanagon/logger'
2
3
  require 'vanagon/component/source/local'
3
4
  require 'net/http'
4
5
  require 'uri'
@@ -93,7 +94,7 @@ class Vanagon
93
94
  #
94
95
  # @raise [RuntimeError] an exception is raised if the sum does not match the sum of the file
95
96
  def verify # rubocop:disable Metrics/AbcSize
96
- warn "Verifying file: #{file} against sum: '#{sum}'"
97
+ VanagonLogger.info "Verifying file: #{file} against sum: '#{sum}'"
97
98
  actual = get_sum(File.join(workdir, file), sum_type)
98
99
  return true if sum == actual
99
100
 
@@ -107,7 +108,7 @@ class Vanagon
107
108
  uri = URI.parse(target_url.to_s)
108
109
  target_file ||= File.basename(uri.path)
109
110
 
110
- warn "Downloading file '#{target_file}' from url '#{target_url}'"
111
+ VanagonLogger.info "Downloading file '#{target_file}' from url '#{target_url}'"
111
112
 
112
113
  Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
113
114
  http.request(Net::HTTP::Get.new(uri, headers)) do |response|
@@ -1,4 +1,5 @@
1
1
  require 'vanagon/utilities'
2
+ require 'vanagon/logger'
2
3
 
3
4
  class Vanagon
4
5
  class Component
@@ -57,7 +58,7 @@ class Vanagon
57
58
  #
58
59
  # @raise [RuntimeError, Vanagon::Error] an exception is raised if the URI scheme cannot be handled
59
60
  def copy
60
- warn "Copying file '#{url.basename}' to workdir"
61
+ VanagonLogger.info "Copying file '#{url.basename}' to workdir"
61
62
 
62
63
  FileUtils.cp_r(url, file)
63
64
  end
@@ -1,4 +1,5 @@
1
1
  require 'vanagon/component/source'
2
+ require 'vanagon/logger'
2
3
 
3
4
  class Vanagon
4
5
  class Component
@@ -16,7 +17,7 @@ class Vanagon
16
17
  # @deprecated Please use the component DSL method #mirror(<URI>)
17
18
  # instead. This method will be removed before Vanagon 1.0.0.
18
19
  def register_rewrite_rule(protocol, rule)
19
- warn <<-HERE.undent
20
+ VanagonLogger.info <<-HERE.undent
20
21
  rewrite rule support is deprecated and will be removed before Vanagon 1.0.0.
21
22
  Rewritten URLs will be automatically converted into mirror URLs for now but
22
23
  please use the component DSL method '#mirror url' to define new mirror URL
@@ -76,7 +77,7 @@ class Vanagon
76
77
  def parse_and_rewrite(uri)
77
78
  return uri if rewrite_rules.empty?
78
79
  if !!uri.match(/^git:http/)
79
- warn <<-HERE.undent
80
+ VanagonLogger.info <<-HERE.undent
80
81
  `fustigit` parsing doesn't get along with how we specify the source
81
82
  type by prefixing `git`. As `rewrite_rules` are deprecated, we'll
82
83
  replace `git:http` with `http` in your uri. At some point this will
@@ -4,6 +4,7 @@ require 'vanagon/component'
4
4
  require 'vanagon/utilities'
5
5
  require 'vanagon/common'
6
6
  require 'vanagon/errors'
7
+ require 'vanagon/logger'
7
8
  require 'tmpdir'
8
9
  require 'logger'
9
10
 
@@ -20,33 +21,29 @@ class Vanagon
20
21
  @retry_count ||= @project.retry_count || ENV["VANAGON_RETRY_COUNT"] || 1
21
22
  end
22
23
 
23
- def initialize(platform, project, options = { workdir: nil, configdir: nil, target: nil, engine: nil, components: nil, skipcheck: false, verbose: false, preserve: false, only_build: nil, remote_workdir: nil }) # rubocop:disable Metrics/AbcSize
24
- @verbose = options[:verbose]
25
- @preserve = options[:preserve]
24
+ def initialize(platform, project, options = {}) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
25
+ @options = options
26
+ @verbose = options[:verbose] || false
27
+ @preserve = options[:preserve] || false
26
28
  @workdir = options[:workdir] || Dir.mktmpdir
27
29
 
28
30
  @@configdir = options[:configdir] || File.join(Dir.pwd, "configs")
29
31
  components = options[:components] || []
30
32
  only_build = options[:only_build]
31
- target = options[:target]
32
33
 
33
34
  @platform = Vanagon::Platform.load_platform(platform, File.join(@@configdir, "platforms"))
34
- @project = Vanagon::Project.load_project(project, File.join(@@configdir, "projects"), @platform, components)
35
+ @project = Vanagon::Project.load_project(
36
+ project, File.join(@@configdir, "projects"), @platform, components
37
+ )
35
38
  @project.settings[:verbose] = options[:verbose]
36
- @project.settings[:skipcheck] = options[:skipcheck]
39
+ @project.settings[:skipcheck] = options[:skipcheck] || false
37
40
  filter_out_components(only_build) if only_build
38
41
  loginit('vanagon_hosts.log')
39
42
 
40
43
  @remote_workdir = options[:"remote-workdir"]
41
44
 
42
- if options[:engine]
43
- # Use the explicitly configured engine.
44
- load_engine_object(options[:engine], @platform, target)
45
- else
46
- # Use 'pooler' as a default, but also apply selection logic that may
47
- # choose something different based on platform configuration.
48
- load_engine('pooler', @platform, target)
49
- end
45
+ engine = pick_engine(options)
46
+ load_engine_object(engine, @platform, options[:target])
50
47
  end
51
48
 
52
49
  def filter_out_components(only_build)
@@ -54,29 +51,31 @@ class Vanagon
54
51
  # flatten all the results in to one array and set project.components to that.
55
52
  @project.components = only_build.flat_map { |comp| @project.filter_component(comp) }.uniq
56
53
  if @verbose
57
- warn "Only building:"
58
- @project.components.each { |comp| warn comp.name }
54
+ VanagonLogger.info "Only building:"
55
+ @project.components.each { |comp| VanagonLogger.info comp.name }
59
56
  end
60
57
  end
61
58
 
62
- def load_engine(engine_type, platform, target)
63
- if engine_type != 'always_be_scheduling'
64
- if platform.build_hosts
65
- engine_type = 'hardware'
66
- elsif platform.aws_ami
67
- engine_type = 'ec2'
68
- elsif platform.docker_image
69
- engine_type = 'docker'
70
- elsif target
71
- engine_type = 'base'
72
- end
59
+ def pick_engine(options) # rubocop:disable Metrics/PerceivedComplexity
60
+ if options[:engine] && !options[:target]
61
+ options[:engine]
62
+ elsif @platform.build_hosts
63
+ 'hardware'
64
+ elsif @platform.aws_ami
65
+ 'ec2'
66
+ elsif @platform.docker_image
67
+ 'docker'
68
+ elsif options[:target]
69
+ 'base'
70
+ else
71
+ 'always_be_scheduling'
73
72
  end
74
- load_engine_object(engine_type, platform, target)
75
73
  end
76
74
 
77
75
  def load_engine_object(engine_type, platform, target)
78
76
  require "vanagon/engine/#{engine_type}"
79
- @engine = Object::const_get("Vanagon::Engine::#{camelize(engine_type)}").new(platform, target, remote_workdir: remote_workdir)
77
+ @engine = Object::const_get("Vanagon::Engine::#{camelize(engine_type)}")
78
+ .new(platform, target, remote_workdir: remote_workdir)
80
79
  rescue StandardError, ScriptError => e
81
80
  raise Vanagon::Error.wrap(e, "Could not load the desired engine '#{engine_type}'")
82
81
  end
@@ -103,7 +102,9 @@ class Vanagon
103
102
  { "name" => @engine.build_host_name, "engine" => @engine.name }
104
103
  end
105
104
 
106
- # Returns the set difference between the build_requires and the components to get a list of external dependencies that need to be installed.
105
+ # Returns the set difference between the build_requires and the
106
+ # components to get a list of external dependencies that need to
107
+ # be installed.
107
108
  def list_build_dependencies
108
109
  @project.components.map(&:build_requires).flatten.uniq - @project.components.map(&:name)
109
110
  end
@@ -134,7 +135,7 @@ class Vanagon
134
135
  end
135
136
 
136
137
  @engine.startup(workdir)
137
- warn "Target is #{@engine.target}"
138
+ VanagonLogger.info "Target is #{@engine.target}"
138
139
  Vanagon::Utilities.retry_with_timeout(retry_count, timeout) do
139
140
  install_build_dependencies
140
141
  end
@@ -159,8 +160,8 @@ class Vanagon
159
160
  @engine.teardown
160
161
  cleanup_workdir
161
162
  end
162
- warn e
163
- warn e.backtrace.join("\n")
163
+ VanagonLogger.error(e)
164
+ VanagonLogger.error e.backtrace.join("\n")
164
165
  raise e
165
166
  ensure
166
167
  if ["hardware", "ec2"].include?(@engine.name)
@@ -174,7 +175,7 @@ class Vanagon
174
175
  raise Vanagon::Error, "Project requires a version set, all is lost."
175
176
  end
176
177
 
177
- warn "rendering Makefile"
178
+ VanagonLogger.info "rendering Makefile"
178
179
  @project.fetch_sources(workdir, retry_count, timeout)
179
180
  @project.make_bill_of_materials(workdir)
180
181
  @project.generate_packaging_artifacts(workdir)
@@ -1,5 +1,7 @@
1
- require 'vanagon/engine/base'
2
1
  require 'json'
2
+ require 'vanagon/engine/base'
3
+ require 'vanagon/logger'
4
+ require 'yaml'
3
5
 
4
6
  class Vanagon
5
7
  class Engine
@@ -82,9 +84,15 @@ class Vanagon
82
84
  # {"name":"aix-53-ppc","engine":"always_be_scheduling"}
83
85
  # ```
84
86
  class AlwaysBeScheduling < Base
87
+ attr_reader :token
88
+ attr_reader :token_vmpooler
89
+
85
90
  def initialize(platform, target, **opts)
86
91
  super
87
92
 
93
+ @available_abs_endpoint = "https://abs-prod.k8s.infracore.puppet.net/api/v2"
94
+ @token_vmpooler = ENV['VMPOOLER_TOKEN']
95
+ @token = load_token
88
96
  Vanagon::Driver.logger.debug "AlwaysBeScheduling engine invoked."
89
97
  end
90
98
 
@@ -94,6 +102,7 @@ class Vanagon
94
102
  end
95
103
 
96
104
  # return the platform name as the "host" name
105
+ # order of preference: abs_resource_name, vmpooler_template or name
97
106
  def build_host_name
98
107
  if @platform.abs_resource_name
99
108
  @platform.abs_resource_name
@@ -103,6 +112,268 @@ class Vanagon
103
112
  @platform.name
104
113
  end
105
114
  end
115
+
116
+ # Retrieve the ABS token from an environment variable
117
+ # ("ABS_TOKEN") or from a number of potential configuration
118
+ # files (~/.vanagon-token or ~/.vmfloaty.yml).
119
+ # @return [String, nil] token for use with the vmpooler
120
+ def load_token
121
+ ENV['ABS_TOKEN'] || token_from_file
122
+ end
123
+
124
+ # a wrapper method around retrieving a token,
125
+ # with an explicitly ordered preference for a Vanagon-specific
126
+ # token file or a preexisting vmfoaty yaml file.
127
+ #
128
+ # @return [String, nil] token for use with the vmpooler
129
+ def token_from_file
130
+ read_vanagon_token || read_vmfloaty_token
131
+ end
132
+ private :token_from_file
133
+
134
+ # Read an ABS/vmpooler token from the plaintext vanagon-token file,
135
+ # as outlined in the project README.
136
+ # The first line should be the ABS token
137
+ # and the second (optional) line should be the vmpooler token
138
+ # Saves the second line into the @token_vmpooler variable
139
+ #
140
+ # @return [String, nil] the vanagon vmpooler token value
141
+ def read_vanagon_token(path = "~/.vanagon-token")
142
+ absolute_path = File.expand_path(path)
143
+ return nil unless File.exist?(absolute_path)
144
+
145
+ VanagonLogger.info "Reading ABS token from: #{path}"
146
+ contents = File.read(absolute_path).chomp
147
+ lines = contents.each_line.map(&:chomp)
148
+
149
+ abs = lines.shift
150
+ @token_vmpooler = lines.shift
151
+
152
+ VanagonLogger.info "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?
153
+ return abs
154
+ end
155
+ private :read_vanagon_token
156
+
157
+ # Read a vmpooler token from the yaml formatted vmfloaty config,
158
+ # as outlined by the vmfloaty project:
159
+ # https://github.com/puppetlabs/vmfloaty
160
+ #
161
+ # It returns the top-level token value or the first 'abs' service
162
+ # token value it finds in the services list
163
+ # it sets @token_vmpooler with the optional vmpooler token
164
+ # if the abs services has a vmpooler_fallback, and that service
165
+ # has a token eg
166
+ #
167
+ # TOP-LEVEL DEFINED => would get only the abs token
168
+ # user: 'jdoe'
169
+ # url: 'https://abs.example.net'
170
+ # token: '456def789'
171
+ #
172
+ # MULTIPLE SERVICES DEFINED => would get the abs token in 'abs-prod' and the vmpooler token in 'vmpooler-prod'
173
+ # user: 'jdoe'
174
+ # services:
175
+ # abs-prod:
176
+ # type: 'abs'
177
+ # url: 'https://abs.example.net/api/v2'
178
+ # token: '123abc456'
179
+ # vmpooler_fallback: 'vmpooler-prod'
180
+ # vmpooler-dev:
181
+ # type: 'vmpooler'
182
+ # url: 'https://vmpooler-dev.example.net'
183
+ # token: '987dsa654'
184
+ # vmpooler-prod:
185
+ # type: 'vmpooler'
186
+ # url: 'https://vmpooler.example.net'
187
+ # token: '456def789'
188
+ #
189
+ # @return [String, nil] the vmfloaty vmpooler token value
190
+ def read_vmfloaty_token(path = "~/.vmfloaty.yml") # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
191
+ absolute_path = File.expand_path(path)
192
+ return nil unless File.exist?(absolute_path)
193
+
194
+ yaml_config = YAML.load_file(absolute_path)
195
+ abs_token = nil
196
+ abs_service_name = nil
197
+ if yaml_config['token'] # top level
198
+ abs_token = yaml_config['token']
199
+ elsif yaml_config['services']
200
+ yaml_config['services'].each do |name, value|
201
+ if value['type'] == "abs" && value['token']
202
+ abs_token = value['token']
203
+ abs_service_name = name
204
+ vmpooler_fallback = value['vmpooler_fallback']
205
+ unless vmpooler_fallback.nil? || yaml_config['services'][vmpooler_fallback].nil? || yaml_config['services'][vmpooler_fallback]['token'].nil?
206
+ @token_vmpooler = yaml_config['services'][vmpooler_fallback]['token']
207
+ end
208
+ break
209
+ end
210
+ end
211
+ end
212
+ message = "Reading ABS token from: #{path}"
213
+ if abs_service_name
214
+ message.concat(" for service named #{abs_service_name}")
215
+ if @token_vmpooler.nil?
216
+ 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")
217
+ else
218
+ message.concat(" with a vmpooler_fallback token")
219
+ end
220
+ end
221
+ VanagonLogger.info message
222
+ return abs_token
223
+ end
224
+ private :read_vmfloaty_token
225
+
226
+ # This method is used to obtain a vm to build upon using Puppet's internal
227
+ # ABS (https://github.com/puppetlabs/always-be-scheduling) which is a level of abstraction for other
228
+ # engines using similar APIs
229
+ # @raise [Vanagon::Error] if a target cannot be obtained
230
+ def select_target
231
+ @pooler = select_target_from(@available_abs_endpoint)
232
+ raise Vanagon::Error, "Something went wrong getting a target vm to build on" if @pooler.empty?
233
+ end
234
+
235
+ # Attempt to provision a host from a specific pooler.
236
+ def select_target_from(pooler) # rubocop:disable Metrics/AbcSize
237
+ request_object = build_request_object
238
+
239
+ VanagonLogger.info "Requesting VMs with job_id: #{@saved_job_id}. Will poll for up to an hour."
240
+ #the initial request is always replied with "come back again"
241
+ response = Vanagon::Utilities.http_request_generic(
242
+ "#{pooler}/request",
243
+ 'POST',
244
+ request_object.to_json,
245
+ { 'X-AUTH-TOKEN' => @token }
246
+ )
247
+
248
+ unless response.code == "202"
249
+ VanagonLogger.info "failed to request ABS with code #{response.code}"
250
+ if valid_json?(response.body)
251
+ response_json = JSON.parse(response.body)
252
+ VanagonLogger.info "reason: #{response_json['reason']}"
253
+ end
254
+ return ''
255
+ end
256
+ response_body = check_queue(pooler, request_object)
257
+
258
+ return '' unless response_body["ok"]
259
+ @target = response_body[build_host_name]['hostname']
260
+ Vanagon::Driver.logger.info "Reserving #{@target} (#{build_host_name}) [#{@token ? 'token used' : 'no token used'}]"
261
+ return pooler
262
+ end
263
+
264
+ # main loop where the status of the request is checked, to see if the request
265
+ # has been allocated
266
+ def check_queue(pooler, request_object)
267
+ retries = 360 # ~ one hour
268
+ response_body = nil
269
+ begin
270
+ (1..retries).each do |i|
271
+ response = Vanagon::Utilities.http_request_generic(
272
+ "#{pooler}/request",
273
+ 'POST',
274
+ request_object.to_json,
275
+ { 'X-AUTH-TOKEN' => @token }
276
+ )
277
+ response_body = validate_queue_status_response(response.code, response.body)
278
+ break if response_body
279
+
280
+ sleep_seconds = 10 if i >= 10
281
+ sleep_seconds = i if i < 10
282
+ VanagonLogger.info "Waiting #{sleep_seconds} seconds to check if ABS request has been filled. (x#{i})"
283
+
284
+ sleep(sleep_seconds)
285
+ end
286
+ rescue SystemExit, Interrupt
287
+ VanagonLogger.error "\nVanagon interrupted during mains ABS polling. Make sure you delete the requested job_id #{@saved_job_id}"
288
+ raise
289
+ end
290
+ response_body = translated(response_body, @saved_job_id)
291
+ response_body
292
+ end
293
+
294
+ def validate_queue_status_response(status_code, body)
295
+ case status_code
296
+ when "200"
297
+ return JSON.parse(body) unless body.empty? || !valid_json?(body)
298
+ when "202"
299
+ return nil
300
+ when "401"
301
+ raise Vanagon::Error, "HTTP #{status_code}: The token provided could not authenticate.\n#{body}"
302
+ when "503"
303
+ return nil
304
+ else
305
+ raise Vanagon::Error, "HTTP #{status_code}: request to ABS failed!\n#{body}"
306
+ end
307
+ end
308
+
309
+ # This method is used to tell the ABS to delete the job_id requested
310
+ # otherwise the resources will eventually get allocated asynchronously
311
+ # and will keep running until the end of their lifetime.
312
+ def teardown # rubocop:disable Metrics/AbcSize
313
+ request_object = {
314
+ 'job_id' => @saved_job_id,
315
+ }
316
+
317
+ response = Vanagon::Utilities.http_request_generic(
318
+ "#{@available_abs_endpoint}/return",
319
+ "POST",
320
+ request_object.to_json,
321
+ { 'X-AUTH-TOKEN' => @token }
322
+ )
323
+ if response && response.body == 'OK'
324
+ Vanagon::Driver.logger.info "#{@saved_job_id} has been scheduled for removal"
325
+ VanagonLogger.info "#{@saved_job_id} has been scheduled for removal"
326
+ else
327
+ Vanagon::Driver.logger.info "#{@saved_job_id} could not be scheduled for removal: #{response.body}"
328
+ VanagonLogger.info "#{@saved_job_id} could not be scheduled for removal"
329
+ end
330
+ rescue Vanagon::Error => e
331
+ Vanagon::Driver.logger.info "#{@saved_job_id} could not be scheduled for removal (#{e.message})"
332
+ VanagonLogger.info "#{@saved_job_id} could not be scheduled for removal (#{e.message})"
333
+ end
334
+
335
+ private
336
+
337
+ def translated(response_body, job_id)
338
+ vmpooler_formatted_body = { 'job_id' => job_id }
339
+
340
+ response_body.each do |host| # in this context there should be only one host
341
+ vmpooler_formatted_body[host['type']] = { 'hostname' => host['hostname'] }
342
+ end
343
+ vmpooler_formatted_body['ok'] = true
344
+
345
+ vmpooler_formatted_body
346
+ end
347
+
348
+ def build_request_object
349
+ user = ENV['USER'] || ENV['USERNAME'] || 'unknown'
350
+
351
+ @saved_job_id = user + "-" + DateTime.now.strftime('%Q')
352
+ request_object = {
353
+ :resources => { build_host_name => 1 },
354
+ :job => {
355
+ :id => @saved_job_id,
356
+ :tags => {
357
+ :jenkins_build_url => ENV['BUILD_URL'],
358
+ :project => ENV['JOB_NAME'] || 'vanagon',
359
+ :created_by => user,
360
+ :user => user
361
+ },
362
+ },
363
+ :priority => 1, # DO NOT use priority 1 in automated CI runs
364
+ }
365
+ unless @token_vmpooler.nil?
366
+ request_object[:vm_token] = @token_vmpooler
367
+ end
368
+ request_object
369
+ end
370
+
371
+ def valid_json?(json)
372
+ JSON.parse(json)
373
+ return true
374
+ rescue TypeError, JSON::ParserError
375
+ return false
376
+ end
106
377
  end
107
378
  end
108
379
  end