depot3 3.0.15 → 3.0.20

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.
@@ -88,7 +88,10 @@ module D3
88
88
  # return the desired password, so remove the pipe,
89
89
  # execute it, and return stdout from it.
90
90
  if path.end_with? "|"
91
- return `#{path.chomp "|"}`.chomp
91
+ cmd = path.chomp '|'
92
+ output = `#{cmd} 2>&1`.chomp
93
+ return output if $CHILD_STATUS.exitstatus.zero?
94
+ raise D3::PermissionError, "can't get client password for #{pw}: #{output}"
92
95
  end
93
96
 
94
97
  file = Pathname.new path
@@ -106,4 +109,3 @@ module D3
106
109
 
107
110
  end # class Client
108
111
  end # module D3
109
-
@@ -22,9 +22,10 @@
22
22
  ###
23
23
  ###
24
24
 
25
-
26
25
  ###
27
26
  module D3
27
+
28
+ # Client
28
29
  class Client < JSS::Client
29
30
 
30
31
  ################# Class Methods #################
@@ -45,8 +46,7 @@ module D3
45
46
  ###
46
47
  ### @return [void]
47
48
  ###
48
- def self.install(pkgs, options )
49
-
49
+ def self.install(pkgs, options)
50
50
  pkgs = [pkgs] if pkgs.is_a? String
51
51
 
52
52
  pkgs.each do |pkg_to_search|
@@ -58,11 +58,26 @@ module D3
58
58
  raise D3::InstallError, "The package for #{desired_pkg.edition} is missing from the JSS" if desired_pkg.missing?
59
59
 
60
60
  if options.custom_expiration
61
- raise "Sorry #{desired_pkg.edition} is not expirable. A d3 admin needs to add an expiration path." if desired_pkg.expiration_paths.empty?
61
+ D3.log "Sorry #{desired_pkg.edition} is not expirable. A d3 admin needs to add an expiration path.", :warn if desired_pkg.expiration_paths.empty?
62
+ break
62
63
  end
63
64
 
64
65
  curr_rcpt = D3::Client::Receipt.all[desired_pkg.basename]
65
66
 
67
+ # If we were asked to freeze_on_install and the currently installed is
68
+ # same edition, freeze it anyway.
69
+ # Use force to freeze if the currently installed is newer.
70
+ #
71
+ if curr_rcpt && curr_rcpt.id >= desired_pkg.id && options.freeze_on_install
72
+ if options.force || curr_rcpt.id == desired_pkg.id
73
+ freeze_receipts([curr_rcpt.basename]) unless curr_rcpt.frozen?
74
+ D3.log "Freezing previously installed #{curr_rcpt.edition}", :warn
75
+ break
76
+ end # if options.force elsif curr_rcpt.id == desired_pkg.id
77
+ D3.log "Cannot freeze previously installed #{curr_rcpt.edition} (#{curr_rcpt.status}) It is newer than #{desired_pkg.edition}. Use --force if needed.", :warn
78
+ break
79
+ end
80
+
66
81
  # many things can be forced
67
82
  # things that are defined in the pkg itself
68
83
  # (exclusions, prohibiting procs, oses, cpus)
@@ -76,42 +91,42 @@ module D3
76
91
  desired_pkg.check_for_skipped
77
92
  # same or newer?
78
93
  desired_pkg.check_for_newer_version
79
-
80
94
  end # unless options.force
81
95
 
82
96
  if curr_rcpt
83
97
  D3.log("Un-freezing #{curr_rcpt.edition} by installing #{desired_pkg.edition}", :warn) if curr_rcpt.frozen?
84
-
85
- if desired_pkg.id == curr_rcpt.id
98
+ if desired_pkg.id == curr_rcpt.id
86
99
  D3.log("Re-installing #{desired_pkg.edition}(#{desired_pkg.status})", :warn)
87
- elsif desired_pkg.id < curr_rcpt.id
100
+ elsif desired_pkg.id < curr_rcpt.id
88
101
  D3.log("Rolling back #{curr_rcpt.edition}(#{curr_rcpt.status}) to #{desired_pkg.edition}(#{desired_pkg.status})", :warn)
89
102
  else
90
103
  D3.log("Updating #{curr_rcpt.edition}(#{curr_rcpt.status}) to #{desired_pkg.edition}(#{desired_pkg.status})", :warn)
91
104
  end
92
105
  end # if curr rcpt
93
106
 
