xolo-admin 1.0.1 → 2.0.3

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.
@@ -26,7 +26,7 @@ module Xolo
26
26
  # Routes for server admins
27
27
 
28
28
  SERVER_STATUS_ROUTE = '/maint/state'
29
- SERVER_CLENUP_ROUTE = '/maint/cleanup'
29
+ SERVER_CLEANUP_ROUTE = '/maint/cleanup'
30
30
  SERVER_ROTATE_LOGS_ROUTE = '/maint/rotate-logs'
31
31
  SERVER_UPDATE_CLIENT_DATA_ROUTE = '/maint/update-client-data'
32
32
  SERVER_LOG_LEVEL_ROUTE = '/maint/set-log-level'
@@ -72,6 +72,14 @@ module Xolo
72
72
  puts msg unless quiet?
73
73
  end
74
74
 
75
+ # TODO: Use this everywhere in admin
76
+ ##########################
77
+ def show_debug(msg)
78
+ return unless debug?
79
+
80
+ puts "DEBUG: #{msg}"
81
+ end
82
+
75
83
  # Search for a title in Xolo
76
84
  # Looks for the search string (case insensitive) in these attributes:
77
85
  # - title
@@ -234,19 +242,30 @@ module Xolo
234
242
 
235
243
  new_title = Xolo::Admin::Title.new opts_to_process
236
244
 
245
+ # TMP TESTING
246
+ # puts 'DEBUG: opts_to_process:'
247
+ # opts_to_process.to_h.each do |k, v|
248
+ # puts " #{k}: #{v}"
249
+ # end
250
+ # return
251
+
237
252
  response_data = new_title.add(server_cnx)
238
253
 
239
254
  if debug?
240
255
  puts "DEBUG: response_data: #{response_data}"
241
256
  puts
242
257
  end
243
-
244
- display_progress response_data[:progress_stream_url_path]
258
+ if response_data[:progress_stream_url_path]
259
+ display_progress response_data[:progress_stream_url_path]
260
+ else
261
+ puts '# No progress stream URL returned'
262
+ return
263
+ end
245
264
 
246
265
  # Upload the ssvc icon, if any?
247
266
  upload_ssvc_icon new_title
248
267
 
249
- speak "Title '#{cli_cmd.title}' has been added to Xolo.\nAdd at least one version to enable piloting and deployment"
268
+ speak "Title '#{cli_cmd.title}' has been added to Xolo.\nEnsure there's at least one version to enable piloting and deployment"
250
269
  rescue StandardError => e
251
270
  handle_processing_error e
252
271
  end
@@ -307,9 +326,26 @@ module Xolo
307
326
  def upload_ssvc_icon(title)
308
327
  return unless title.self_service_icon.is_a? Pathname
309
328
 
329
+ speak 'Pausing to let jamf server catch up before uploading self-service icon...'
330
+ sleep 30
310
331
  speak "Uploading self-service icon #{title.self_service_icon.basename}, #{title.self_service_icon.pix_humanize_size} to Xolo."
311
332
 
312
- title.upload_self_service_icon(upload_cnx)
333
+ wait_for_title_timer = 0
334
+ begin
335
+ title.upload_self_service_icon(upload_cnx)
336
+ rescue Xolo::ConnectionError => e
337
+ speak "Error uploading self-service icon: #{e.message}, trying again in 5 secs"
338
+ # If we get a 404, it might be because the title isn't fully created yet.
339
+ # Wait a bit and try again, up to a certain number of seconds.
340
+ if wait_for_title_timer < 90
341
+ sleep 5
342
+ wait_for_title_timer += 5
343
+ retry
344
+ else
345
+ msg = "Failed to upload icon for Title '#{title}' after retrying for #{wait_for_title_timer} seconds."
346
+ raise Xolo::NoSuchItemError, msg
347
+ end # if
348
+ end # begin
313
349
 
314
350
  speak 'Self-service icon uploaded. Will be added to Self Service policies as needed'
315
351
  rescue StandardError => e
@@ -478,6 +514,7 @@ module Xolo
478
514
  opts_to_process.title = cli_cmd.title
479
515
  opts_to_process.version = cli_cmd.version
480
516
 
