vmfloaty 0.9.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,11 +7,13 @@ require 'vmfloaty/ssh'
7
7
 
8
8
  class Service
9
9
  attr_reader :config
10
+ attr_accessor :silent
10
11
 
11
12
  def initialize(options, config_hash = {})
12
13
  options ||= Commander::Command::Options.new
13
14
  @config = Utils.get_service_config config_hash, options
14
15
  @service_object = Utils.get_service_object @config['type']
16
+ @silent = false
15
17
  end
16
18
 
17
19
  def method_missing(method_name, *args, &block)
@@ -36,7 +38,7 @@ class Service
36
38
 
37
39
  def user
38
40
  unless @config['user']
39
- puts "Enter your #{@config['url']} service username:"
41
+ FloatyLogger.info "Enter your #{@config['url']} service username:"
40
42
  @config['user'] = STDIN.gets.chomp
41
43
  end
42
44
  @config['user']
@@ -44,7 +46,7 @@ class Service
44
46
 
45
47
  def token
46
48
  unless @config['token']
47
- puts 'No token found. Retrieving a token...'
49
+ FloatyLogger.info 'No token found. Retrieving a token...'
48
50
  @config['token'] = get_new_token(nil)
49
51
  end
50
52
  @config['token']
@@ -75,10 +77,14 @@ class Service
75
77
  @service_object.list_active verbose, url, token, user
76
78
  end
77
79
 
78
- def retrieve(verbose, os_types, use_token = true)
79
- puts 'Requesting a vm without a token...' unless use_token
80
+ def retrieve(verbose, os_types, use_token = true, ondemand = nil)
81
+ FloatyLogger.info 'Requesting a vm without a token...' unless use_token
80
82
  token_value = use_token ? token : nil
81
- @service_object.retrieve verbose, os_types, token_value, url, user, @config
83
+ @service_object.retrieve verbose, os_types, token_value, url, user, @config, ondemand
84
+ end
85
+
86
+ def wait_for_request(verbose, requestid)
87
+ @service_object.wait_for_request verbose, requestid, url
82
88
  end
83
89
 
84
90
  def ssh(verbose, host_os, use_token = true)
@@ -87,20 +93,11 @@ class Service
87
93
  begin
88
94
  token_value = token || get_new_token(verbose)
89
95
  rescue TokenError => e
90
- STDERR.puts e
91
- STDERR.puts 'Could not get token... requesting vm without a token anyway...'
96
+ FloatyLogger.error e
97
+ FloatyLogger.info 'Could not get token... requesting vm without a token anyway...'
92
98
  end
93
99
  end
94
- Ssh.ssh(verbose, host_os, token_value, url)
95
- end
96
-
97
- def pretty_print_running(verbose, hostnames = [])
98
- if hostnames.empty?
99
- puts 'You have no running VMs.'
100
- else
101
- puts 'Running VMs:'
102
- @service_object.pretty_print_hosts(verbose, hostnames, url)
103
- end
100
+ Ssh.ssh(verbose, self, host_os, token_value)
104
101
  end
105
102
 
106
103
  def query(verbose, hostname)
@@ -108,6 +105,7 @@ class Service
108
105
  end
109
106
 
110
107
  def modify(verbose, hostname, modify_hash)
108
+ maybe_use_vmpooler
111
109
  @service_object.modify verbose, url, hostname, token, modify_hash
112
110
  end
113
111
 
@@ -120,18 +118,35 @@ class Service
120
118
  end
121
119
 
122
120
  def summary(verbose)
121
+ maybe_use_vmpooler
123
122
  @service_object.summary verbose, url
124
123
  end
125
124
 
126
125
  def snapshot(verbose, hostname)
126
+ maybe_use_vmpooler
127
127
  @service_object.snapshot verbose, url, hostname, token
128
128
  end
129
129
 
130
130
  def revert(verbose, hostname, snapshot_sha)
131
+ maybe_use_vmpooler
131
132
  @service_object.revert verbose, url, hostname, token, snapshot_sha
132
133
  end
133
134
 
