mirror of
https://github.com/munki/munki.git
synced 2026-04-23 21:40:25 -05:00
Significant changes to how Apple update items are matched to metadata; new function in updatecheck specifically created to handle collection of catalog data by appleupdates; new function created for applying metadata
Of note, 'copyUpdateMetadata' function uses a list of whitelisted metadata keys as well as appropriate logic to determine if a particular 'RestartAction' should be applied.
This commit is contained in:
@@ -830,33 +830,22 @@ class AppleUpdates(object):
|
||||
Returns:
|
||||
Boolean. True if apple updates was updated, False otherwise.
|
||||
"""
|
||||
metadata_exclusions = ['catalogs',
|
||||
'installed_size',
|
||||
'installer_type',
|
||||
'name',
|
||||
'version',
|
||||
'version_to_install']
|
||||
apple_updates = self.GetSoftwareUpdateInfo()
|
||||
if apple_updates:
|
||||
# Process update metadata only if AppleSoftwareUpdatesOnly is false
|
||||
if not munkicommon.pref('AppleSoftwareUpdatesOnly'):
|
||||
self.apple_md = updatecheck.check(
|
||||
self.client_id, apple_update_md_only=True)
|
||||
for update in apple_updates:
|
||||
matching_items = self.getAllItemsWithProductKey(
|
||||
update['productKey'])
|
||||
if matching_items:
|
||||
update_metadata = matching_items[0]
|
||||
for key in update_metadata:
|
||||
# Don't overwrite items in exclusions list
|
||||
if key in metadata_exclusions:
|
||||
continue
|
||||
else:
|
||||
# Apply non-empty metadata
|
||||
if update_metadata[key]:
|
||||
munkicommon.display_debug2(
|
||||
'Applying %s to %s...' % (key, update['display_name']))
|
||||
update[key] = update_metadata[key]
|
||||
# Gather available apple_update_metadata
|
||||
cataloglist, self.apple_md = \
|
||||
updatecheck.getAppleUpdateMetaData(self.client_id)
|
||||
for item in apple_updates:
|
||||
# Find matching metadata item
|
||||
metadata_item = updatecheck.getItemDetail(
|
||||
item['productKey'], cataloglist,
|
||||
apple_update_metadata=True)
|
||||
if metadata_item:
|
||||
munkicommon.display_debug1(
|
||||
'Processing metadata for %s, %s...'
|
||||
% (item['productKey'], item['display_name']))
|
||||
self.copyUpdateMetadata(item, metadata_item)
|
||||
plist = {'AppleUpdates': apple_updates}
|
||||
FoundationPlist.writePlist(plist, self.apple_updates_plist)
|
||||
return True
|
||||
@@ -1327,43 +1316,43 @@ class AppleUpdates(object):
|
||||
return updates
|
||||
|
||||
|
||||
def getAllItemsWithProductKey(self, productKey):
|
||||
"""Searches apple_md for all items matching a given Apple productKey
|
||||
|
||||
Returns:
|
||||
list of pkginfo items; sorted with newest version first. No precedence
|
||||
is given to catalog order.
|
||||
def copyUpdateMetadata(self, item, metadata):
|
||||
"""Applies metadata to Apple update item restricted
|
||||
to keys contained in 'metadata_to_copy'.
|
||||
"""
|
||||
def compare_item_versions(a, b):
|
||||
"""Internal comparison function for use with sorting"""
|
||||
return cmp(munkicommon.MunkiLooseVersion(b['version']),
|
||||
munkicommon.MunkiLooseVersion(a['version']))
|
||||
metadata_to_copy = ['blocking_applications',
|
||||
'description',
|
||||
'display_name',
|
||||
'force_install_after_date',
|
||||
'unattended_install',
|
||||
'RestartAction']
|
||||
|
||||
itemlist = []
|
||||
# Mapping of supported RestartActions to
|
||||
# equal or greater auxiliary actions
|
||||
RestartActions = {
|
||||
'RequireRestart' : ['RequireRestart', 'RecommendRestart'],
|
||||
'RecommendRestart': ['RequireRestart', 'RecommendRestart'],
|
||||
'RequireLogout' : ['RequireRestart', 'RecommendRestart',
|
||||
'RequireLogout'],
|
||||
'None' : ['RequireRestart', 'RecommendRestart',
|
||||
'RequireLogout']
|
||||
}
|
||||
|
||||
munkicommon.display_debug1(
|
||||
'Looking for metadata matching: %s...' % productKey)
|
||||
# is productKey in the catalog name table?
|
||||
for catalogname in self.apple_md.keys():
|
||||
if productKey in self.apple_md[catalogname]['named']:
|
||||
versionsmatchingproductKey = \
|
||||
self.apple_md[catalogname]['named'][productKey]
|
||||
for vers in versionsmatchingproductKey.keys():
|
||||
if vers != 'latest':
|
||||
indexlist = \
|
||||
self.apple_md[catalogname]['named'][productKey][vers]
|
||||
for index in indexlist:
|
||||
thisitem = self.apple_md[catalogname]['items'][index]
|
||||
if not thisitem in itemlist:
|
||||
munkicommon.display_debug1(
|
||||
'Adding item %s, version %s from catalog %s...' %
|
||||
(productKey, thisitem['version'], catalogname))
|
||||
itemlist.append(thisitem)
|
||||
|
||||
if itemlist:
|
||||
# sort so latest version is first
|
||||
itemlist.sort(compare_item_versions)
|
||||
return itemlist
|
||||
for key in metadata:
|
||||
# Apply 'white-listed', non-empty metadata keys
|
||||
if key in metadata_to_copy and metadata[key]:
|
||||
if key == 'RestartAction':
|
||||
# Ensure that a heavier weighted 'RestartAction' is not
|
||||
# overriden by one supplied in metadata
|
||||
if metadata[key] not in RestartActions.get(item.get(key, 'None')):
|
||||
munkicommon.display_debug2(
|
||||
'\tSkipping %s \'%s\', \'%s\' is preferred.'
|
||||
% (key, metadata[key], item[key]))
|
||||
continue
|
||||
munkicommon.display_debug2('\tApplying %s...' % key)
|
||||
item[key] = metadata[key]
|
||||
return item
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -44,8 +44,6 @@ from Foundation import NSDate, NSPredicate, NSTimeZone
|
||||
# This many hours before a force install deadline, start notifying the user.
|
||||
FORCE_INSTALL_WARNING_HOURS = 4
|
||||
|
||||
# Flag denoting a check from appleupdates for apple_update_metadata
|
||||
APPLE_UPDATE_MD_ONLY = False
|
||||
|
||||
def makeCatalogDB(catalogitems):
|
||||
"""Takes an array of catalog items and builds some indexes so we can
|
||||
@@ -53,12 +51,6 @@ def makeCatalogDB(catalogitems):
|
||||
name_table = {}
|
||||
pkgid_table = {}
|
||||
|
||||
# Filter catalogitems so that they only contain apple_update_metadata items
|
||||
if APPLE_UPDATE_MD_ONLY:
|
||||
catalogitems = [item for item in catalogitems
|
||||
if item.get('installer_type') ==
|
||||
'apple_update_metadata']
|
||||
|
||||
itemindex = -1
|
||||
for item in catalogitems:
|
||||
itemindex = itemindex + 1
|
||||
@@ -952,7 +944,7 @@ def trimVersionString(version_string):
|
||||
return '.'.join(version_parts)
|
||||
|
||||
|
||||
def getItemDetail(name, cataloglist, vers=''):
|
||||
def getItemDetail(name, cataloglist, vers='', apple_update_metadata=False):
|
||||
"""Searches the catalogs in list for an item matching the given name.
|
||||
|
||||
If no version is supplied, but the version is appended to the name
|
||||
@@ -965,14 +957,17 @@ def getItemDetail(name, cataloglist, vers=''):
|
||||
return cmp(munkicommon.MunkiLooseVersion(b),
|
||||
munkicommon.MunkiLooseVersion(a))
|
||||
|
||||
(name, includedversion) = nameAndVersion(name)
|
||||
if vers == '':
|
||||
if includedversion:
|
||||
vers = includedversion
|
||||
if vers:
|
||||
vers = trimVersionString(vers)
|
||||
else:
|
||||
if apple_update_metadata:
|
||||
vers = 'latest'
|
||||
else:
|
||||
(name, includedversion) = nameAndVersion(name)
|
||||
if vers == '':
|
||||
if includedversion:
|
||||
vers = includedversion
|
||||
if vers:
|
||||
vers = trimVersionString(vers)
|
||||
else:
|
||||
vers = 'latest'
|
||||
|
||||
munkicommon.display_debug1('Looking for detail for: %s, version %s...' %
|
||||
(name, vers))
|
||||
@@ -2616,16 +2611,14 @@ def getDownloadCachePath(destinationpathprefix, url):
|
||||
return os.path.join(
|
||||
destinationpathprefix, getInstallerItemBasename(url))
|
||||
|
||||
|
||||
MACHINE = {}
|
||||
CONDITIONS = {}
|
||||
def check(client_id='', localmanifestpath=None, apple_update_md_only=False):
|
||||
def check(client_id='', localmanifestpath=None):
|
||||
"""Checks for available new or updated managed software, downloading
|
||||
installer items if needed. Returns 1 if there are available updates,
|
||||
0 if there are no available updates, and -1 if there were errors."""
|
||||
|
||||
global APPLE_UPDATE_MD_ONLY
|
||||
APPLE_UPDATE_MD_ONLY = apple_update_md_only
|
||||
|
||||
global MACHINE
|
||||
munkicommon.getMachineFacts()
|
||||
MACHINE = munkicommon.getMachineFacts()
|
||||
@@ -2639,6 +2632,9 @@ def check(client_id='', localmanifestpath=None, apple_update_md_only=False):
|
||||
if munkicommon.munkistatusoutput:
|
||||
munkistatus.activate()
|
||||
|
||||
munkicommon.log('### Beginning managed software check ###')
|
||||
munkicommon.display_status_major('Checking for available updates...')
|
||||
|
||||
if localmanifestpath:
|
||||
mainmanifestpath = localmanifestpath
|
||||
else:
|
||||
@@ -2648,14 +2644,6 @@ def check(client_id='', localmanifestpath=None, apple_update_md_only=False):
|
||||
|
||||
installinfo = {}
|
||||
|
||||
if APPLE_UPDATE_MD_ONLY:
|
||||
munkicommon.display_detail('**Checking for Apple Update Metadata**')
|
||||
print munkicommon.pref('AppleSoftwareUpdatesOnly')
|
||||
return getAppleUpdateMetaData(mainmanifestpath)
|
||||
|
||||
munkicommon.log('### Beginning managed software check ###')
|
||||
munkicommon.display_status_major('Checking for available updates...')
|
||||
|
||||
if mainmanifestpath:
|
||||
# initialize our installinfo record
|
||||
installinfo['processed_installs'] = []
|
||||
@@ -3140,15 +3128,19 @@ def getResourceIfChangedAtomically(url,
|
||||
verify=verify)
|
||||
|
||||
|
||||
def getAppleUpdateMetaData(manifest):
|
||||
global CATALOG
|
||||
CATALOG = {}
|
||||
def getAppleUpdateMetaData(client_id=''):
|
||||
"""Used by appleupdates in determining
|
||||
metadata to apply to available Apple updates"""
|
||||
|
||||
cataloglist = []
|
||||
munkicommon.display_detail('**Checking for Apple Update Metadata**')
|
||||
manifest = getPrimaryManifest(client_id)
|
||||
if manifest:
|
||||
manifestdata = getManifestData(manifest)
|
||||
cataloglist = manifestdata.get('catalogs')
|
||||
if cataloglist:
|
||||
getCatalogs(cataloglist)
|
||||
return CATALOG
|
||||
return cataloglist, CATALOG
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
Reference in New Issue
Block a user