517
+ title_obj = Xolo::Admin::Title.fetch cli_cmd.title, server_cnx
481
518
  new_vers = Xolo::Admin::Version.new opts_to_process
482
519
  response_data = new_vers.add(server_cnx)
483
520
 
@@ -489,7 +526,8 @@ module Xolo
489
526
  display_progress response_data[:progress_stream_url_path]
490
527
 
491
528
  # Upload the pkg, if any?
492
- upload_pkg(new_vers)
529
+ upload_pkg(new_vers) unless title_obj.autopkg_enabled?
530
+
493
531
  speak 'It can take up to 15 minutes for the version to be available via Jamf and Self Service.'
494
532
  rescue StandardError => e
495
533
  handle_processing_error e
@@ -510,6 +548,8 @@ module Xolo
510
548
  def upload_pkg(version)
511
549
  return unless version.pkg_to_upload.is_a? Pathname
512
550
 
551
+ # TODO: deal with autopkg pkgs, should we be able to do this?
552
+
513
553
  speak "Uploading #{version.pkg_to_upload.basename}, #{version.pkg_to_upload.pix_humanize_size} to Xolo"
514
554
  # start the upload in a thread
515
555
  upload_thr = Thread.new { version.upload_pkg(upload_cnx) }
@@ -710,6 +750,9 @@ module Xolo
710
750
  # description is handled specially below
711
751
  next if attr == :description
712
752
 
753
+ # only show patch_source and title_id if subscribed
754
+ next if %i[patch_source title_id].include?(attr) && !title.subscribed?
755
+
713
756
  value = title.send attr
714
757
  value = value.join(Xolo::COMMA_JOIN) if value.is_a? Array
715
758
  puts "- #{deets[:label]}: #{value}".pix_word_wrap
@@ -941,7 +984,7 @@ module Xolo
941
984
  def server_cleanup
942
985
  return unless confirmed? 'Run the Xolo Server cleanup process'
943
986
 
944
- result = server_cnx.post(SERVER_CLENUP_ROUTE).body
987
+ result = server_cnx.post(SERVER_CLEANUP_ROUTE).body
945
988
  puts result[:result]
946
989
  rescue StandardError => e
947
990
  handle_processing_error e
@@ -1036,6 +1079,35 @@ module Xolo
1036
1079
  list_in_cols header, jamf_computer_group_names.sort_by(&:downcase)
1037
1080
  end
1038
1081
 
1082
+ # List Available titles for subscription
1083
+ #
1084
+ # @return [void]
1085
+ #############################
1086
+ def list_available_titles
1087
+ data = jamf_available_titles.sort_by { |t| t[:app_name].downcase }
1088
+ data.map! do |t|
1089
+ {
1090
+ display_name: t[:app_name],
1091
+ publisher: t[:publisher],
1092
+ patch_source: t[:source_name],
1093
+ title_id: t[:name_id],
1094
+ current_version: t[:current_version],
1095
+ last_modified: t[:last_modified]
1096
+ }
1097
+ end
1098
+
1099
+ if json?
1100
+ puts JSON.pretty_generate(data)
1101
+ return
1102
+ end
1103
+
1104
+ title = 'Titles Available for Subscription'
1105
+ header = %w[DisplayName Publisher PatchSource TitleID]
1106
+ lines = data.map { |t| [t[:display_name], t[:publisher], t[:patch_source], t[:title_id]] }
1107
+
1108
+ show_text generate_report(lines, header_row: header, title: title)
1109
+ end
1110
+
1039
1111
  # List all the SSVC categories in jamf pro
1040
1112
  #
1041
1113
  # @return [void]
@@ -85,7 +85,7 @@ module Xolo
85
85
  ###################
86
86
  def progress_history
87
87
  progress_history_file.pix_touch
88
- history = YAML.load progress_history_file.read
88
+ history = YAML.safe_load progress_history_file.read, permitted_classes: [Symbol, Time]
89
89
  history ||= {}
90
90
 
91
91
  now = Time.now
@@ -43,10 +43,17 @@ module Xolo
43
43
  #############################
44
44
 
45
45
  # @return [Hash{Symbol: Hash}] The ATTRIBUTES that are available as CLI & walkthru options