134
135
  def disk(verbose, hostname, disk)
136
+ maybe_use_vmpooler
135
137
  @service_object.disk(verbose, url, hostname, token, disk)
136
138
  end
139
+
140
+ # some methods do not exist for ABS, and if possible should target the Pooler service
141
+ def maybe_use_vmpooler
142
+ if @service_object.is_a?(ABS.class)
143
+ if !self.silent
144
+ FloatyLogger.info "The service in use is ABS, but the requested method should run against vmpooler directly, using fallback_vmpooler config from ~/.vmfloaty.yml"
145
+ self.silent = true
146
+ end
147
+
148
+ @config = Utils.get_vmpooler_service_config(@config['vmpooler_fallback'])
149
+ @service_object = Pooler
150
+ end
151
+ end
137
152
  end
@@ -14,21 +14,27 @@ class Ssh
14
14
  nil
15
15
  end
16
16
 
17
- def self.ssh(verbose, host_os, token, url)
17
+ def self.command_string(verbose, service, host_os, use_token)
18
18
  ssh_path = which('ssh')
19
19
  raise 'Could not determine path to ssh' unless ssh_path
20
20
 
21
21
  os_types = {}
22
22
  os_types[host_os] = 1
23
23
 
24
- response = Pooler.retrieve(verbose, os_types, token, url)
25
- raise "Could not get vm from vmpooler:\n #{response}" unless response['ok']
24
+ response = service.retrieve(verbose, os_types, use_token)
25
+ raise "Could not get vm from #{service.type}:\n #{response}" unless response['ok']
26
26
 
27
27
  user = /win/.match?(host_os) ? 'Administrator' : 'root'
28
28
 
29
- hostname = "#{response[host_os]['hostname']}.#{response['domain']}"
30
- cmd = "#{ssh_path} #{user}@#{hostname}"
29
+ hostname = response[host_os]['hostname']
30
+ hostname = response[host_os]['hostname'][0] if response[host_os]['hostname'].is_a?(Array)
31
+ hostname = "#{hostname}.#{response['domain']}" unless hostname.end_with?('puppetlabs.net')
31
32
 
33
+ "#{ssh_path} #{user}@#{hostname}"
34
+ end
35
+
36
+ def self.ssh(verbose, service, host_os, use_token)
37
+ cmd = command_string(verbose, service, host_os, use_token)
32
38
  # TODO: Should this respect more ssh settings? Can it be configured
33
39
  # by users ssh config and does this respect those settings?
34
40
  Kernel.exec(cmd)
@@ -3,6 +3,7 @@
3
3
  require 'vmfloaty/abs'
4
4
  require 'vmfloaty/nonstandard_pooler'
5
5
  require 'vmfloaty/pooler'
6
+ require 'vmfloaty/conf'
6
7
 
7
8
  class Utils
8
9
  # TODO: Takes the json response body from an HTTP GET
@@ -45,7 +46,12 @@ class Utils
45
46
 
46
47
  result = {}
47
48
 
48
- response_body.each do |os, value|
49
+ # ABS has a job_id associated with hosts so pass that along
50
+ abs_job_id = response_body.delete('job_id')
51
+ result['job_id'] = abs_job_id unless abs_job_id.nil?
52
+
53
+ filtered_response_body = response_body.reject { |key, _| key == 'request_id' || key == 'ready' }
54
+ filtered_response_body.each do |os, value|
49
55
  hostnames = Array(value['hostname'])
50
56
  hostnames.map! { |host| "#{host}.#{domain}" } if domain
51
57
  result[os] = hostnames
@@ -56,7 +62,8 @@ class Utils
56
62
 
57
63
  def self.format_host_output(hosts)
58
64
  hosts.flat_map do |os, names|
59
- names.map { |name| "- #{name} (#{os})" }
65
+ # Assume hosts are stored in Arrays and ignore everything else
66
+ names.map { |name| "- #{name} (#{os})" } if names.is_a? Array
60
67
  end.join("\n")
61
68
  end
62
69
 
@@ -77,41 +84,96 @@ class Utils
77
84
  os_types