107
+ cloud = cloud_dist_point_to_use(pkg: desired_pkg)
108
+
94
109
  desired_pkg.install(
95
- :force => options.force,
96
- :admin => self.get_admin(desired_pkg, options),
97
- :puppywalk => options.puppies,
98
- :expiration => options.custom_expiration,
99
- :verbose => options.verbose,
100
- :alt_download_url => self.cloud_dist_point_to_use
110
+ force: options.force,
111
+ admin: get_admin(desired_pkg, options),
112
+ puppywalk: options.puppies,
113
+ expiration: options.custom_expiration,
114
+ verbose: options.verbose,
115
+ alt_download_url: cloud
101
116
  )
102
117
 
103
- self.freeze_receipts([desired_pkg.basename]) if options.freeze_on_install
118
+ freeze_receipts([desired_pkg.basename]) if options.freeze_on_install
104
119
 
105
120
  D3.log "Finished installing #{desired_pkg.edition}(#{desired_pkg.status})", :info
106
121
 
107
122
  rescue JSS::MissingDataError, JSS::NoSuchItemError, JSS::InvalidDataError, D3::InstallError
108
- D3.log "Skipping installation of #{pkg_to_search}: #{$!}", :error
123
+ D3.log "Skipping installation of #{pkg_to_search}: #{$ERROR_INFO}", :error
109
124
  D3.log_backtrace
110
125
  rescue D3::PreInstallError
111
- D3.log "There was an error with the pre-install script for #{desired_pkg.edition}: #{$!}", :error
126
+ D3.log "There was an error with the pre-install script for #{desired_pkg.edition}: #{$ERROR_INFO}", :error
112
127
  D3.log_backtrace
113
128
  rescue D3::PostInstallError
114
- D3.log "There was an error with the post-install script for #{desired_pkg.edition}: #{$!} NOTE: it was installed, but may have problems.", :error
129
+ D3.log "There was an error with the post-install script for #{desired_pkg.edition}: #{$ERROR_INFO} NOTE: it was installed, but may have problems.", :error
115
130
  D3.log_backtrace
116
131
  end # begin
117
132
  end # args.each
@@ -136,13 +151,12 @@ module D3
136
151
  D3.log "Finished uninstalling #{rcpt.edition}.", :info
137
152
 
138
153
  rescue JSS::MissingDataError, D3::UninstallError, JSS::InvalidDataError
139
- D3.log "Skipping uninstall of #{rcpt_to_remove}: #{$!}", :error
154
+ D3.log "Skipping uninstall of #{rcpt_to_remove}: #{$ERROR_INFO}", :error
140
155
  D3.log_backtrace
141
156
  next
142
157
  end # begin
143
158
  end # rcpts.each
144
-
145
- end #uninstall_manual
159
+ end # uninstall_manual
146
160
 
147
161
  ### Return a valid, possibly-default, admin name for
148
162
  ### installing a package. Since the admin name is stored in
@@ -154,10 +168,9 @@ module D3
154
168
  ### @return [String] a valid admin name to use for the install
155
169
  ###
156
170
  def self.get_admin(pkg_to_install, options)
157
-
158
171
  # is this puppy already in the queue? If so,
159
172
  # the queue has our admin_name
160
- if options.puppies and D3::PUPPY_Q.q[pkg_to_install.basename] then
173
+ if options.puppies && D3::PUPPY_Q.q[pkg_to_install.basename]
161
174
 
162
175
  admin = D3::PUPPY_Q.q[pkg_to_install.basename].admin
163
176
  admin ||= D3::DFT_PUPPY_ADMIN
@@ -171,7 +184,7 @@ module D3
171
184
 
172
185
  end # if @options.puppies
173
186
 
174
- return admin
187
+ admin
175
188
  end # get admin name
176
189
 
177
190
  ### Remove one or more puppies from the puppy queue
@@ -180,9 +193,9 @@ module D3
180
193
  ###
181
194
  ### @return [void]
182
195
  ###
183
- def self.dequeue_puppies (puppies)
196
+ def self.dequeue_puppies(puppies)
184
197
  puppies = [puppies] if puppies.is_a? String
185
- puppies = D3::PUPPY_Q.pups if puppies.include? "all"
198
+ puppies = D3::PUPPY_Q.pups if puppies.include? 'all'
186
199
  puppies.each do |pup|
187
200
  unless the_puppy = D3::PUPPY_Q.q[pup]
188
201
  D3.log "No pkg for basename '#{pup}' in the puppy queue.", :warn
