depot3 0.0.0a1 → 3.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +55 -1
- data/bin/d3 +323 -0
- data/bin/d3admin +1011 -0
- data/bin/d3helper +354 -0
- data/bin/puppytime +334 -0
- data/data/d3/com.pixar.d3.RepoMan.plist +23 -0
- data/data/d3/d3.conf.example +507 -0
- data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftAppKit.dylib +0 -0
- data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCore.dylib +0 -0
- data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCoreData.dylib +0 -0
- data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCoreGraphics.dylib +0 -0
- data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftCoreImage.dylib +0 -0
- data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftDarwin.dylib +0 -0
- data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftDispatch.dylib +0 -0
- data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftFoundation.dylib +0 -0
- data/data/d3/d3RepoMan.app/Contents/Frameworks/libswiftObjectiveC.dylib +0 -0
- data/data/d3/d3RepoMan.app/Contents/Info.plist +56 -0
- data/data/d3/d3RepoMan.app/Contents/MacOS/d3RepoMan +0 -0
- data/data/d3/d3RepoMan.app/Contents/PkgInfo +1 -0
- data/data/d3/d3RepoMan.app/Contents/Resources/Base.lproj/MainMenu.nib +0 -0
- data/data/d3/d3RepoMan.app/Contents/Resources/last-foreground-times-template.plist +5 -0
- data/data/d3/d3RepoMan.app/Contents/_CodeSignature/CodeResources +214 -0
- data/data/d3/puppytime/ImageLicenses.txt +165 -0
- data/data/d3/puppytime/notification_image +1 -0
- data/data/d3/puppytime/opt_out_image +1 -0
- data/data/d3/puppytime/slideshow/2008-07-11_White_German_Shepherd_pup_chilling_at_the_Coker_Arboretum.jpg +0 -0
- data/data/d3/puppytime/slideshow/2009-04-21_APBT_pup_on_deck.jpg +0 -0
- data/data/d3/puppytime/slideshow/A_puppy_Yorkie.jpg +0 -0
- data/data/d3/puppytime/slideshow/Alert_Pug_Puppy.jpg +0 -0
- data/data/d3/puppytime/slideshow/Australian_Cattle_Dog_puppies_04.JPG +0 -0
- data/data/d3/puppytime/slideshow/Beagle_puppy_Cadet.jpg +0 -0
- data/data/d3/puppytime/slideshow/Bernese_Mountain_Dog.jpg +0 -0
- data/data/d3/puppytime/slideshow/Bloodhound_Puppy.jpg +0 -0
- data/data/d3/puppytime/slideshow/Boston_terrier_with_toy.jpg +0 -0
- data/data/d3/puppytime/slideshow/Boxer_puppy_fawn_portrai.jpg +0 -0
- data/data/d3/puppytime/slideshow/Caracal_kitten.jpg +0 -0
- data/data/d3/puppytime/slideshow/Chihuahua_&_Doberman_Pup.jpg +0 -0
- data/data/d3/puppytime/slideshow/Cuccioli_di_Margot_a_35_gg_Basenjis.jpg +0 -0
- data/data/d3/puppytime/slideshow/Dalmatian_puppy_03.jpg +0 -0
- data/data/d3/puppytime/slideshow/GoldenRetrieverPuppyDaisyParker.JPG +0 -0
- data/data/d3/puppytime/slideshow/Green_eyed_beige_Chihuahua.jpg +0 -0
- data/data/d3/puppytime/slideshow/Let_Sleeping_Dogs_Lie.jpg +0 -0
- data/data/d3/puppytime/slideshow/Meatball_-_French_Bulldog_Puppy.jpg +0 -0
- data/data/d3/puppytime/slideshow/Oola_-_9_weeks.jpg +0 -0
- data/data/d3/puppytime/slideshow/Pancho0008.JPG +0 -0
- data/data/d3/puppytime/slideshow/Pomeranian_orange-sable_Coco.jpg +0 -0
- data/data/d3/puppytime/slideshow/Pug_puppy_001.jpg +0 -0
- data/data/d3/puppytime/slideshow/Puggle_puppy_6_weeks.JPG +0 -0
- data/data/d3/puppytime/slideshow/Puli_kan.jpg +0 -0
- data/data/d3/puppytime/slideshow/Puppy_French_Bulldog.jpg +0 -0
- data/data/d3/puppytime/slideshow/Rocco_the_Bulldog.jpg +0 -0
- data/data/d3/puppytime/slideshow/Rottweiler_Face.jpg +0 -0
- data/data/d3/puppytime/slideshow/Saint_Bernard_puppy.jpg +0 -0
- data/data/d3/puppytime/slideshow/Scottish_froment.jpg +0 -0
- data/data/d3/puppytime/slideshow/Shar_pei_puppy_(age_2_months).jpg +0 -0
- data/data/d3/puppytime/slideshow/Shiba-Inu_beim_Spielen_im_Schnee.JPG +0 -0
- data/data/d3/puppytime/slideshow/Smooth-coat_Border_Collie_puppy..jpg +0 -0
- data/data/d3/puppytime/slideshow/Smooth_Dachshund_puppies.jpg +0 -0
- data/data/d3/puppytime/slideshow/Snow_dog.jpg +0 -0
- data/data/d3/puppytime/slideshow/Taylor_the_Pembroke_Welsh_Corgi.png +0 -0
- data/data/d3/puppytime/slideshow/Weim_Pups_001.jpg +0 -0
- data/data/d3/puppytime/slideshow/Westie_pups.jpg +0 -0
- data/data/d3/puppytime/slideshow/Yellow_Labrador_puppies_(4165737325).jpg +0 -0
- data/lib/d3/admin/add.rb +451 -0
- data/lib/d3/admin/auth.rb +470 -0
- data/lib/d3/admin/edit.rb +297 -0
- data/lib/d3/admin/help.rb +396 -0
- data/lib/d3/admin/interactive.rb +972 -0
- data/lib/d3/admin/options.rb +454 -0
- data/lib/d3/admin/prefs.rb +204 -0
- data/lib/d3/admin/report.rb +727 -0
- data/lib/d3/admin/state.rb +42 -0
- data/lib/d3/admin/validate.rb +413 -0
- data/lib/d3/admin.rb +42 -0
- data/lib/d3/basename.rb +217 -0
- data/lib/d3/client/auth.rb +108 -0
- data/lib/d3/client/class_methods.rb +766 -0
- data/lib/d3/client/class_variables.rb +47 -0
- data/lib/d3/client/cli.rb +187 -0
- data/lib/d3/client/environment.rb +134 -0
- data/lib/d3/client/help.rb +110 -0
- data/lib/d3/client/lists.rb +314 -0
- data/lib/d3/client/receipt.rb +1173 -0
- data/lib/d3/client.rb +45 -0
- data/lib/d3/configuration.rb +319 -0
- data/lib/d3/constants.rb +60 -0
- data/lib/d3/database.rb +488 -0
- data/lib/d3/exceptions.rb +44 -0
- data/lib/d3/log.rb +271 -0
- data/lib/d3/package/aliases.rb +80 -0
- data/lib/d3/package/attributes.rb +97 -0
- data/lib/d3/package/class_methods.rb +817 -0
- data/lib/d3/package/class_variables.rb +46 -0
- data/lib/d3/package/client_actions.rb +293 -0
- data/lib/d3/package/constants.rb +58 -0
- data/lib/d3/package/constructor.rb +191 -0
- data/lib/d3/package/getters.rb +164 -0
- data/lib/d3/package/mixins.rb +39 -0
- data/lib/d3/package/private_methods.rb +227 -0
- data/lib/d3/package/questions.rb +95 -0
- data/lib/d3/package/server_actions.rb +683 -0
- data/lib/d3/package/setters.rb +326 -0
- data/lib/d3/package/validate.rb +448 -0
- data/lib/d3/package.rb +51 -0
- data/lib/d3/puppytime/pending_puppy.rb +108 -0
- data/lib/d3/puppytime/puppy_queue.rb +274 -0
- data/lib/d3/puppytime.rb +68 -0
- data/lib/d3/state.rb +105 -0
- data/lib/d3/utility.rb +325 -0
- data/lib/d3/version.rb +1 -1
- metadata +162 -9
@@ -0,0 +1,683 @@
|
|
1
|
+
### Copyright 2016 Pixar
|
2
|
+
###
|
3
|
+
### Licensed under the Apache License, Version 2.0 (the "Apache License")
|
4
|
+
### with the following modification; you may not use this file except in
|
5
|
+
### compliance with the Apache License and the following modification to it:
|
6
|
+
### Section 6. Trademarks. is deleted and replaced with:
|
7
|
+
###
|
8
|
+
### 6. Trademarks. This License does not grant permission to use the trade
|
9
|
+
### names, trademarks, service marks, or product names of the Licensor
|
10
|
+
### and its affiliates, except as required to comply with Section 4(c) of
|
11
|
+
### the License and to reproduce the content of the NOTICE file.
|
12
|
+
###
|
13
|
+
### You may obtain a copy of the Apache License at
|
14
|
+
###
|
15
|
+
### http://www.apache.org/licenses/LICENSE-2.0
|
16
|
+
###
|
17
|
+
### Unless required by applicable law or agreed to in writing, software
|
18
|
+
### distributed under the Apache License with the above modification is
|
19
|
+
### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
20
|
+
### KIND, either express or implied. See the Apache License for the specific
|
21
|
+
### language governing permissions and limitations under the Apache License.
|
22
|
+
###
|
23
|
+
###
|
24
|
+
|
25
|
+
|
26
|
+
module D3
|
27
|
+
class Package < JSS::Package
|
28
|
+
|
29
|
+
### Create this package in the JSS if needed, and in d3
|
30
|
+
###
|
31
|
+
### @return [Integer] the JSS id of the package
|
32
|
+
###
|
33
|
+
def create
|
34
|
+
|
35
|
+
# if it's already there, just return
|
36
|
+
return @id if @in_d3
|
37
|
+
|
38
|
+
# gotta know who did this
|
39
|
+
raise JSS::MissingDataError, "An admin name must be set before creating this new d3 package. Use #admin= " unless @admin
|
40
|
+
|
41
|
+
# create the JSS package if needed
|
42
|
+
super unless @in_jss
|
43
|
+
|
44
|
+
# who and when are we adding this pkg?
|
45
|
+
@added_date = Time.now
|
46
|
+
@added_by = @admin
|
47
|
+
|
48
|
+
# change status from unsaved to pilot
|
49
|
+
@status = :pilot
|
50
|
+
|
51
|
+
# loop through the field definitions, and
|
52
|
+
# use them to get data for the insert statement
|
53
|
+
field_names = []
|
54
|
+
sql_values = []
|
55
|
+
P_FIELDS.each_pair do |key,field_def|
|
56
|
+
field_names << field_def[:field_name]
|
57
|
+
# nils and empty strings become NULL
|
58
|
+
sql_values << (self.send(key).to_s.empty? ? 'NULL' : "'#{to_sql(key)}'")
|
59
|
+
end # do |key,field_def
|
60
|
+
|
61
|
+
|
62
|
+
# use the two arrays to build the SQL statement
|
63
|
+
stmt = JSS::DB_CNX.db.prepare <<-ENDINSERT
|
64
|
+
INSERT INTO #{P_TABLE[:table_name]} (
|
65
|
+
#{field_names.join(",\n ")}
|
66
|
+
) VALUES (
|
67
|
+
#{sql_values.join(",\n ")}
|
68
|
+
)
|
69
|
+
ENDINSERT
|
70
|
+
|
71
|
+
# Execute it to create the record
|
72
|
+
stmt_result = stmt.execute
|
73
|
+
|
74
|
+
|
75
|
+
# while we're writing to the db, mark any missing packages as missing
|
76
|
+
mark_missing_packages
|
77
|
+
|
78
|
+
|
79
|
+
@in_d3 = true
|
80
|
+
return @id
|
81
|
+
end # create
|
82
|
+
|
83
|
+
### Update this package in the JSS and in d3
|
84
|
+
###
|
85
|
+
### @return [Integer] the JSS id of the package
|
86
|
+
###
|
87
|
+
def update
|
88
|
+
|
89
|
+
# we might be importing an existing JSS pkg to d3, which
|
90
|
+
# means we need to create the d3 record, but the JSS record needs updating
|
91
|
+
create if @importing and (not @in_d3)
|
92
|
+
|
93
|
+
# update the JSS first, if needed
|
94
|
+
super
|
95
|
+
|
96
|
+
# and return unless we need to do something.
|
97
|
+
return unless @need_to_update_d3
|
98
|
+
|
99
|
+
# Loop thru the field defs to build the SQL update statement
|
100
|
+
new_vals = []
|
101
|
+
P_FIELDS.each_pair do |key,field_def|
|
102
|
+
|
103
|
+
# start builing the SET clause values, e.g. "basename = 'foobar'"
|
104
|
+
field_val = "#{field_def[:field_name]} = "
|
105
|
+
|
106
|
+
# finish the SET clause value
|
107
|
+
field_val << (self.send(key).to_s.empty? ? 'NULL' : "'#{to_sql(key)}'")
|
108
|
+
|
109
|
+
# add it to the array
|
110
|
+
new_vals << field_val
|
111
|
+
end # do |key,field_def
|
112
|
+
|
113
|
+
# use the new_vals array to create the update statement
|
114
|
+
stmt = JSS::DB_CNX.db.prepare <<-ENDUPDATE
|
115
|
+
UPDATE #{P_TABLE[:table_name]} SET
|
116
|
+
#{new_vals.join(", ")}
|
117
|
+
WHERE
|
118
|
+
#{P_FIELDS[:id][:field_name]} = #{@id}
|
119
|
+
ENDUPDATE
|
120
|
+
|
121
|
+
# Execute it to update the record
|
122
|
+
stmt_result = stmt.execute
|
123
|
+
|
124
|
+
# while we're writing to the db, mark any missing packages as missing
|
125
|
+
mark_missing_packages
|
126
|
+
|
127
|
+
return @id
|
128
|
+
|
129
|
+
end # update
|
130
|
+
|
131
|
+
### An alias for both save and update
|
132
|
+
###
|
133
|
+
def save
|
134
|
+
if @in_jss
|
135
|
+
update # this will create the d3 data if needed
|
136
|
+
else
|
137
|
+
create
|
138
|
+
end
|
139
|
+
end # save
|
140
|
+
|
141
|
+
### Make this package the live one for its basename
|
142
|
+
###
|
143
|
+
### @param admin[String] the name of the admin doing this.
|
144
|
+
###
|
145
|
+
### @return [void]
|
146
|
+
###
|
147
|
+
def make_live(admin = @admin)
|
148
|
+
|
149
|
+
return :live if @status == :live
|
150
|
+
|
151
|
+
# gotta know who did this
|
152
|
+
raise JSS::MissingDataError, "An admin name must be set before making this d3 package live. Use the admin= method." if admin.to_s.empty?
|
153
|
+
@admin = admin
|
154
|
+
|
155
|
+
# who and when are we making this pkg live?
|
156
|
+
@release_date = Time.now
|
157
|
+
@released_by = @admin
|
158
|
+
|
159
|
+
id_field = P_FIELDS[:id][:field_name]
|
160
|
+
status_field = P_FIELDS[:status][:field_name]
|
161
|
+
basename_field = P_FIELDS[:basename][:field_name]
|
162
|
+
rel_date_field = P_FIELDS[:release_date][:field_name]
|
163
|
+
rel_by_field = P_FIELDS[:released_by][:field_name]
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
# if any OLDER pkg is live for this basename, make it deprecated
|
168
|
+
q = <<-ENDUPDATE
|
169
|
+
UPDATE #{P_TABLE[:table_name]}
|
170
|
+
SET #{status_field} = '#{P_FIELDS[:status][:to_sql].call(:deprecated)}'
|
171
|
+
WHERE #{basename_field} = '#{to_sql :basename}'
|
172
|
+
AND #{status_field} = '#{P_FIELDS[:status][:to_sql].call(:live)}'
|
173
|
+
AND #{id_field} < '#{to_sql(:id)}'
|
174
|
+
ENDUPDATE
|
175
|
+
stmt = JSS::DB_CNX.db.prepare q
|
176
|
+
stmt_result = stmt.execute
|
177
|
+
|
178
|
+
# now make any older pilot pkgs for this basename :skipped
|
179
|
+
q = <<-ENDUPDATE
|
180
|
+
UPDATE #{P_TABLE[:table_name]}
|
181
|
+
SET #{status_field} = '#{P_FIELDS[:status][:to_sql].call(:skipped)}'
|
182
|
+
WHERE #{basename_field} = '#{to_sql :basename}'
|
183
|
+
AND #{id_field} < #{to_sql(:id)}
|
184
|
+
AND #{status_field} = '#{P_FIELDS[:status][:to_sql].call(:pilot)}'
|
185
|
+
ENDUPDATE
|
186
|
+
stmt = JSS::DB_CNX.db.prepare q
|
187
|
+
stmt_result = stmt.execute
|
188
|
+
|
189
|
+
|
190
|
+
# any NEWER pkgs for this basename, become pilot (perhaps again)
|
191
|
+
# This is for when we re-enliven an old pkg
|
192
|
+
q = <<-ENDUPDATE
|
193
|
+
UPDATE #{P_TABLE[:table_name]}
|
194
|
+
SET #{status_field} = '#{P_FIELDS[:status][:to_sql].call(:pilot)}'
|
195
|
+
WHERE #{basename_field} = '#{to_sql :basename}'
|
196
|
+
AND #{id_field} > '#{to_sql(:id)}'
|
197
|
+
ENDUPDATE
|
198
|
+
stmt = JSS::DB_CNX.db.prepare q
|
199
|
+
stmt_result = stmt.execute
|
200
|
+
|
201
|
+
# now make this pkg live
|
202
|
+
@status = :live
|
203
|
+
q = <<-ENDUPDATE
|
204
|
+
UPDATE #{P_TABLE[:table_name]} SET
|
205
|
+
#{status_field} = '#{to_sql :status}',
|
206
|
+
#{rel_by_field} = '#{to_sql :released_by}',
|
207
|
+
#{rel_date_field} = '#{to_sql :release_date}'
|
208
|
+
WHERE #{id_field} = #{@id}
|
209
|
+
ENDUPDATE
|
210
|
+
stmt = JSS::DB_CNX.db.prepare q
|
211
|
+
stmt_result = stmt.execute
|
212
|
+
|
213
|
+
# update our knowledge of the world
|
214
|
+
self.class.package_data :refresh
|
215
|
+
|
216
|
+
# while we're writing to the db, mark any missing packages as missing
|
217
|
+
mark_missing_packages
|
218
|
+
|
219
|
+
puts "Done '#{edition}' is now live."
|
220
|
+
|
221
|
+
# auto_clean if we should
|
222
|
+
auto_clean(admin) if D3::CONFIG.admin_auto_clean
|
223
|
+
|
224
|
+
# run any post-make-live script if needed
|
225
|
+
run_make_live_script
|
226
|
+
|
227
|
+
end # make live
|
228
|
+
|
229
|
+
### Add or replace a pre- or post- script for this package.
|
230
|
+
###
|
231
|
+
### This adds a new script to the JSS, and the sets this package to
|
232
|
+
### use it.
|
233
|
+
###
|
234
|
+
### If the desired script already exists in the JSS, use an appropriate setter method:
|
235
|
+
### {#pre_install_script_id=}, {#post_install_script_id=}, {#pre_remove_script_id=}, {#post_remove_script_id=},
|
236
|
+
### {#pre_install_script_name=},{#post_install_script_name=}, {#pre_remove_script_name=}, {#post_remove_script_name=}
|
237
|
+
###
|
238
|
+
### @param args[Hash]
|
239
|
+
###
|
240
|
+
### @option args :script_type[Symbol] which script to set? One of :pre_install, :post_install, :pre_remove, :post_remove
|
241
|
+
###
|
242
|
+
### @option args :source[String,Pathname] the script code, or a full path to a file containing the script code.
|
243
|
+
### If the value is a String and doesn't start with a /, it's considered to be the script code.
|
244
|
+
###
|
245
|
+
### @option args :script_name[String] the name of the new script in the JSS. Defaults to "<basename>-d3<script_type>-YYYYmmddHHMMSS"
|
246
|
+
###
|
247
|
+
### @option args :script_category[String] the name of the JSS category for this script. Defaults to the value of D3:Package::DFT_SCRIPT_CATEGORY
|
248
|
+
###
|
249
|
+
### @option args :delete_current[Boolean] if this new script is replacing one for this pkg, should the old one be deleted from the JSS?
|
250
|
+
###
|
251
|
+
### @return [Integer] the id of the newly created JSS::Script.
|
252
|
+
###
|
253
|
+
def new_script (args = {})
|
254
|
+
|
255
|
+
raise JSS::InvalidDataError, ":script_type must be one of :#{SCRIPT_TYPES.join(', :')}" unless SCRIPT_TYPES.include? args[:script_type]
|
256
|
+
|
257
|
+
args[:script_category] ||= D3::CONFIG.jss_default_script_category
|
258
|
+
if args[:script_category]
|
259
|
+
raise JSS::NoSuchItemError, "No such category '#{args[:script_category]}' in the JSS." unless JSS::Category.all_names.include? args[:script_category]
|
260
|
+
end
|
261
|
+
|
262
|
+
args[:script_name] ||= "#{@basename}-d3#{args[:script_type]}-#{Time.now.strftime('%Y%m%d%H%M%S')}"
|
263
|
+
|
264
|
+
file_source = nil
|
265
|
+
|
266
|
+
file_source = case args[:source]
|
267
|
+
when Pathname
|
268
|
+
args[:source]
|
269
|
+
when String
|
270
|
+
Pathname.new(args[:source]) if args[:source].start_with? "/"
|
271
|
+
else
|
272
|
+
raise JSS::InvalidDataError, ":source must be a full path (Pathname or String), or a String containing the script code."
|
273
|
+
end # case
|
274
|
+
|
275
|
+
if file_source
|
276
|
+
raise JSS::MissingDataError, "The file #{file_source} is missing or unreadable." unless file_source.readable?
|
277
|
+
code = file_source.read
|
278
|
+
else
|
279
|
+
code = args[:source]
|
280
|
+
end
|
281
|
+
|
282
|
+
# get the new script into the JSS
|
283
|
+
script = JSS::Script.new :id => :new, :name => args[:script_name]
|
284
|
+
script.contents = code
|
285
|
+
script.category = args[:script_category]
|
286
|
+
new_script_id = script.save
|
287
|
+
|
288
|
+
# update our knowledge of all JSS scripts so the next steps don't fail.
|
289
|
+
JSS::Script.all :refresh
|
290
|
+
|
291
|
+
case args[:script_type]
|
292
|
+
when :pre_install
|
293
|
+
old_script_id = pre_install_script_id
|
294
|
+
self.pre_install_script_id = new_script_id
|
295
|
+
when :post_install
|
296
|
+
old_script_id = post_install_script_id
|
297
|
+
self.post_install_script_id = new_script_id
|
298
|
+
when :pre_remove
|
299
|
+
old_script_id = pre_remove_script_id
|
300
|
+
self.pre_remove_script_id = new_script_id
|
301
|
+
when :post_remove
|
302
|
+
old_script_id = post_remove_script_id
|
303
|
+
self.post_remove_script_id = new_script_id
|
304
|
+
end
|
305
|
+
|
306
|
+
# delete the old?
|
307
|
+
if args[:delete_current] and old_script_id
|
308
|
+
JSS::Script.new(:id => old_script_id).delete if JSS::Script.all_ids.include? old_script_id
|
309
|
+
end
|
310
|
+
|
311
|
+
new_script_id
|
312
|
+
end # new_script
|
313
|
+
|
314
|
+
### Perform any auto_cleanup, if the config says we should
|
315
|
+
###
|
316
|
+
### @param admin[String] the admin doing the make-live
|
317
|
+
###
|
318
|
+
### @return [void]
|
319
|
+
###
|
320
|
+
def auto_clean (admin)
|
321
|
+
|
322
|
+
### safety
|
323
|
+
return unless D3::CONFIG.admin_auto_clean
|
324
|
+
|
325
|
+
puts "Starting auto-clean of old packages for '#{@basename}'"
|
326
|
+
|
327
|
+
#### First the deprecated pkgs
|
328
|
+
|
329
|
+
# the id's of the deprecated pkgs for this basename, in numerical order
|
330
|
+
# the last ones are the newest.
|
331
|
+
deprecated_ids = D3::Package.deprecated_data.values.select{|dp| dp[:basename] == @basename}
|
332
|
+
deprecated_ids.map!{|dp| dp[:id] }.sort!
|
333
|
+
|
334
|
+
# keeping any?
|
335
|
+
number_deprecated_to_keep = D3::CONFIG.admin_auto_clean_keep_deprecated
|
336
|
+
number_deprecated_to_keep ||= 0
|
337
|
+
|
338
|
+
puts "Keeping #{number_deprecated_to_keep} deprecated packages."
|
339
|
+
|
340
|
+
# 'pop' pulls them off the end
|
341
|
+
deprecated_ids_to_keep = []
|
342
|
+
number_deprecated_to_keep.times{ deprecated_ids_to_keep << deprecated_ids.pop }
|
343
|
+
deprecated_ids_to_keep.compact!
|
344
|
+
|
345
|
+
# delete them if we should
|
346
|
+
deprecated_ids.each do |id|
|
347
|
+
next if deprecated_ids_to_keep.include? id
|
348
|
+
victim = D3::Package.new(:id => id)
|
349
|
+
victim.delete(
|
350
|
+
admin: admin,
|
351
|
+
delete_scripts: true,
|
352
|
+
keep_in_jss: false,
|
353
|
+
rwpw: D3::Admin::Auth.rw_credentials(:dist)[:password]
|
354
|
+
)
|
355
|
+
puts "Deleted deprecated package: #{victim.edition}, id:#{victim.id}, filename: #{victim.filename}."
|
356
|
+
end
|
357
|
+
|
358
|
+
#### then the skipped pkgs
|
359
|
+
|
360
|
+
skipped_ids = D3::Package.skipped_data.values.select{|sp| sp[:basename] == @basename}
|
361
|
+
skipped_ids.map!{|sp| sp[:id] }.sort!
|
362
|
+
|
363
|
+
# keep the ones newer than the just-deprecated pkg?
|
364
|
+
if D3::CONFIG.admin_auto_clean_keep_latest_pilots
|
365
|
+
deprecated_ids = D3::Package.deprecated_data(:refresh).values.select{|dp| dp[:basename] == @basename}
|
366
|
+
deprecated_ids.map!{|dp| dp[:id] }
|
367
|
+
just_deprecated = deprecated_ids.max
|
368
|
+
just_deprecated ||= 0
|
369
|
+
skipped_ids_to_keep = skipped_ids.select{|id| id > just_deprecated }
|
370
|
+
puts "Keeping most recent pilot packages as skipped."
|
371
|
+
# no, delete them all
|
372
|
+
else
|
373
|
+
skipped_ids_to_keep = []
|
374
|
+
puts "Not keeping any pilot or skipped packages."
|
375
|
+
end
|
376
|
+
|
377
|
+
# delete them if we should
|
378
|
+
skipped_ids.each do |id|
|
379
|
+
next if skipped_ids_to_keep.include? id
|
380
|
+
victim = D3::Package.new(:id => id)
|
381
|
+
victim.delete(
|
382
|
+
admin: admin,
|
383
|
+
delete_scripts: true,
|
384
|
+
keep_in_jss: false,
|
385
|
+
rwpw: D3::Admin::Auth.rw_credentials(:dist)[:password]
|
386
|
+
)
|
387
|
+
puts "Deleted skipped package: #{victim.edition}, id:#{victim.id}, filename: #{victim.filename}."
|
388
|
+
end
|
389
|
+
puts "Finished auto-clean of old packages for '#{@basename}'"
|
390
|
+
return true
|
391
|
+
end
|
392
|
+
|
393
|
+
### Delete this package from d3, possibly leaving it in the JSS
|
394
|
+
###
|
395
|
+
### @param keep_in_jss[Boolean] should we keep the JSS package around? defaults to false
|
396
|
+
###
|
397
|
+
### @param delete_scripts[Boolean] should the related scripts also be deleted?
|
398
|
+
###
|
399
|
+
### @param admin[String] who's doing this?
|
400
|
+
###
|
401
|
+
### @param rwpw[String] the read-write for the master distr. point
|
402
|
+
###
|
403
|
+
### @return [Array<String>] a textual list of scripts delted and not
|
404
|
+
### deleted because they're in use by other d3 pkgs or casper policies
|
405
|
+
### (empty if delete_scripts is false)
|
406
|
+
###
|
407
|
+
def delete (keep_in_jss: false, delete_scripts: false, admin: @admin, rwpw: nil)
|
408
|
+
|
409
|
+
unless keep_in_jss
|
410
|
+
# raise an exception if any polcies are using this pkg.
|
411
|
+
pols = policy_ids
|
412
|
+
unless pols.empty?
|
413
|
+
names = pols.map{|pid| JSS::Policy.map_all_ids_to(:name)[pid]}.join(', ')
|
414
|
+
raise JSS::UnsupportedError, "Can't delete package from JSS, used by these policies: #{names} "
|
415
|
+
end # unless pols.empty
|
416
|
+
end # unles keep in jss
|
417
|
+
|
418
|
+
# use @ admin if its defined and needed
|
419
|
+
admin ||= @admin
|
420
|
+
|
421
|
+
# if delete_scripts
|
422
|
+
script_actions = delete_scripts ? delete_pkg_scripts : []
|
423
|
+
|
424
|
+
# delete it from the pakcages table
|
425
|
+
stmt = JSS::DB_CNX.db.prepare "DELETE FROM #{P_TABLE[:table_name]} WHERE #{P_FIELDS[:id][:field_name]} = '#{@id}'"
|
426
|
+
stmt_result = stmt.execute
|
427
|
+
|
428
|
+
@status = :deleted
|
429
|
+
|
430
|
+
# delete it from the JSS unless asked not to
|
431
|
+
unless keep_in_jss
|
432
|
+
super delete_file: true, rw_pw: rwpw, unmount: false
|
433
|
+
end
|
434
|
+
|
435
|
+
# while we're writing to the db, mark any missing packages as missing
|
436
|
+
mark_missing_packages
|
437
|
+
|
438
|
+
# update our knowledge of the world
|
439
|
+
D3::Package.package_data :refresh
|
440
|
+
|
441
|
+
return script_actions
|
442
|
+
end
|
443
|
+
|
444
|
+
### Learn the apple package id's installed by this pkg by
|
445
|
+
### querying the package on the current dist. point. This is primarily used
|
446
|
+
### for importing or repairing packages already on the server.
|
447
|
+
###
|
448
|
+
### When adding new packages, the {#upload_master_file} method will query the
|
449
|
+
### data before uploading the file.
|
450
|
+
###
|
451
|
+
###
|
452
|
+
### @param dist_pw[String] the read-only or read-write password for the dist. point for this machine
|
453
|
+
###
|
454
|
+
### @param unmount[Boolean] should the dist.point be unmounted when done?
|
455
|
+
###
|
456
|
+
### @return [void]
|
457
|
+
###
|
458
|
+
def update_apple_receipt_data(dist_pw, unmount = true)
|
459
|
+
return nil if @filename.end_with? ".dmg"
|
460
|
+
raise JSS::NoSuchItemError, "Please create this package on the server before updating the Apple receipt data" unless @in_jss
|
461
|
+
|
462
|
+
mdp = JSS::DistributionPoint.my_distribution_point
|
463
|
+
raise JSS::MissingDataError, "Missing :dist_pw for distrib. point '#{mdp.name}'" unless dist_pw
|
464
|
+
|
465
|
+
# try the passwd both with ro and rw
|
466
|
+
begin
|
467
|
+
mnt_path = mdp.mount(dist_pw, :ro)
|
468
|
+
rescue JSS::InvalidDataError
|
469
|
+
mnt_path = mdp.mount(dist_pw, :rw)
|
470
|
+
end
|
471
|
+
|
472
|
+
pkg_path = mnt_path + JSS::Package::DIST_POINT_PKGS_FOLDER + @filename
|
473
|
+
raise JSS::NoSuchItemError, "Package file #{@filename} doesn't exist on the current dist. point." unless pkg_path.exist?
|
474
|
+
|
475
|
+
pkg_to_query = pkg_path
|
476
|
+
|
477
|
+
# do we need to unzip a bundle pkg?
|
478
|
+
if @filename.end_with? ".zip"
|
479
|
+
work_dir = Pathname.new Dir.mktmpdir
|
480
|
+
unless system "/usr/bin/unzip -qq -o -d #{Shellwords.escape work_dir.to_s} #{Shellwords.escape pkg_path.to_s}"
|
481
|
+
raise RuntimeError, "Failed to unzip bundle pkg #{@filename}"
|
482
|
+
end #system
|
483
|
+
pkg_to_query = work_dir + @filename.sub(/\.zip$/, '')
|
484
|
+
cleanup_work_dir = true
|
485
|
+
end # if @filename.end_with? ".zip"
|
486
|
+
|
487
|
+
@apple_receipt_data = D3::Package.receipt_data_from_pkg(pkg_to_query)
|
488
|
+
@need_to_update_d3 = true unless @initializing
|
489
|
+
work_dir.rmtree if cleanup_work_dir
|
490
|
+
mdp.unmount if unmount
|
491
|
+
end # update_apple_receipt_data
|
492
|
+
|
493
|
+
|
494
|
+
### Mark missing packages as so on the server
|
495
|
+
###
|
496
|
+
### This should run any time we write to the d3_packages table
|
497
|
+
###
|
498
|
+
### @return [void]
|
499
|
+
def mark_missing_packages
|
500
|
+
missing_ids = self.class.missing_data.keys
|
501
|
+
unless missing_ids.empty?
|
502
|
+
q = <<-ENDUPDATE
|
503
|
+
UPDATE #{P_TABLE[:table_name]}
|
504
|
+
SET #{ P_FIELDS[:status][:field_name]} = '#{P_FIELDS[:status][:to_sql].call(:missing)}'
|
505
|
+
WHERE #{P_FIELDS[:id][:field_name]} IN (#{missing_ids.join(',')})
|
506
|
+
ENDUPDATE
|
507
|
+
stmt = JSS::DB_CNX.db.prepare q
|
508
|
+
stmt_result = stmt.execute
|
509
|
+
end # unless empty
|
510
|
+
end # mark missing pkgs
|
511
|
+
|
512
|
+
### Upload a locally-readable file to the master distribution point.
|
513
|
+
### If the file is a directory (like a bundle .pk/.mpkg) it will be zipped before
|
514
|
+
### uploading and the @filename will be adjusted accordingly
|
515
|
+
###
|
516
|
+
### If you'll be uploading several files you can specify unmount as false, and do it manually when all
|
517
|
+
### are finished with JSS::DistributionPoint.master_distribution_point.unmount
|
518
|
+
###
|
519
|
+
### This method is mostly performed by the parent class, see {JSS::Package.upload_master_file}.
|
520
|
+
### Before calling super, this method populates @apple_receipt_data with info
|
521
|
+
### from the local file.
|
522
|
+
###
|
523
|
+
### @param local_file_path[String,Pathname] the local path to the file to be uploaded
|
524
|
+
###
|
525
|
+
### @param rw_pw[String,Symbol] the password for the read/write account on the master Distribution Point,
|
526
|
+
### or :prompt, or :stdin# where # is the line of stdin containing the password See {JSS::DistributionPoint#mount}
|
527
|
+
###
|
528
|
+
### @param unmount[Boolean] whether or not ot unount the distribution point when finished.
|
529
|
+
###
|
530
|
+
### @return [void]
|
531
|
+
###
|
532
|
+
def upload_master_file (local_file_path, rw_pw, unmount = true)
|
533
|
+
raise JSS::NoSuchItemError, "Please create this package in d3 before uploading it." unless @in_d3
|
534
|
+
if local_file_path.to_s =~ PKG_RE
|
535
|
+
@apple_receipt_data = D3::Package.receipt_data_from_pkg(local_file_path)
|
536
|
+
@need_to_update_d3 = true
|
537
|
+
self.save
|
538
|
+
end # if local_file_path.to_s =~ PKG_RE
|
539
|
+
|
540
|
+
super
|
541
|
+
end
|
542
|
+
|
543
|
+
### Create, or re-create, the BOM index records for this
|
544
|
+
### Package in the JSS Database.
|
545
|
+
###
|
546
|
+
### This is the equivalent of clicking the "index" button
|
547
|
+
### in Casper Admin.app, and is necessary for Casper to
|
548
|
+
### be able to uninstall items. It can only happen after the
|
549
|
+
### item has already been saved to the JSS and has an
|
550
|
+
### id in the database.
|
551
|
+
###
|
552
|
+
### @param args[Hash] The arguments for the method
|
553
|
+
###
|
554
|
+
### @option args :local_filepath[String,Pathname] the path
|
555
|
+
### to a local copy of the installer pkg/dmg
|
556
|
+
###
|
557
|
+
### @option args :ro_pw[String] the read-only password
|
558
|
+
### for the AFP/SMB share of the master distribution point.
|
559
|
+
###
|
560
|
+
### @return [void]
|
561
|
+
###
|
562
|
+
def mk_index(args = {})
|
563
|
+
|
564
|
+
raise JSS::NoSuchItemError, "Please create this package in the JSS before indexing it." unless @in_jss
|
565
|
+
raise JSS::InvalidConnectionError, "Indexing a package requires a database connection. Use JSS::DB_CNX.connect" unless JSS::DB_CNX.connected?
|
566
|
+
|
567
|
+
if args[:local_filepath]
|
568
|
+
file_to_index = Pathname.new(args[:local_filepath])
|
569
|
+
|
570
|
+
elsif args[:ro_pw]
|
571
|
+
mdp = JSS::DistributionPoint.master_distribution_point
|
572
|
+
file_to_index = mdp.mount(args[:ro_pw], :ro) +"#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
|
573
|
+
if file_to_index.to_s.end_with? ".zip"
|
574
|
+
tmpdir = Pathname.new "/tmp/jss-tmp-#{$$}"
|
575
|
+
system "/usr/bin/unzip '#{thing_to_index}' -d '#{tmpdir}'"
|
576
|
+
file_to_index = tmpdir + file_to_index.basename.to_s.sub(/.zip$/, '')
|
577
|
+
end
|
578
|
+
|
579
|
+
else
|
580
|
+
raise JSS::InvalidDataError, "Need a :local_filepath or :ro_pw"
|
581
|
+
end
|
582
|
+
|
583
|
+
# get the index data
|
584
|
+
# is it an (m)pkg?
|
585
|
+
if file_to_index.to_s =~ /\.m?pkg$/
|
586
|
+
bom_lines = ''
|
587
|
+
|
588
|
+
# if the thing is a pkg bundle, find and read all the bom files it contains
|
589
|
+
if (file_to_index + "Contents").directory?
|
590
|
+
(file_to_index + "Contents").find do |path|
|
591
|
+
bom_lines += `echo; /usr/bin/lsbom -p fugTsMc '#{path}'` if path.to_s =~ /\.bom$/
|
592
|
+
end # do path
|
593
|
+
|
594
|
+
else
|
595
|
+
# else its a flat file - so do it using pkgutil
|
596
|
+
bom_files = `/usr/sbin/pkgutil --bom '#{file_to_index}'`
|
597
|
+
bom_files.split("\n").each do |file|
|
598
|
+
bom_lines += `/usr/bin/lsbom -p fugTsMc '#{file}'`
|
599
|
+
end
|
600
|
+
end # .directory?
|
601
|
+
|
602
|
+
elsif file_to_index.to_s =~ /\.dmg$/
|
603
|
+
|
604
|
+
# if its a .dmg, mount it, make a tmp bom file, and read that
|
605
|
+
mnt_line = `/usr/bin/hdiutil attach -readonly -nobrowse -noautoopen -owners on '#{file_to_index}'`.lines.last
|
606
|
+
mnt_point = Pathname.new mnt_line.split("\t").last.chomp
|
607
|
+
raise FileServiceError, "There was a problem mounting the image #{file_to_index}" unless mnt_point.mountpoint?
|
608
|
+
|
609
|
+
tmp_bom = "/tmp/#{@filename}.#{$$}.bom"
|
610
|
+
system "/usr/bin/mkbom '#{mnt_point}' '#{tmp_bom}'"
|
611
|
+
bom_lines = `/usr/bin/lsbom -p fugTsMc '#{tmp_bom}'`
|
612
|
+
|
613
|
+
system "/usr/bin/hdiutil detach '#{mnt_point}'"
|
614
|
+
system "rm -rf '#{tmp_bom}'"
|
615
|
+
|
616
|
+
else
|
617
|
+
raise JSS::InvalidDataError, "#{@filename} is doesn't looks like a .pkg or .dmg. Try Casper Admin to index it."
|
618
|
+
end # if filename .pkg
|
619
|
+
|
620
|
+
# If there are no bomlines (perhaps a payloadless pkg?) just return
|
621
|
+
return true if bom_lines.empty?
|
622
|
+
|
623
|
+
# split the bom lines
|
624
|
+
index_records = bom_lines.split "\n"
|
625
|
+
|
626
|
+
# reset our lists of files
|
627
|
+
@index = []
|
628
|
+
@file_list = []
|
629
|
+
|
630
|
+
# the start of the SQL insert statement
|
631
|
+
insert_stmt = "INSERT INTO package_contents (package_id,file,owner_name,group_name,modification_date,size,mode,checksum) VALUES"
|
632
|
+
insert_vals = []
|
633
|
+
|
634
|
+
# loop through the bom data and make a new record for each line
|
635
|
+
index_records.each do |line|
|
636
|
+
next if line.empty?
|
637
|
+
|
638
|
+
#break out the data for each item
|
639
|
+
(path,uid,gid,modtime,size,mode,checksum) = line.split "\t"
|
640
|
+
|
641
|
+
# if the path is just a dot (usually the first one)
|
642
|
+
# make it a /
|
643
|
+
if path == "."
|
644
|
+
clean_path = "/"
|
645
|
+
elsif path.start_with? "."
|
646
|
+
clean_path = path.sub ".", ""
|
647
|
+
else
|
648
|
+
clean_path = path
|
649
|
+
end
|
650
|
+
|
651
|
+
# rebuild our local lists of files
|
652
|
+
@index << { 'path' => clean_path,
|
653
|
+
'uid' => uid,
|
654
|
+
'gif' => gid,
|
655
|
+
'modtime' => modtime,
|
656
|
+
'size' => size,
|
657
|
+
'mode' => mode }
|
658
|
+
|
659
|
+
@file_list << clean_path unless mode.start_with? "d"
|
660
|
+
|
661
|
+
# JSS stores modtime as string w/o the weekday
|
662
|
+
modtime.gsub!(/^(Sun|Mon||Tue|Wed|Thu|Fri|Sat) /, '') if defined? modtime
|
663
|
+
|
664
|
+
insert_vals << "('#{@id}','#{Mysql.quote clean_path}','#{uid}','#{gid}','#{modtime}','#{size}','#{mode}','#{checksum}')"
|
665
|
+
|
666
|
+
end # do line
|
667
|
+
|
668
|
+
# first delete any existing index records for this pkg
|
669
|
+
stmt = JSS::DB_CNX.db.prepare "DELETE FROM #{PKG_CONTENTS_TABLE} WHERE package_id = #{@id}"
|
670
|
+
stmt_result = stmt.execute
|
671
|
+
|
672
|
+
# now insert the new values
|
673
|
+
stmt = JSS::DB_CNX.db.prepare(insert_stmt + " " + insert_vals.join(','))
|
674
|
+
stmt_result = stmt.execute
|
675
|
+
|
676
|
+
# while we're writing to the db, mark any missing packages as missing
|
677
|
+
mark_missing_packages
|
678
|
+
|
679
|
+
return true
|
680
|
+
end #mk_index
|
681
|
+
|
682
|
+
end # class Package
|
683
|
+
end # module D3
|