78
85
  end
79
86
 
80
- def self.pretty_print_hosts(verbose, service, hostnames = [])
87
+ def self.print_fqdn_for_host(service, hostname, host_data)
88
+ case service.type
89
+ when 'ABS'
90
+ abs_hostnames = []
91
+
92
+ host_data['allocated_resources'].each do |vm_name, _i|
93
+ abs_hostnames << vm_name['hostname']
94
+ end
95
+
96
+ puts abs_hostnames.join("\n")
97
+ when 'Pooler'
98
+ puts "#{hostname}.#{host_data['domain']}"
99
+ when 'NonstandardPooler'
100
+ puts host_data['fqdn']
101
+ else
102
+ raise "Invalid service type #{service.type}"
103
+ end
104
+ end
105
+
106
+ def self.pretty_print_hosts(verbose, service, hostnames = [], print_to_stderr = false, indent = 0)
107
+ output_target = print_to_stderr ? $stderr : $stdout
108
+
109
+ fetched_data = self.get_host_data(verbose, service, hostnames)
110
+ fetched_data.each do |hostname, host_data|
111
+ case service.type
112
+ when 'ABS'
113
+ # For ABS, 'hostname' variable is the jobID
114
+ #
115
+ # Create a vmpooler service to query each hostname there so as to get the metadata too
116
+
117
+ output_target.puts "- [JobID:#{host_data['request']['job']['id']}] <#{host_data['state']}>"
118
+ host_data['allocated_resources'].each do |allocated_resources, _i|
119
+ if allocated_resources['engine'] == "vmpooler"
120
+ vmpooler_service = service.clone
121
+ vmpooler_service.silent = true
122
+ vmpooler_service.maybe_use_vmpooler
123
+ self.pretty_print_hosts(verbose, vmpooler_service, allocated_resources['hostname'].split('.')[0], print_to_stderr, indent+2)
124
+ else
125
+ #TODO we could add more specific metadata for the other services, nspooler and aws
126
+ output_target.puts " - #{allocated_resources['hostname']} (#{allocated_resources['type']})"
127
+ end
128
+ end
129
+ when 'Pooler'
130
+ tag_pairs = []
131
+ tag_pairs = host_data['tags'].map { |key, value| "#{key}: #{value}" } unless host_data['tags'].nil?
132
+ duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
133
+ metadata = [host_data['template'], duration, *tag_pairs]
134
+ output_target.puts "- #{hostname}.#{host_data['domain']} (#{metadata.join(', ')})".gsub(/^/, ' ' * indent)
135
+ when 'NonstandardPooler'
136
+ line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
137
+ line += ", #{host_data['hours_left_on_reservation']}h remaining"
138
+ line += ", reason: #{host_data['reserved_for_reason']}" unless host_data['reserved_for_reason'].empty?
139
+ line += ')'
140
+ output_target.puts line
141
+ else
142
+ raise "Invalid service type #{service.type}"
143
+ end
144
+ end
145
+ end
146
+
147
+ def self.get_host_data(verbose, service, hostnames = [])
148
+ result = {}
81
149
  hostnames = [hostnames] unless hostnames.is_a? Array
82
150
  hostnames.each do |hostname|
83
151
  begin
84
152
  response = service.query(verbose, hostname)
85
153
  host_data = response[hostname]
86
-
87
- case service.type
88
- when 'ABS'
89
- # For ABS, 'hostname' variable is the jobID
90
- if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
91
- host_data['allocated_resources'].each do |vm_name, _i|
92
- puts "- [JobID:#{host_data['request']['job']['id']}] #{vm_name['hostname']} (#{vm_name['type']}) <#{host_data['state']}>"
154
+ if block_given?
155
+ yield host_data result
156
+ else
157
+ case service.type
158
+ when 'ABS'
159
+ # For ABS, 'hostname' variable is the jobID
160
+ if host_data['state'] == 'allocated' || host_data['state'] == 'filled'
161
+ result[hostname] = host_data
93
162
  end