@@ -192,7 +205,7 @@ module D3
192
205
  D3.log "Removing '#{the_puppy.edition}' from the puppy queue.", :warn
193
206
  D3::PUPPY_Q - the_puppy
194
207
  rescue
195
- D3.log "Couldn't remove #{the_puppy.edition} from the puppy queue: #{$!}", :error
208
+ D3.log "Couldn't remove #{the_puppy.edition} from the puppy queue: #{$ERROR_INFO}", :error
196
209
  end # begin
197
210
  end
198
211
  end
@@ -203,9 +216,9 @@ module D3
203
216
  ###
204
217
  ### @return [void]
205
218
  ###
206
- def self.sync (options = OpenStruct.new)
219
+ def self.sync(options = OpenStruct.new)
207
220
  D3::Client.set_env :sync
208
- D3.log "Starting sync", :warn
221
+ D3.log 'Starting sync', :warn
209
222
 
210
223
  begin
211
224
  # update rcpts
@@ -233,7 +246,7 @@ module D3
233
246
  # updates)
234
247
  clean_missing_receipts
235
248
 
236
- D3.log "Finished sync", :warn
249
+ D3.log 'Finished sync', :warn
237
250
  ensure
238
251
  D3::Client.unset_env :sync
239
252
  end
@@ -253,8 +266,7 @@ module D3
253
266
  ### @return [void]
254
267
  ###
255
268
  def self.update_rcpts
256
- D3.log "Updating receipts", :warn
257
- need_saving = false
269
+ D3.log 'Updating receipts', :warn
258
270
 
259
271
  D3::Client::Receipt.all.each do |basename, rcpt|
260
272
  pkgdata = D3::Package.find_package rcpt.edition, :hash
@@ -317,7 +329,7 @@ module D3
317
329
  need_update = true
318
330
  end # if
319
331
 
320
- if (rcpt.expiration != pkgdata[:expiration].to_i) and (not rcpt.custom_expiration)
332
+ if (rcpt.expiration != pkgdata[:expiration].to_i) && !rcpt.custom_expiration
321
333
  rcpt.expiration = pkgdata[:expiration].to_i
322
334
  D3.log "Updating expiration for #{rcpt.edition}", :info
323
335
  need_update = true
@@ -336,7 +348,6 @@ module D3
336
348
  need_update = true
337
349
  end # if
338
350
 
339
-
340
351
  rcpt.update if need_update
341
352
  end # each do basename, rcpt
342
353
  end # update_rcpts
@@ -347,7 +358,7 @@ module D3
347
358
  ### @return [void]
348
359
  ###
349
360
  def self.clean_doghouse
350
- D3.log "Checking for invalid puppies in the queue", :warn
361
+ D3.log 'Checking for invalid puppies in the queue', :warn
351
362
  D3::PUPPY_Q.pending_puppies.each do |basename, pup|
352
363
  unless D3::Package.all_ids.include? pup.id
353
364
  D3.log "Removing #{pup.edition} from puppy queue: no longer in d3", :info
@@ -356,7 +367,7 @@ module D3
356
367
  end
357
368
  if D3::Package.missing_data.keys.include? pup.id
358
369
  D3.log "Removing #{pup.edition} from puppy queue: status is 'missing'", :info
359
- D3::PUPPY_Q -pup
370
+ D3::PUPPY_Q - pup
360
371
  end
361
372
  end
362
373
  end
@@ -367,13 +378,13 @@ module D3
367
378
  ###
368
379
  ### @return [void]
369
380
  ###
370
- def self.do_auto_installs (options)
381
+ def self.do_auto_installs(options)
371
382
  verbose = options.verbose
372
- force = options.force or D3.forced?
373
- D3.log "Checking for new packages to auto-install", :warn
383
+ force = options.force || D3.forced?
384
+ D3.log 'Checking for new packages to auto-install', :warn
374
385
  D3::Client.set_env :auto_install
375
386
  begin # for ensure below
376
- installed_basenames = D3::Client::Receipt.basenames :refresh
387
+ D3::Client::Receipt.basenames :refresh
377
388
 
378
389
  # loop through the groups for this machine
379
390
  auto_groups = D3::Client.computer_groups.dup
@@ -382,12 +393,11 @@ module D3
382
393
  # this is the intersection of all pkg ids that get auto-installed
383
394
  # for the group, and all live pkg ids...
384
395
  # meaning this machine should have these pkg ids.