46
+ ##############################
46
47
  def self.cli_opts
47
48
  @cli_opts ||= ATTRIBUTES.select { |_k, v| v[:cli] }
48
49
  end
49
50
 
51
+ # @return [Hash{Symbol: Hash}] The ATTRIBUTES that are available as opts for the subscribe command
52
+ ################################
53
+ # def self.subscribe_cli_opts
54
+ # @subscribe_cli_opts ||= ATTRIBUTES.select { |_k, v| v[:cli_subscribe] }
55
+ # end
56
+
50
57
  # @return [Array<String>] The currently known titles names on the server
51
58
  #############################
52
59
  def self.all_titles(cnx)
@@ -77,8 +84,9 @@ module Xolo
77
84
  ####################
78
85
  def self.fetch(title, cnx)
79
86
  resp = cnx.get "#{SERVER_ROUTE}/#{title}"
80
-
81
- new resp.body
87
+ title_obj = new resp.body
88
+ title_obj.cnx = cnx
89
+ title_obj
82
90
  rescue Faraday::ResourceNotFound
83
91
  raise Xolo::NoSuchItemError, "No such title '#{title}'"
84
92
  end
@@ -115,6 +123,8 @@ module Xolo
115
123
  # Attributes
116
124
  ######################
117
125
  ######################
126
+ # the server connection used to fetch this version
127
+ attr_accessor :cnx
118
128
 
119
129
  # Constructor
120
130
  ######################
@@ -139,7 +149,7 @@ module Xolo
139
149
  # @param cnx [Faraday::Connection] The connection to use, must be logged in already
140
150
  # @return [Hash] the response body from the server
141
151
  ####################
142
- def add(cnx)
152
+ def add(cnx = self.cnx)
143
153
  resp = cnx.post SERVER_ROUTE, to_h
144
154
  resp.body
145
155
  end
@@ -148,7 +158,7 @@ module Xolo
148
158
  # @param cnx [Faraday::Connection] The connection to use, must be logged in already
149
159
  # @return [Hash] the response body from the server
150
160
  ####################
151
- def update(cnx)
161
+ def update(cnx = self.cnx)
152
162
  resp = cnx.put "#{SERVER_ROUTE}/#{title}", to_h
153
163
  resp.body
154
164
  end
@@ -158,8 +168,11 @@ module Xolo
158
168
  # @param version [String] the version to release
159
169
  # @return [Hash] the response body from the server
160
170
  ####################
161
- def release(cnx, version:)
162
- resp = cnx.patch "#{SERVER_ROUTE}/#{title}/release/#{version}", {}
171
+ def release(cnx = self.cnx, version:)
172
+ resp = cnx.patch "#{SERVER_ROUTE}/#{title}/release/#{URI::Parser.new.escape version}", {}
173
+ ### the CGI escape one doesn't seem to work with, e.g. "7.1.5 (84650)"
174
+ # resp = cnx.patch "#{SERVER_ROUTE}/#{title}/release/#{CGI.escape version}", {}
175
+
163
176
  resp.body
164
177
  end
165
178
 
@@ -168,7 +181,7 @@ module Xolo
168
181
  # @param versions [Boolean] if true, repair all versions of this title
169
182
  # @return [Hash] the response body from the server
170
183
  ####################
171
- def repair(cnx, versions: false)
184
+ def repair(cnx = self.cnx, versions: false)
172
185
  resp = cnx.post "#{SERVER_ROUTE}/#{title}/repair", { repair_versions: versions }
173
186
  resp.body
174
187
  end
@@ -177,7 +190,7 @@ module Xolo
177
190
  # @param cnx [Faraday::Connection] The connection to use, must be logged in already
178
191
  # @return [Hash] the response data
179
192
  ####################
180
- def delete(cnx)
193
+ def delete(cnx = self.cnx)
181
194
  self.class.delete title, cnx
182
195
  end
183
196
 
@@ -186,7 +199,7 @@ module Xolo
186
199
  # @param cnx [Faraday::Connection] The connection to use, must be logged in already
187
200
  # @return [Hash] the response data
188
201
  ####################