163
+ when 'Pooler'
164
+ result[hostname] = host_data
165
+ when 'NonstandardPooler'
166
+ result[hostname] = host_data
167
+ else
168
+ raise "Invalid service type #{service.type}"
94
169
  end
95
- when 'Pooler'
96
- tag_pairs = []
97
- tag_pairs = host_data['tags'].map { |key, value| "#{key}: #{value}" } unless host_data['tags'].nil?
98
- duration = "#{host_data['running']}/#{host_data['lifetime']} hours"
99
- metadata = [host_data['template'], duration, *tag_pairs]
100
- puts "- #{hostname}.#{host_data['domain']} (#{metadata.join(', ')})"
101
- when 'NonstandardPooler'
102
- line = "- #{host_data['fqdn']} (#{host_data['os_triple']}"
103
- line += ", #{host_data['hours_left_on_reservation']}h remaining"
104
- line += ", reason: #{host_data['reserved_for_reason']}" unless host_data['reserved_for_reason'].empty?
105
- line += ')'
106
- puts line
107
- else
108
- raise "Invalid service type #{service.type}"
109
170
  end
110
171
  rescue StandardError => e
111
- STDERR.puts("Something went wrong while trying to gather information on #{hostname}:")
112
- STDERR.puts(e)
172
+ FloatyLogger.error("Something went wrong while trying to gather information on #{hostname}:")
173
+ FloatyLogger.error(e)
113
174
  end
114
175
  end
176
+ result
115
177
  end
116
178
 
117
179
  def self.pretty_print_status(verbose, service)
@@ -133,7 +195,7 @@ class Utils
133
195
  char = 'o'
134
196
  puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
135
197
  rescue StandardError => e
136
- puts "#{name.ljust(width)} #{e.red}"
198
+ FloatyLogger.error "#{name.ljust(width)} #{e.red}"
137
199
  end
138
200
  end
139
201
  puts message.colorize(status_response['status']['ok'] ? :default : :red)
@@ -152,11 +214,11 @@ class Utils
152
214
  char = 'o'
153
215
  puts "#{name.ljust(width)} #{(char * ready).green}#{(char * pending).yellow}#{(char * missing).red}"
154
216
  rescue StandardError => e
155
- puts "#{name.ljust(width)} #{e.red}"
217
+ FloatyLogger.error "#{name.ljust(width)} #{e.red}"
156
218
  end
157
219
  end
158
220
  when 'ABS'
159
- puts 'ABS Not OK'.red unless status_response
221
+ FloatyLogger.error 'ABS Not OK' unless status_response
160
222
  puts 'ABS is OK'.green if status_response
161
223
  else
162
224
  raise "Invalid service type #{service.type}"
@@ -172,12 +234,15 @@ class Utils
172
234
  end
173
235
 
174
236
  def self.get_service_object(type = '')
175
- nspooler_strings = %w[ns nspooler nonstandard nonstandard_pooler]
176
237
  abs_strings = %w[abs alwaysbescheduling always_be_scheduling]
177
- if nspooler_strings.include? type.downcase
178
- NonstandardPooler
179
- elsif abs_strings.include? type.downcase
238
+ nspooler_strings = %w[ns nspooler nonstandard nonstandard_pooler]
239
+ vmpooler_strings = %w[vmpooler]
240
+ if abs_strings.include? type.downcase
180
241
  ABS
242
+ elsif nspooler_strings.include? type.downcase
243
+ NonstandardPooler
244
+ elsif vmpooler_strings.include? type.downcase
245
+ Pooler
181
246
  else
182
247
  Pooler
183
248
  end
@@ -205,6 +270,9 @@ class Utils
205
270
  # If the service is configured but some values are missing, use the top-level defaults to fill them in
206
271
  service_config.merge! config['services'][options.service]
207
272
  end
273
+ # No config file but service is declared on command line
274
+ elsif !config['services'] && options.service
275
+ service_config['type'] = options.service
208
276
  end
209
277
 
210
278
  # Prioritize an explicitly specified url, user, or token if the user provided one
@@ -215,4 +283,30 @@ class Utils
215
283
 