385
- live_ids_for_group = ( D3::Package.live_data.keys & D3::Package.auto_install_ids_for_group(group))
396
+ live_ids_for_group = (D3::Package.live_data.keys & D3::Package.auto_install_ids_for_group(group))
386
397
 
387
398
  live_ids_for_group.each do |live_id|
388
-
389
399
  # skip those not available
390
- next unless self.available_pkg_ids.include? live_id
400
+ next unless available_pkg_ids.include? live_id
391
401
 
392
402
  auto_install_basename = D3::Package.live_data[live_id][:basename]
393
403
 
@@ -395,7 +405,7 @@ module D3
395
405
  # the update_installed_pkgs method during sync.
396
406
  next if D3::Client::Receipt.all.keys.include? auto_install_basename
397
407
 
398
- new_pkg = D3::Package.new :id => live_id
408
+ new_pkg = D3::Package.new id: live_id
399
409
 
400
410
  if new_pkg.reboot?
401
411
  queued_id = puppy_in_queue new_pkg.basename
@@ -407,24 +417,25 @@ module D3
407
417
 
408
418
  begin
409
419
  D3.log "Auto-installing #{new_pkg.basename} for group '#{group}'", :info
420
+ cloud = cloud_dist_point_to_use(pkg: new_pkg)
410
421
  new_pkg.install(
411
- :admin => D3::AUTO_INSTALL_ADMIN,
412
- :verbose => verbose,
413
- :force => force,
414
- :puppywalk => options.puppies,
415
- :alt_download_url => self.cloud_dist_point_to_use
422
+ admin: D3::AUTO_INSTALL_ADMIN,
423
+ verbose: verbose,
424
+ force: force,
425
+ puppywalk: options.puppies,
426
+ alt_download_url: cloud
416
427
  )
417
428
  D3.log "Auto-installed #{new_pkg.basename}", :warn
418
429
  rescue JSS::MissingDataError, JSS::InvalidDataError, D3::InstallError
419
- D3.log "Skipping auto-install of #{new_pkg.edition}: #{$!}", :error
430
+ D3.log "Skipping auto-install of #{new_pkg.edition}: #{$ERROR_INFO}", :error
420
431
  D3.log_backtrace
421
432
  rescue D3::PreInstallError
422
- D3.log "There was an error with the pre-install script for #{new_pkg.edition}: #{$!}", :error
433
+ D3.log "There was an error with the pre-install script for #{new_pkg.edition}: #{$ERROR_INFO}", :error
423
434
  D3.log_backtrace
424
435
  rescue D3::PostInstallError
425
- D3.log "There was an error with the post-install script for #{new_pkg.edition}: #{$!} NOTE: #{new_pkg.edition} was installed, but may not work.", :error
436
+ D3.log "There was an error with the post-install script for #{new_pkg.edition}: #{$ERROR_INFO} NOTE: #{new_pkg.edition} was installed, but may not work.", :error
426
437
  D3.log_backtrace
427
- end #begin
438
+ end # begin
428
439
  end # live_ids_for_group.each do |live_id|
429
440
  end # each group
430
441
  ensure
@@ -436,8 +447,8 @@ module D3
436
447
  ###
437
448
  ###
438
449
  def self.clean_missing_receipts
439
- D3.log "Checking for receipts no longer in d3", :warn
440
- D3::Client::Receipt.all.values.select{|r| r.status == :missing}.each do |mrcpt|
450
+ D3.log 'Checking for receipts no longer in d3', :warn
451
+ D3::Client::Receipt.all.values.select { |r| r.status == :missing }.each do |mrcpt|
441
452
  D3.log "Removing receipt for missing edition #{mrcpt.edition}", :info
442
453
  D3::Client::Receipt.remove_receipt mrcpt.basename
443
454
  D3.log "Removed receipt for missing edition #{mrcpt.edition}", :info
@@ -451,10 +462,10 @@ module D3
451
462
  ###
452
463
  ### @return [void]
453
464
  ###
454
- def self.update_installed_pkgs (options)
465
+ def self.update_installed_pkgs(options)
455
466
  verbose = options.verbose
456
- force = options.force or D3.forced?
457
- D3.log "Checking for updates to installed packages", :warn
467
+ force = options.force || D3.forced?
468
+ D3.log 'Checking for updates to installed packages', :warn
458
469
  D3::Client.set_env :auto_update
459
470
  begin # see ensure below
460
471
 
