depot3 3.0.15 → 3.0.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +21 -1
- data/README.md +6 -6
- data/bin/d3 +3 -3
- data/bin/d3admin +257 -293
- data/lib/d3/admin/add.rb +98 -94
- data/lib/d3/admin/interactive.rb +195 -161
- data/lib/d3/admin/options.rb +424 -412
- data/lib/d3/admin/prefs.rb +73 -42
- data/lib/d3/admin/validate.rb +50 -43
- data/lib/d3/client/auth.rb +4 -2
- data/lib/d3/client/class_methods.rb +169 -119
- data/lib/d3/client/receipt.rb +2 -2
- data/lib/d3/database.rb +167 -180
- data/lib/d3/log.rb +2 -3
- data/lib/d3/package/server_actions.rb +2 -3
- data/lib/d3/package/validate.rb +63 -70
- data/lib/d3/version.rb +1 -2
- metadata +2 -2
data/lib/d3/client/auth.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
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
|
-
:
|
96
|
-
:
|
97
|
-
:
|
98
|
-
:
|
99
|
-
:
|
100
|
-
:
|
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
|
-
|
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}: #{
|
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}: #{
|
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}: #{
|
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}: #{
|
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
|
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
|
-
|
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
|
196
|
+
def self.dequeue_puppies(puppies)
|
184
197
|
puppies = [puppies] if puppies.is_a? String
|
185
|
-
puppies = D3::PUPPY_Q.pups if puppies.include?
|
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
|
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
|
219
|
+
def self.sync(options = OpenStruct.new)
|
207
220
|
D3::Client.set_env :sync
|
208
|
-
D3.log
|
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
|
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
|
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)
|
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
|
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
|
381
|
+
def self.do_auto_installs(options)
|
371
382
|
verbose = options.verbose
|
372
|
-
force =
|
373
|
-
D3.log
|
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
|
-
|
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 = (
|
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
|
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 :
|
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
|
-
:
|
412
|
-
:
|
413
|
-
:
|
414
|
-
:
|
415
|
-
:
|
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}: #{
|
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}: #{
|
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}: #{
|
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
|
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
|
465
|
+
def self.update_installed_pkgs(options)
|
455
466
|
verbose = options.verbose
|
456
|
-
force =
|
457
|
-
D3.log
|
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 #{
|
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 :
|
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
|
-
:
|
525
|
-
:
|
526
|
-
:
|
527
|
-
:
|
528
|
-
:
|
529
|
-
:
|
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}: #{
|
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}: #{
|
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}: #{
|
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
|
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
|
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
|
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
|
618
|
+
def self.do_puppy_queue_installs_from_sync(options)
|
608
619
|
return unless options.puppies
|
609
|
-
return if
|
610
|
-
D3.log
|
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 :
|
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
|
-
:
|
617
|
-
:
|
618
|
-
:
|
619
|
-
:
|
620
|
-
:
|
621
|
-
:
|
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: #{
|
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}: #{
|
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}: #{
|
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
|
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}: #{
|
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
|
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
|
-
|
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] &
|
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
|
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(:
|
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 =
|
769
|
+
return @@cloud_dist_url = nil
|
755
770
|
end
|
756
771
|
|
757
|
-
|
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 =
|
775
|
+
return @@cloud_dist_url = nil
|
760
776
|
end
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
D3.log
|
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
|
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?
|
782
|
-
|
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
|
836
|
+
def self.puppy_in_queue(basename)
|
793
837
|
pup = D3::PUPPY_Q.queue[basename]
|
794
838
|
return nil unless pup
|
795
|
-
|
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 =
|
807
|
-
ls_app_id =
|
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
|
-
|
811
|
-
|
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
|