216
284
  service_config
217
285
  end
286
+
287
+ # This method gets the vmpooler service configured in ~/.vmfloaty
288
+ def self.get_vmpooler_service_config(vmpooler_fallback)
289
+ config = Conf.read_config
290
+ # The top-level url, user, and token values in the config file are treated as defaults
291
+ service_config = {
292
+ 'url' => config['url'],
293
+ 'user' => config['user'],
294
+ 'token' => config['token'],
295
+ 'type' => 'vmpooler',
296
+ }
297
+
298
+ # at a minimum, the url needs to be configured
299
+ if config['services'] && config['services'][vmpooler_fallback] && config['services'][vmpooler_fallback]['url']
300
+ # If the service is configured but some values are missing, use the top-level defaults to fill them in
301
+ service_config.merge! config['services'][vmpooler_fallback]
302
+ else
303
+ if vmpooler_fallback.nil?
304
+ raise ArgumentError, "The abs service should have a key named 'vmpooler_fallback' in ~/.vmfloaty.yml with a value that points to a vmpooler service name use this format:\nservices:\n myabs:\n url: 'http://abs.com'\n user: 'superman'\n token: 'kryptonite'\n vmpooler_fallback: 'myvmpooler'\n myvmpooler:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
305
+ else
306
+ raise ArgumentError, "Could not find a configured service named '#{vmpooler_fallback}' in ~/.vmfloaty.yml use this format:\nservices:\n #{vmpooler_fallback}:\n url: 'http://vmpooler.com'\n user: 'superman'\n token: 'kryptonite'"
307
+ end
308
+ end
309
+
310
+ service_config
311
+ end
218
312
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Vmfloaty
4
- VERSION = '0.9.1'
4
+ VERSION = '1.0.0'
5
5
  end
6
+
@@ -1,5 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'simplecov'
4
+ require 'coveralls'
5
+
6
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new([
7
+ SimpleCov::Formatter::HTMLFormatter,
8
+ Coveralls::SimpleCov::Formatter
9
+ ])
10
+ SimpleCov.start do
11
+ add_filter %r{^/spec/}
12
+ end
13
+
3
14
  require 'vmfloaty'
4
15
  require 'webmock/rspec'
5
16
 
@@ -9,15 +9,60 @@ describe ABS do
9
9
  before :each do
10
10
  end
11
11
 
12
+ describe '#list' do
13
+ it 'skips empty platforms and lists aws' do
14
+ stub_request(:get, "http://foo/status/platforms/vmpooler").
15
+ to_return(:status => 200, :body => "", :headers => {})
16
+ stub_request(:get, "http://foo/status/platforms/ondemand_vmpooler").
17
+ to_return(:status => 200, :body => "", :headers => {})
18
+ stub_request(:get, "http://foo/status/platforms/nspooler").
19
+ to_return(:status => 200, :body => "", :headers => {})
20
+ body = '{
21
+ "aws_platforms": [
22
+ "amazon-6-x86_64",
23
+ "amazon-7-x86_64",
24
+ "amazon-7-arm64",
25
+ "centos-7-x86-64-west",
26
+ "redhat-8-arm64"
27
+ ]
28
+ }'
29
+ stub_request(:get, "http://foo/status/platforms/aws").
30
+ to_return(:status => 200, :body => body, :headers => {})
31
+
32
+
33
+ results = ABS.list(false, "http://foo")
34
+
35
+ expect(results).to include("amazon-6-x86_64", "amazon-7-x86_64", "amazon-7-arm64", "centos-7-x86-64-west", "redhat-8-arm64")
36
+ end
37
+ it 'legacy JSON string, prior to PR 306' do
38
+ stub_request(:get, "http://foo/status/platforms/vmpooler").
39
+ to_return(:status => 200, :body => "", :headers => {})
40
+ stub_request(:get, "http://foo/status/platforms/ondemand_vmpooler").
41
+ to_return(:status => 200, :body => "", :headers => {})
42
+ stub_request(:get, "http://foo/status/platforms/nspooler").
43
+ to_return(:status => 200, :body => "", :headers => {})
44
+ body = '{
45
+ "aws_platforms": "[\"amazon-6-x86_64\",\"amazon-7-x86_64\",\"amazon-7-arm64\",\"centos-7-x86-64-west\",\"redhat-8-arm64\"]"
46
+ }'
47
+ stub_request(:get, "http://foo/status/platforms/aws").
48
+ to_return(:status => 200, :body => body, :headers => {})
49
+
50
+ results = ABS.list(false, "http://foo")
51
+
52
+ expect(results).to include("amazon-6-x86_64", "amazon-7-x86_64", "amazon-7-arm64", "centos-7-x86-64-west", "redhat-8-arm64")
53
+ end
54
+ end
55
+
12
56
  describe '#format' do