@@ -463,7 +474,6 @@ module D3
463
474
 
464
475
  # loop through the install pkgs
465
476
  D3::Client::Receipt.all.values.each do |rcpt|
466
-
467
477
  # is there a live pkg for this basename?
468
478
  if live_basenames_to_ids[rcpt.basename]
469
479
  live_id = live_basenames_to_ids[rcpt.basename]
@@ -501,7 +511,7 @@ module D3
501
511
  if live_pkg_data[:reboot]
502
512
  queued_id = puppy_in_queue(live_pkg_data[:basename])
503
513
  if queued_id && queued_id >= live_pkg_data[:id]
504
- D3.log "Skipping auto-update of puppy-queue item #{ live_pkg_data[:edition]}, there's a newer one in the queue already", :info
514
+ D3.log "Skipping auto-update of puppy-queue item #{live_pkg_data[:edition]}, there's a newer one in the queue already", :info
505
515
  next
506
516
  end # if queued_id && queued_id >= live_pkg.id
507
517
  end # if live_pkg.reboot?
@@ -517,26 +527,27 @@ module D3
517
527
  expiration = rcpt.custom_expiration ? rcpt.expiration : nil
518
528
 
519
529
  # heres the pkg
520
- live_pkg = D3::Package.new :id => live_basenames_to_ids[rcpt.basename]
530
+ live_pkg = D3::Package.new id: live_basenames_to_ids[rcpt.basename]
521
531
 
522
532
  begin
533
+ cloud = cloud_dist_point_to_use(pkg: live_pkg)
523
534
  live_pkg.install(
524
- :admin => rcpt.admin,
525
- :expiration => expiration,
526
- :verbose => verbose,
527
- :force => force,
528
- :puppywalk => options.puppies,
529
- :alt_download_url => self.cloud_dist_point_to_use
530
- )
535
+ admin: rcpt.admin,
536
+ expiration: expiration,
537
+ verbose: verbose,
538
+ force: force,
539
+ puppywalk: options.puppies,
540
+ alt_download_url: cloud
541
+ )
531
542
  D3.log "Done updating #{rcpt.edition} (#{rcpt.status}) to #{live_pkg.edition} (#{live_pkg.status})", :info
532
543
  rescue JSS::MissingDataError, JSS::InvalidDataError, D3::InstallError
533
- D3.log "Skipping update of #{rcpt.edition} to #{live_pkg.edition}: #{$!}", :error
544
+ D3.log "Skipping update of #{rcpt.edition} to #{live_pkg.edition}: #{$ERROR_INFO}", :error
534
545
  D3.log_backtrace
535
546
  rescue D3::PreInstallError
536
- D3.log "There was an error with the pre-install script for #{live_pkg.edition}: #{$!}", :error
547
+ D3.log "There was an error with the pre-install script for #{live_pkg.edition}: #{$ERROR_INFO}", :error
537
548
  D3.log_backtrace
538
549
  rescue D3::PostInstallError
539
- D3.log "There was an error with the post-install script for #{live_pkg.edition}: #{$!} NOTE: #{live_pkg.edition} was installed, but may not work.", :error
550
+ D3.log "There was an error with the post-install script for #{live_pkg.edition}: #{$ERROR_INFO} NOTE: #{live_pkg.edition} was installed, but may not work.", :error
540
551
  D3.log_backtrace
541
552
  end # begin
542
553
  end # D3::Client::Receipt.all.values.each
@@ -551,7 +562,7 @@ module D3
551
562
  ###
552
563
  ### @return [void]
553
564
  ###
554
- def self.freeze_receipts (basenames)
565
+ def self.freeze_receipts(basenames)
555
566
  basenames.each do |bn|
556
567
  rcpt = D3::Client::Receipt.all[bn]
557
568
  next unless rcpt
@@ -571,7 +582,7 @@ module D3
571
582
  ###
572
583
  ### @return [void]
573
584
  ###
574
- def self.thaw_receipts (basenames)
585
+ def self.thaw_receipts(basenames)
575
586
  basenames.each do |bn|
576
587
  rcpt = D3::Client::Receipt.all[bn]
577
588
  next unless rcpt
@@ -591,11 +602,11 @@ module D3
591
602
  ###
592
603
  ### @return [void]
593
604
  ###
594
- def self.forget_receipts (basenames)
605
+ def self.forget_receipts(basenames)
595
606
  basenames.each do |bn|
596
607
  rcpt = D3::Client::Receipt.all[bn]