189
- def freeze(targets, users = false, cnx)
202
+ def freeze(targets, users = false, cnx = self.cnx)
190
203
  data = { targets: targets, users: users }
191
204
  resp = cnx.put "#{SERVER_ROUTE}/#{title}/freeze", data
192
205
  resp.body
@@ -197,7 +210,7 @@ module Xolo
197
210
  # @param cnx [Faraday::Connection] The connection to use, must be logged in already
198
211
  # @return [Hash] the response data
199
212
  ####################
200
- def thaw(targets, users = false, cnx)
213
+ def thaw(targets, users = false, cnx = self.cnx)
201
214
  data = { targets: targets, users: users }
202
215
  resp = cnx.put "#{SERVER_ROUTE}/#{title}/thaw", data
203
216
  resp.body
@@ -207,7 +220,7 @@ module Xolo
207
220
  # @param cnx [Faraday::Connection] The connection to use, must be logged in already
208
221
  # @return [Hash{String => String}] computer name => user name
209
222
  ####################
210
- def frozen(cnx)
223
+ def frozen(cnx = self.cnx)
211
224
  resp = cnx.get "#{SERVER_ROUTE}/#{title}/frozen"
212
225
  resp.body
213
226
  end
@@ -216,7 +229,7 @@ module Xolo
216
229
  # @param cnx [Faraday::Connection] The connection to use, must be logged in already
217
230
  # @return [Hash{String => String}] page_name => url
218
231
  ####################
219
- def gui_urls(cnx)
232
+ def gui_urls(cnx = self.cnx)
220
233
  resp = cnx.get "#{SERVER_ROUTE}/#{title}/urls"
221
234
  resp.body
222
235
  end
@@ -227,7 +240,7 @@ module Xolo
227
240
  # @param cnx [Faraday::Connection] The connection to use, must be logged in already
228
241
  # @return [Array<Hash>] The change log for this title
229
242
  ####################
230
- def changelog(cnx)
243
+ def changelog(cnx = self.cnx)
231
244
  resp = cnx.get "#{SERVER_ROUTE}/#{title}/changelog"
232
245
  resp.body
233
246
  end
@@ -248,19 +261,16 @@ module Xolo
248
261
  "Can't upload self service icon '#{self_service_icon}': file doesn't exist or is not readable"
249
262
  end
250
263
 
251
- # upfile = Faraday::UploadIO.new(
252
- # self_service_icon.to_s,
253
- # 'application/octet-stream',
254
- # self_service_icon.basename.to_s
255
- # )
256
-
257
264
  mimetype = `/usr/bin/file --brief --mime-type #{Shellwords.escape self_service_icon.expand_path.to_s}`.chomp
258
265
  upfile = Faraday::Multipart::FilePart.new(self_service_icon.expand_path.to_s, mimetype)
259
266
  content = { file: upfile }
260
- # route = "#{UPLOAD_ICON_ROUTE}/#{title}"
261
- route = "#{SERVER_ROUTE}/#{title}/#{UPLOAD_ICON_ROUTE}"
262
267
 
268
+ route = "#{SERVER_ROUTE}/#{title}/#{UPLOAD_ICON_ROUTE}"
263
269
  upload_cnx.post(route) { |req| req.body = content }
270
+ rescue Xolo::NoSuchItemError
271
+ raise
272
+ rescue StandardError => e
273
+ raise Xolo::ConnectionError, "#{e.class}: #{e.message}"
264
274
  end
265
275
 
266
276
  # Get the patch report data for this title
@@ -269,16 +279,16 @@ module Xolo
269
279
  # @param cnx [Faraday::Connection] The connection to use, must be logged in already
270
280
  # @return [Array<Hash>] Data for each computer with any version of this title installed
271
281
  ##################################
272
- def patch_report_data(cnx)
282
+ def patch_report_data(cnx = self.cnx)
273
283
  resp = cnx.get "#{SERVER_ROUTE}/#{title}/patch_report"
274
284
  resp.body
275
285
  end
276
286
 
277
287
  # Add more data to our hash
278
288
  ###########################
279
- def to_h
280
- super
281
- end
289
+ # def to_h
290
+ # super
291
+ # end
282
292
 
283
293
  end # class Title
284
294