13
- it 'returns an hash formatted like a vmpooler return' do
57
+ it 'returns an hash formatted like a vmpooler return, plus the job_id' do
58
+ job_id = "generated_by_floaty_12345"
14
59
  abs_formatted_response = [
15
60
  { 'hostname' => 'aaaaaaaaaaaaaaa.delivery.puppetlabs.net', 'type' => 'centos-7.2-x86_64', 'engine' => 'vmpooler' },
16
61
  { 'hostname' => 'aaaaaaaaaaaaaab.delivery.puppetlabs.net', 'type' => 'centos-7.2-x86_64', 'engine' => 'vmpooler' },
17
62
  { 'hostname' => 'aaaaaaaaaaaaaac.delivery.puppetlabs.net', 'type' => 'ubuntu-7.2-x86_64', 'engine' => 'vmpooler' },
18
63
  ]
19
64
 
20
- vmpooler_formatted_response = ABS.translated(abs_formatted_response)
65
+ vmpooler_formatted_response = ABS.translated(abs_formatted_response, job_id)
21
66
 
22
67
  vmpooler_formatted_compare = {
23
68
  'centos-7.2-x86_64' => {},
@@ -29,6 +74,8 @@ describe ABS do
29
74
 
30
75
  vmpooler_formatted_compare['ok'] = true
31
76
 
77
+ vmpooler_formatted_compare['job_id'] = job_id
78
+
32
79
  expect(vmpooler_formatted_response).to eq(vmpooler_formatted_compare)
33
80
  vmpooler_formatted_response.delete('ok')
34
81
  vmpooler_formatted_compare.delete('ok')
@@ -65,14 +112,14 @@ describe ABS do
65
112
 
66
113
  describe '#test_abs_status_queue_endpoint' do
67
114
  before :each do
68
- # rubocop:disable Metrics/LineLength
115
+ # rubocop:disable Layout/LineLength
69
116
  @active_requests_response = '
70
117
  [
71
- "{ \"state\":\"allocated\",\"last_processed\":\"2019-12-16 23:00:34 +0000\",\"allocated_resources\":[{\"hostname\":\"take-this.delivery.puppetlabs.net\",\"type\":\"win-2012r2-x86_64\",\"engine\":\"vmpooler\"}],\"audit_log\":{\"2019-12-13 16:45:29 +0000\":\"Allocated take-this.delivery.puppetlabs.net for job 1576255517241\"},\"request\":{\"resources\":{\"win-2012r2-x86_64\":1},\"job\":{\"id\":\"1576255517241\",\"tags\":{\"user\":\"test-user\"},\"user\":\"test-user\",\"time-received\":1576255519},\"priority\":1}}",
118
+ { "state":"allocated","last_processed":"2019-12-16 23:00:34 +0000","allocated_resources":[{"hostname":"take-this.delivery.puppetlabs.net","type":"win-2012r2-x86_64","engine":"vmpooler"}],"audit_log":{"2019-12-13 16:45:29 +0000":"Allocated take-this.delivery.puppetlabs.net for job 1576255517241"},"request":{"resources":{"win-2012r2-x86_64":1},"job":{"id":"1576255517241","tags":{"user":"test-user"},"user":"test-user","time-received":1576255519},"priority":1}},
72
119
  "null",
73
- "{\"state\":\"allocated\",\"last_processed\":\"2019-12-16 23:00:34 +0000\",\"allocated_resources\":[{\"hostname\":\"not-this.delivery.puppetlabs.net\",\"type\":\"win-2012r2-x86_64\",\"engine\":\"vmpooler\"}],\"audit_log\":{\"2019-12-13 16:46:14 +0000\":\"Allocated not-this.delivery.puppetlabs.net for job 1576255565159\"},\"request\":{\"resources\":{\"win-2012r2-x86_64\":1},\"job\":{\"id\":\"1576255565159\",\"tags\":{\"user\":\"not-test-user\"},\"user\":\"not-test-user\",\"time-received\":1576255566},\"priority\":1}}"
120
+ {"state":"allocated","last_processed":"2019-12-16 23:00:34 +0000","allocated_resources":[{"hostname":"not-this.delivery.puppetlabs.net","type":"win-2012r2-x86_64","engine":"vmpooler"}],"audit_log":{"2019-12-13 16:46:14 +0000":"Allocated not-this.delivery.puppetlabs.net for job 1576255565159"},"request":{"resources":{"win-2012r2-x86_64":1},"job":{"id":"1576255565159","tags":{"user":"not-test-user"},"user":"not-test-user","time-received":1576255566},"priority":1}}
74
121
  ]'
75
- # rubocop:enable Metrics/LineLength
122
+ # rubocop:enable Layout/LineLength
76
123
  @token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
77
124
  @test_user = 'test-user'
78
125
  end
@@ -92,5 +139,35 @@ describe ABS do
92
139
  )