597
608
  next unless rcpt
598
- rcpt.apple_pkg_ids.each{|ar| system "/usr/sbin/pkgutil --forget '#{ar}'" }
609
+ rcpt.apple_pkg_ids.each { |ar| system "/usr/sbin/pkgutil --forget '#{ar}'" }
599
610
  D3::Client::Receipt.remove_receipt bn
600
611
  D3.log "Receipt for #{rcpt.edition} has been forgotten", :warn
601
612
  end
@@ -604,22 +615,23 @@ module D3
604
615
  ### Do any pending puppy installs right now, because we're
605
616
  ### syncing and --puppies option was given
606
617
  ###
607
- def self.do_puppy_queue_installs_from_sync (options)
618
+ def self.do_puppy_queue_installs_from_sync(options)
608
619
  return unless options.puppies
609
- return if D3::PUPPY_Q.q.empty?
610
- D3.log "Installing all pkgs from puppy-queue during sync with --puppies", :info
620
+ return if D3::PUPPY_Q.q.empty?
621
+ D3.log 'Installing all pkgs from puppy-queue during sync with --puppies', :info
611
622
  D3::PUPPY_Q.q.each do |basename, puppy|
612
623
  begin
613
624
  D3.log "Installing #{puppy.edition} from puppy-queue during sync with --puppies", :debug
614
- new_pkg = D3::Package.new :id => puppy.id
625
+ new_pkg = D3::Package.new id: puppy.id
626
+ cloud = cloud_dist_point_to_use(pkg: new_pkg)
615
627
  new_pkg.install(
616
- :admin => puppy.admin,
617
- :verbose => options.verbose,
618
- :force => puppy.force,
619
- :puppywalk => true,
620
- :expiration => puppy.expiration,
621
- :alt_download_url => self.cloud_dist_point_to_use
622
- )
628
+ admin: puppy.admin,
629
+ verbose: options.verbose,
630
+ force: puppy.force,
631
+ puppywalk: true,
632
+ expiration: puppy.expiration,
633
+ alt_download_url: cloud
634
+ )
623
635
 
624
636
  D3::PUPPY_Q - puppy
625
637
  rescue JSS::NoSuchItemError
@@ -627,13 +639,13 @@ module D3
627
639
  D3.log_backtrace
628
640
  D3::PUPPY_Q - puppy
629
641
  rescue JSS::MissingDataError, JSS::InvalidDataError, D3::InstallError
630
- D3.log "Skipping install of #{new_pkg.edition} from queue: #{$!}", :error
642
+ D3.log "Skipping install of #{new_pkg.edition} from queue: #{$ERROR_INFO}", :error
631
643
  D3.log_backtrace
632
644
  rescue D3::PreInstallError
633
- D3.log "There was an error with the pre-install script for #{new_pkg.edition}: #{$!}", :error
645
+ D3.log "There was an error with the pre-install script for #{new_pkg.edition}: #{$ERROR_INFO}", :error
634
646
  D3.log_backtrace
635
647
  rescue D3::PostInstallError
636
- D3.log "There was an error with the post-install script for #{new_pkg.edition}: #{$!} NOTE: #{new_pkg.edition} was installed, but may not work.", :error
648
+ D3.log "There was an error with the post-install script for #{new_pkg.edition}: #{$ERROR_INFO} NOTE: #{new_pkg.edition} was installed, but may not work.", :error
637
649
  D3.log_backtrace
638
650
  D3::PUPPY_Q - puppy
639
651
  end # begin
@@ -669,7 +681,7 @@ module D3
669
681
  ###
670
682
  def self.do_expirations (verbose = false, force = D3.forced?)
671
683
  @@editions_expired = []
672
- D3.log "Starting expiration check", :warn
684
+ D3.log 'Starting expiration check', :warn
673
685
 
674
686
  D3::Client::Receipt.all.values.each do |rcpt|
675
687
  begin
@@ -677,14 +689,14 @@ module D3
677
689
  expired_edition = rcpt.expire verbose, force
678
690
  @@editions_expired << expired_edition if expired_edition
679
691
  rescue
680
- D3.log "There was an error expiring #{rcpt.edition}: #{$!}", :error
692
+ D3.log "There was an error expiring #{rcpt.edition}: #{$ERROR_INFO}", :error
681
693
  D3.log_backtrace
682
694
  end
683
695
  end
684
696
 
685
697
  return true if @@editions_expired.empty?
