depot3 3.0.15 → 3.0.20

Sign up to get free protection for your applications and to get access to all the features.
@@ -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