93
140
  end
94
141
  end
142
+
143
+ describe '#test_abs_delete_jobid' do
144
+ before :each do
145
+ # rubocop:disable Layout/LineLength
146
+ @active_requests_response = '
147
+ [
148
+ { "state":"allocated", "last_processed":"2020-01-17 22:29:13 +0000", "allocated_resources":[{"hostname":"craggy-chord.delivery.puppetlabs.net", "type":"centos-7-x86_64", "engine":"vmpooler"}, {"hostname":"visible-revival.delivery.puppetlabs.net", "type":"centos-7-x86_64", "engine":"vmpooler"}], "audit_log":{"2020-01-17 22:28:45 +0000":"Allocated craggy-chord.delivery.puppetlabs.net, visible-revival.delivery.puppetlabs.net for job 1579300120799"}, "request":{"resources":{"centos-7-x86_64":2}, "job":{"id":"1579300120799", "tags":{"user":"test-user"}, "user":"test-user", "time-received":1579300120}, "priority":3}}
149
+ ]'
150
+ @return_request = { '{"job_id":"1579300120799","hosts":{"hostname":"craggy-chord.delivery.puppetlabs.net","type":"centos-7-x86_64","engine":"vmpooler"},{"hostname":"visible-revival.delivery.puppetlabs.net","type":"centos-7-x86_64","engine":"vmpooler"}}'=>true }
151
+ # rubocop:enable Layout/LineLength
152
+ @token = 'utpg2i2xswor6h8ttjhu3d47z53yy47y'
153
+ @test_user = 'test-user'
154
+ # Job ID
155
+ @hosts = ['1579300120799']
156
+ end
157
+
158
+ it 'will delete the whole job' do
159
+ stub_request(:get, 'https://abs.example.com/status/queue')
160
+ .to_return(:status => 200, :body => @active_requests_response, :headers => {})
161
+ stub_request(:post, 'https://abs.example.com/return')
162
+ .with(:body => @return_request)
163
+ .to_return(:status => 200, :body => 'OK', :headers => {})
164
+
165
+ ret = ABS.delete(false, @abs_url, @hosts, @token, @test_user)
166
+
167
+ expect(ret).to include(
168
+ 'craggy-chord.delivery.puppetlabs.net' => { 'ok'=>true }, 'visible-revival.delivery.puppetlabs.net' => { 'ok'=>true },
169
+ )
170
+ end
171
+ end
95
172
  end
96
173
  end