686
698
 
687
- D3::Client.set_env :finished_expirations, @@editions_expired.join(" ")
699
+ D3::Client.set_env :finished_expirations, @@editions_expired.join(' ')
688
700
 
689
701
  if policy = D3::CONFIG.client_expiration_policy
690
702
  D3.run_policy policy, :expiration, verbose
@@ -701,11 +713,11 @@ module D3
701
713
  ###
702
714
  ### @return [Array<Integer>]
703
715
  ###
704
- def self.available_pkg_ids (refresh = false)
716
+ def self.available_pkg_ids(refresh = false)
705
717
  @@available_pkg_ids = nil if refresh
706
718
  return @@available_pkg_ids if @@available_pkg_ids
707
719
 
708
- self.computer_groups(:refresh) if refresh
720
+ computer_groups(:refresh) if refresh
709
721
 
710
722
  my_cpu = `/usr/bin/uname -p`
711
723
  my_os = `/usr/bin/sw_vers -productVersion`.chomp
@@ -715,7 +727,7 @@ module D3
715
727
  D3::Package.package_data.values.each do |pkg|
716
728
  next unless JSS.os_ok? pkg[:oses], my_os
717
729
  next unless JSS.processor_ok? pkg[:required_processor], my_cpu
718
- @@available_pkg_ids << pkg[:id] if (pkg[:excluded_groups] & self.computer_groups).empty?
730
+ @@available_pkg_ids << pkg[:id] if (pkg[:excluded_groups] & computer_groups).empty?
719
731
  end # do pkg
720
732
  @@available_pkg_ids
721
733
  end
@@ -726,10 +738,10 @@ module D3
726
738
  ###
727
739
  ### @return [Array<String>] the JSS groups to which this machine belongs
728
740
  ###
729
- def self.computer_groups (refresh = false)
741
+ def self.computer_groups(refresh = false)
730
742
  @@computer_groups = nil if refresh
731
743
  return @@computer_groups if @@computer_groups
732
- @@computer_groups = JSS::Computer.new(:udid => JSS::Client.udid).computer_groups
744
+ @@computer_groups = JSS::Computer.new(udid: JSS::Client.udid).computer_groups
733
745
  end
734
746
 
735
747
  ### The cloud dist point to use for installs
@@ -743,29 +755,61 @@ module D3
743
755
  ### @return [String, nil] The download url for the cloud dist point,
744
756
  ### if we should use one, or nil.
745
757
  ###
746
- def self.cloud_dist_point_to_use(refresh = false)
758
+ def self.cloud_dist_point_to_use(refresh = false, pkg: nil)
759
+ byebug
760
+ raise 'You must provide a pkg' unless pkg.is_a? D3::Package
761
+
747
762
  @@cloud_dist_url == :unknown if refresh
748
- return @@cloud_dist_url unless @@cloud_dist_url == :unknown
749
763
 
750
764
  mdp = JSS::DistributionPoint.my_distribution_point
751
765
 
766
+ # Should we try a cloud distribution point if the primary is unavailable ?
752
767
  unless D3::CONFIG.client_try_cloud_distpoint
753
768
  D3.log "Config is not to try cloud, using only Distribution Point '#{mdp.name}'", :info
754
- return @@cloud_dist_url = nil
769
+ return @@cloud_dist_url = nil
755
770
  end
756
771
 
757
- if mdp.reachable_for_download?(self.get_ro_pass :http) or mdp.reachable_for_download?(self.get_ro_pass :dist)
772
+ # Can we reach the primary distribution point? Return nil if true.
773
+ if mdp.reachable_for_download?(get_ro_pass(:http)) || mdp.reachable_for_download?(get_ro_pass(:dist))
758
774
  D3.log "Distribution Point '#{mdp.name}' is reachable, no need for cloud", :info
759
- return @@cloud_dist_url = nil
775
+ return @@cloud_dist_url = nil
760
776
  end
761
- cloud_url = self.cloud_distribution_point_url
762
- if cloud_url
763
- D3.log "Cloud distribution URL found '#{cloud_url}', will use for pkg installs", :info
764
- else
765
- D3.log "No cloud distribution URL found.", :info
777
+
778
+ # Get the cloud distribution point defined in the JSS
779
+ cloud_url = cloud_distribution_point_url
780
+ unless cloud_url
781
+ D3.log 'No cloud distribution URL found.', :info
782
+ return @@cloud_dist_url = nil
766
783
  end
784
+ # Make sure the package is available on the cloud distribution point
785
+ pkg_available = validate_pkg_in_cloud(cloud_url, pkg) if cloud_url
786
+ unless pkg_available
787
+ D3.log "#{pkg.edition} is not available in the cloud", :info
788
+ return @@cloud_dist_url = nil
789
+ end
790
+
767
791
  @@cloud_dist_url = cloud_url
768
- end
792
+ end # self.cloud_dist_point_to_use
793
+
794
+ # given a cloud url and a D3::Package
795
+ # is the pkg available at that url?
796
+ #
797
+ # @return [Boolean]
798
+ #
799
+ def self.validate_pkg_in_cloud(url, pkg)
800
+ full_url = "#{url}/#{pkg.filename}"
801
+ dummy_install_target = Pathname.new('/Volumes/' + Time.now.asctime)
802
+ jamf_cmd = "#{JSS::Client::JAMF_BINARY} install -package #{Shellwords.escape pkg.filename} -path #{Shellwords.escape full_url} -target #{Shellwords.escape dummy_install_target}/ -showProgress -verbose"
803
+ Open3.popen2e(jamf_cmd) do |_stdin, stdout_err, wait_thr|
804
+ stdout_err.each do |line|
805
+ if /^\d*+\.\d*+% / =~ line
806
+ Process.kill('KILL', wait_thr.pid)
807
+ return true
808
+ end # end if /^\d*+\.\d*+% / =~ line
809
+ end # end stdout_err.each do |line|
810
+ return false
811
+ end # end Open3.popen2e(jamf_cmd) do |_stdin, stdout_err, wait_thr|
812
+ end # def self.validate_pkg_in_cloud pkg
769
813
 
770
814
  ### Is a Cloud Distribution Point available for pkg downloads?
771
815
  ### If so, return the url for downloading pkg files
@@ -774,12 +818,12 @@ module D3
774
818
  ### @return [String, nil]
775
819
  ###
776
820
  def self.cloud_distribution_point_url
777
- result = JSS::DB_CNX.db.query "SELECT download_url, cdn_url FROM cloud_distribution_point"
821
+ result = JSS::DB_CNX.db.query 'SELECT download_url, cdn_url FROM cloud_distribution_point'
778
822
  urls = result.fetch
779
823
  result.free
780
824
  return nil if urls.nil?
781
- return nil if urls[0].empty? and urls[1].empty?
782
- return urls[0].empty? ? urls[1] : urls[0]
825
+ return nil if urls[0].empty? || urls[1].empty?
826
+ urls[0].empty? ? urls[1] : urls[0]
783
827
  end
784
828
 
785
829
  ### Given a basename, is any edition of it in the puppy queue?
@@ -789,26 +833,32 @@ module D3
789
833
  ###
790
834
  ### @return [Integer, nil] The id of the queued package for the basename, if any
791
835
  ###
792
- def self.puppy_in_queue (basename)
836
+ def self.puppy_in_queue(basename)
793
837
  pup = D3::PUPPY_Q.queue[basename]
794
838
  return nil unless pup
795
- return pup.id
839
+ pup.id
796
840
  end # basename in puppy queue
797
841
 
798
-
799
842
  ### get the executable path of the current foreground GUI app. NOTE, if you
800
843
  ### have fast user switching on, or multi-user screensharing,
801
844
  ### this only gets the one currenly using the physical console
802
845
  ###
803
- ### @return [Pathname] the path to the executable of the current foreground app
846
+ ### @return [Pathname, nil] the path to the executable of the current foreground app, nil if none
804
847
  ###
805
848
  def self.foreground_executable_path
806
- lsai = "/usr/bin/lsappinfo"
807
- ls_app_id = `#{lsai} front`.chomp
849
+ lsai = '/usr/bin/lsappinfo'
850
+ ls_app_id = `#{lsai} front`.chomp
851
+ return nil if ls_app_id == '[ NULL ] '
808
852
 
809
853
  raw = `#{lsai} info -only executablepath '#{ls_app_id}'`.chomp
810
- path = raw.split(/=\s*"/).last.chomp('"')
811
- return Pathname.new path
854
+ return nil if raw.empty?
855
+
856
+ path = raw.split(/=\s*"/).last
857
+ return nil unless path
858
+ path.chomp!('"')
859
+ path.empty? ? nil : Pathname.new(path)
812
860
  end
861
+
813
862
  end # class
863
+
814
864
  end # module D3