mirror of
https://github.com/munki/munki.git
synced 2026-05-01 09:49:31 -05:00
Begin reorg of repo plugins code
This commit is contained in:
+15
-24
@@ -30,11 +30,11 @@ from optparse import OptionParser
|
||||
# our libs
|
||||
from munkilib import dmgutils
|
||||
from munkilib import iconutils
|
||||
from munkilib import munkirepo
|
||||
from munkilib import osutils
|
||||
from munkilib import pkgutils
|
||||
|
||||
from munkilib import FoundationPlist
|
||||
from munkilib import Repo
|
||||
|
||||
# PyLint cannot properly find names inside Cocoa libraries, so issues bogus
|
||||
# No name 'Foo' in module 'Bar' warnings. Disable them.
|
||||
@@ -45,7 +45,8 @@ from Foundation import CFPreferencesCopyAppValue
|
||||
def generate_png_from_dmg_item(install_item, repo):
|
||||
'''Generate a PNG from a disk image containing an application'''
|
||||
dmgpath = repo.join('pkgs', install_item['installer_item_location'])
|
||||
mountpoints = dmgutils.mountdmg(dmgpath)
|
||||
dmg_handle = repo.open(dmgpath, 'r')
|
||||
mountpoints = dmgutils.mountdmg(dmg_handle.local_path)
|
||||
if mountpoints:
|
||||
mountpoint = mountpoints[0]
|
||||
apps = [item for item in install_item.get('items_to_copy', [])
|
||||
@@ -78,7 +79,8 @@ def generate_pngs_from_pkg(install_item, repo):
|
||||
item_path = repo.join(u'pkgs', install_item['installer_item_location'])
|
||||
if pkgutils.hasValidDiskImageExt(item_path):
|
||||
dmg_path = item_path
|
||||
mountpoints = dmgutils.mountdmg(dmg_path)
|
||||
dmg_handle = repo.open(dmg_path, 'r')
|
||||
mountpoints = dmgutils.mountdmg(dmg_handle.local_path)
|
||||
if mountpoints:
|
||||
mountpoint = mountpoints[0]
|
||||
if install_item.get('package_path'):
|
||||
@@ -198,7 +200,7 @@ def pref(prefname):
|
||||
|
||||
def main():
|
||||
'''Main'''
|
||||
usage = "usage: %prog [options] [/path/to/repo_root] [repo_url] [plugin]"
|
||||
usage = "usage: %prog [options] [/path/to/repo_root]"
|
||||
parser = OptionParser(usage=usage)
|
||||
parser.add_option(
|
||||
'--force', '-f', action='store_true', dest='force',
|
||||
@@ -206,13 +208,14 @@ def main():
|
||||
parser.add_option(
|
||||
'--item', '-i', action='append', type='string', dest='items',
|
||||
help='Only run for given pkginfo item name(s).')
|
||||
parser.add_option('--plugin', '--plugin', default=pref('plugin'),
|
||||
help='Optional. Custom plugin to connect to repo.')
|
||||
parser.add_option('--repo_url', '--repo-url', default=pref('repo_url'),
|
||||
help='Optional repo fileshare URL used by repo plugin.')
|
||||
parser.set_defaults(force=False)
|
||||
options, arguments = parser.parse_args()
|
||||
|
||||
# Make sure we have a path to work with
|
||||
repo_path = None
|
||||
repo_url = None
|
||||
plugin = None
|
||||
if len(arguments) == 0:
|
||||
repo_path = pref('repo_path')
|
||||
if not repo_path:
|
||||
@@ -223,26 +226,14 @@ def main():
|
||||
else:
|
||||
repo_path = arguments[0].rstrip("/")
|
||||
|
||||
# Make sure the repo path exists
|
||||
if len(arguments) > 1:
|
||||
repo_url = arguments[1].rstrip("/")
|
||||
else:
|
||||
repo_url = pref('repo_url')
|
||||
if not repo_url:
|
||||
print_err_utf8("Need to specify a URL for the repo!")
|
||||
exit(-1)
|
||||
else:
|
||||
print_utf8("Using repo url: %s" % repo_url)
|
||||
|
||||
if len(arguments) > 2:
|
||||
plugin = arguments[2]
|
||||
else:
|
||||
plugin = pref('plugin')
|
||||
if options.plugin is None:
|
||||
options.plugin = 'FileRepo'
|
||||
|
||||
# Make sure the repo exists
|
||||
repo = Repo.Open(repo_path, repo_url, plugin)
|
||||
repo = munkirepo.connect(repo_path, options.repo_url, options.plugin)
|
||||
if not repo.available():
|
||||
print_err_utf8("Repo root path %s doesn't exist!" % repo_path)
|
||||
print_err_utf8('Could not connect to munki repo. Check the '
|
||||
'configuration and try again.')
|
||||
exit(-1)
|
||||
|
||||
# generate icons!
|
||||
|
||||
@@ -31,7 +31,8 @@ import sys
|
||||
import os
|
||||
import optparse
|
||||
import hashlib
|
||||
from munkilib import Repo
|
||||
|
||||
from munkilib import munkirepo
|
||||
|
||||
try:
|
||||
from munkilib import FoundationPlist as plistlib
|
||||
@@ -381,8 +382,8 @@ def pref(prefname):
|
||||
|
||||
|
||||
PREFSNAME = 'com.googlecode.munki.munkiimport.plist'
|
||||
PREFSPATH = os.path.expanduser(os.path.join('~/Library/Preferences',
|
||||
PREFSNAME))
|
||||
PREFSPATH = os.path.expanduser(os.path.join('~/Library/Preferences', PREFSNAME))
|
||||
|
||||
def main():
|
||||
'''Main'''
|
||||
usage = "usage: %prog [options] [/path/to/repo_root]"
|
||||
@@ -396,7 +397,7 @@ def main():
|
||||
'over the default repo_url specified via '
|
||||
'--configure.')
|
||||
parser.add_option('--plugin', '--plugin', default=pref('plugin'),
|
||||
help='Specify a custom plugin to run for munkiimport Repo.')
|
||||
help='Specify a custom plugin to connect to repo.')
|
||||
parser.set_defaults(force=False)
|
||||
options, arguments = parser.parse_args()
|
||||
|
||||
@@ -416,11 +417,18 @@ def main():
|
||||
else:
|
||||
repopath = arguments[0].rstrip("/")
|
||||
|
||||
# Make sure the repo path exists
|
||||
repo = Repo.Open(repopath, options.repo_url, options.plugin)
|
||||
if not repo.exists():
|
||||
print_err_utf8("Repo root path %s doesn't exist!" % repopath)
|
||||
if options.plugin is None:
|
||||
options.plugin = 'FileRepo'
|
||||
|
||||
# Make sure the repo exists
|
||||
repo = munkirepo.connect(repopath, options.repo_url, options.plugin)
|
||||
if not repo.available():
|
||||
print_err_utf8('Could not connect to munki repo. Check the '
|
||||
'configuration and try again.')
|
||||
exit(-1)
|
||||
#if not repo.exists():
|
||||
# print_err_utf8("Repo root path %s doesn't exist!" % repopath)
|
||||
# exit(-1)
|
||||
|
||||
# Make the catalogs
|
||||
makecatalogs(repo, options)
|
||||
|
||||
@@ -42,10 +42,11 @@ from munkilib import info
|
||||
from munkilib import display
|
||||
from munkilib import dmgutils
|
||||
from munkilib import munkihash
|
||||
from munkilib import munkirepo
|
||||
from munkilib import osutils
|
||||
from munkilib import pkgutils
|
||||
from munkilib import FoundationPlist
|
||||
from munkilib import Repo
|
||||
|
||||
|
||||
# PyLint cannot properly find names inside Cocoa libraries, so issues bogus
|
||||
# No name 'Foo' in module 'Bar' warnings. Disable them.
|
||||
@@ -902,7 +903,7 @@ def main():
|
||||
'over the default repo_url specified via '
|
||||
'--configure.')
|
||||
parser.add_option('--plugin', '--plugin', default='',
|
||||
help='Optional custom plugin to run for munkiimport Repo.')
|
||||
help='Specify a custom plugin to connect to repo.')
|
||||
parser.add_option('--icon_path', '--icon-path', default='', type='string',
|
||||
help='Path to an icon file for the package. '
|
||||
'Will overwrite an existing icon.')
|
||||
@@ -927,7 +928,7 @@ def main():
|
||||
#default is what user put in munkiimport --configure
|
||||
REPO_PATH = pref('repo_path')
|
||||
REPO_URL = pref('repo_url')
|
||||
REPO_PLUGIN = pref('plugin')
|
||||
REPO_PLUGIN = pref('plugin') or 'FileRepo'
|
||||
|
||||
if options.repo_path:
|
||||
if not os.path.exists(options.repo_path) and not options.repo_url:
|
||||
@@ -999,7 +1000,7 @@ def main():
|
||||
'tool, or provide with --repo-path')
|
||||
exit(-1)
|
||||
|
||||
repo = Repo.Open(REPO_PATH, REPO_URL, REPO_PLUGIN)
|
||||
repo = munkirepo.connect(REPO_PATH, REPO_URL, REPO_PLUGIN)
|
||||
if not repo.available():
|
||||
print >> sys.stderr, ('Could not connect to munki repo. Check the '
|
||||
'configuration and try again.')
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2016 Centrify Corporation.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""
|
||||
Repo
|
||||
Created by Centrify Corporation 2016-06-02.
|
||||
Interface for accessing a repo.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import imp
|
||||
import os
|
||||
|
||||
def Open(path, url, plugin):
|
||||
#looks for installtion path for munki
|
||||
# first look for a plugin in the same dir as us in munkilib/plugins
|
||||
munkilib_path = os.path.dirname(os.path.abspath(__file__))
|
||||
munkilib_path = os.path.join(munkilib_path, 'plugins')
|
||||
if not os.path.exists(munkilib_path):
|
||||
# didn't find it; assume the default install path
|
||||
command = "munkiimport"
|
||||
commandPath = os.popen("/usr/bin/which %s" % command).read().strip()
|
||||
commandPath = os.path.split(commandPath)
|
||||
munkilib_path = commandPath[0]
|
||||
#use default munki location if munki installation path is not found
|
||||
if munkilib_path == None or munkilib_path == "":
|
||||
munkilib_path = '/usr/local/munki/munkilib/plugins'
|
||||
else:
|
||||
munkilib_path = munkilib_path + '/munkilib/plugins'
|
||||
#looks for plugin in /usr/local/munki/munkilib/plugins (installation of munki)
|
||||
if plugin == None or plugin == "":
|
||||
#default is FileRepo
|
||||
plugin = 'FileRepo'
|
||||
module = imp.load_source(plugin, munkilib_path + "/" + plugin + ".py")
|
||||
import_class = getattr(module, plugin)
|
||||
parent = import_class
|
||||
|
||||
class Repo(parent):
|
||||
mounted = False
|
||||
|
||||
def available(self):
|
||||
#if path does not exist, mount to local filesystem
|
||||
if not self.exists():
|
||||
retcode = self.mount()
|
||||
if retcode == 0:
|
||||
self.mounted = True
|
||||
#if path still doesn't exist, then cannot find munki_repo
|
||||
if not self.exists():
|
||||
print >> sys.stderr, "repo is missing"
|
||||
return False
|
||||
#checks if all subdirectories are there
|
||||
for subdir in ['catalogs', 'manifests', 'pkgs', 'pkgsinfo']:
|
||||
if not self.exists(subdir):
|
||||
print >> sys.stderr, "repo is missing %s" % subdir
|
||||
return False
|
||||
# if we get this far, the repo path looks OK
|
||||
return True
|
||||
|
||||
return Repo(path, url)
|
||||
Regular → Executable
+38
-39
@@ -22,13 +22,15 @@ Implementation for accessing a repo via direct file access, including
|
||||
a remote repo mounted via AFP, SMB, or NFS.
|
||||
"""
|
||||
|
||||
from munkilib.munkicommon import listdir
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import objc
|
||||
import glob
|
||||
|
||||
from munkilib.munkicommon import listdir
|
||||
from munkilib.munkirepo import Repo
|
||||
|
||||
# NetFS share mounting code borrowed and liberally adapted from Michael Lynn's
|
||||
# work here: https://gist.github.com/pudquick/1362a8908be01e23041d
|
||||
try:
|
||||
@@ -126,16 +128,13 @@ if NETFSMOUNTURLSYNC_AVAILABLE:
|
||||
password = getpass.getpass()
|
||||
mount_share_with_credentials(share_url, username, password)
|
||||
|
||||
class FileRepo(object):
|
||||
class FileRepo(Repo):
|
||||
WE_MOUNTED_THE_REPO = False
|
||||
'''Repo implementation that access a local or locally-mounted repo.'''
|
||||
def __init__(self, path, url):
|
||||
self.path = path
|
||||
self.url = url
|
||||
|
||||
def exists(self, subdir = None):
|
||||
def exists(self, subdir=None):
|
||||
'''Returns true if the specified path exists in the repo'''
|
||||
full_path = self.path
|
||||
full_path = self.root
|
||||
if subdir:
|
||||
full_path = os.path.join(full_path, subdir)
|
||||
return os.path.exists(full_path)
|
||||
@@ -143,12 +142,12 @@ class FileRepo(object):
|
||||
def isdir(self, path):
|
||||
'''Returns true if the specified path exists in the repo
|
||||
and is a directory.'''
|
||||
return os.path.isdir(os.path.join(self.path, path))
|
||||
return os.path.isdir(os.path.join(self.root, path))
|
||||
|
||||
def isfile(self, path):
|
||||
'''Returns true if the specified path exists in the repo
|
||||
and is a regular file.'''
|
||||
return os.path.isfile(os.path.join(self.path, path))
|
||||
return os.path.isfile(os.path.join(self.root, path))
|
||||
|
||||
def join(self, *args):
|
||||
'''Combines path elements within the repo.'''
|
||||
@@ -168,32 +167,32 @@ class FileRepo(object):
|
||||
|
||||
def mkdir(self, path, mode=0777):
|
||||
'''Creates a directory within the repo.'''
|
||||
return os.mkdir(os.path.join(self.path, path), mode)
|
||||
return os.mkdir(os.path.join(self.root, path), mode)
|
||||
|
||||
def makedirs(self, path, mode=0777):
|
||||
'''Creates a directory within the repo, including parent directories.'''
|
||||
return os.makedirs(os.path.join(self.path, path), mode)
|
||||
return os.makedirs(os.path.join(self.root, path), mode)
|
||||
|
||||
def listdir(self, path):
|
||||
'''Lists the contents of a repo directory.'''
|
||||
return listdir(os.path.join(self.path, path))
|
||||
return listdir(os.path.join(self.root, path))
|
||||
|
||||
def remove(self, path):
|
||||
'''Removes a file from the repo.'''
|
||||
return os.remove(os.path.join(self.path, path))
|
||||
return os.remove(os.path.join(self.root, path))
|
||||
|
||||
def unlink(self, path):
|
||||
'''Removes a file from the repo.'''
|
||||
return os.unlink(os.path.join(self.path, path))
|
||||
return os.unlink(os.path.join(self.root, path))
|
||||
|
||||
def get(self, src, dest):
|
||||
'''Copies a file from the repo to a local file.'''
|
||||
cmd = ['/bin/cp', os.path.join(self.path, src), dest]
|
||||
cmd = ['/bin/cp', os.path.join(self.root, src), dest]
|
||||
return subprocess.call(cmd)
|
||||
|
||||
def put(self, src, dest):
|
||||
'''Copies a local file to the repo.'''
|
||||
cmd = ['/bin/cp', src, os.path.join(self.path, dest)]
|
||||
cmd = ['/bin/cp', src, os.path.join(self.root, dest)]
|
||||
return subprocess.call(cmd)
|
||||
|
||||
#
|
||||
@@ -205,24 +204,24 @@ class FileRepo(object):
|
||||
# will be a local temporary file that was copied from the remote
|
||||
# repo and/or will be copied to the remote repo on close.
|
||||
#
|
||||
class RepoFile(object):
|
||||
def __init__(self, repo, repo_path, mode):
|
||||
self.repo = repo
|
||||
self.repo_path = repo_path
|
||||
self.repo_mode = mode
|
||||
self.file = open(self.repo_path, mode)
|
||||
self.local_path = self.repo_path
|
||||
|
||||
def read(self):
|
||||
return self.file.read()
|
||||
|
||||
def open(self, path, mode='r'):
|
||||
'''Opens a file in the repo.'''
|
||||
class RepoFile(object):
|
||||
def __init__(self, repo, repo_path, mode):
|
||||
self.repo = repo
|
||||
self.repo_path = repo_path
|
||||
self.repo_mode = mode
|
||||
self.file = open(self.repo_path, mode)
|
||||
self.local_path = self.repo_path
|
||||
|
||||
def read(self):
|
||||
return self.file.read()
|
||||
|
||||
return RepoFile(self, os.path.join(self.path, path), mode)
|
||||
return self.RepoFile(self, os.path.join(self.root, path), mode)
|
||||
|
||||
def mount(self):
|
||||
'''Mounts the repo locally.'''
|
||||
if os.path.exists(self.path):
|
||||
if os.path.exists(self.root):
|
||||
return
|
||||
print 'Attempting to mount fileshare %s:' % self.url
|
||||
if NETFSMOUNTURLSYNC_AVAILABLE:
|
||||
@@ -235,38 +234,38 @@ class FileRepo(object):
|
||||
self.WE_MOUNTED_THE_REPO = True
|
||||
return 0
|
||||
else:
|
||||
os.mkdir(self.path)
|
||||
os.mkdir(self.root)
|
||||
if self.url.startswith('afp:'):
|
||||
cmd = ['/sbin/mount_afp', '-i', self.url, self.path]
|
||||
cmd = ['/sbin/mount_afp', '-i', self.url, self.root]
|
||||
elif self.url.startswith('smb:'):
|
||||
cmd = ['/sbin/mount_smbfs', self.url[4:], self.path]
|
||||
cmd = ['/sbin/mount_smbfs', self.url[4:], self.root]
|
||||
elif self.url.startswith('nfs://'):
|
||||
cmd = ['/sbin/mount_nfs', self.url[6:], self.path]
|
||||
cmd = ['/sbin/mount_nfs', self.url[6:], self.root]
|
||||
else:
|
||||
print >> sys.stderr, 'Unsupported filesystem URL!'
|
||||
return
|
||||
retcode = subprocess.call(cmd)
|
||||
if retcode:
|
||||
os.rmdir(self.path)
|
||||
os.rmdir(self.root)
|
||||
else:
|
||||
self.WE_MOUNTED_THE_REPO = True
|
||||
return retcode
|
||||
|
||||
def unmount(self):
|
||||
'''Unmounts the repo.'''
|
||||
if not os.path.exists(self.path):
|
||||
if not os.path.exists(self.root):
|
||||
return
|
||||
retcode = 0
|
||||
if os.path.exists(self.path):
|
||||
cmd = ['/sbin/umount', self.path]
|
||||
if os.path.exists(self.root):
|
||||
cmd = ['/sbin/umount', self.root]
|
||||
retcode = subprocess.call(cmd)
|
||||
return retcode
|
||||
|
||||
def walk(self, path, **kwargs):
|
||||
'''Walks a path in the repo, returning all files and subdirectories.
|
||||
Only a subset of the features of os.walk() are supported.'''
|
||||
for (dirpath, dirnames, filenames) in os.walk(os.path.join(self.path, path), **kwargs):
|
||||
dirpath = dirpath[len(self.path) + 1:]
|
||||
for (dirpath, dirnames, filenames) in os.walk(os.path.join(self.root, path), **kwargs):
|
||||
dirpath = dirpath[len(self.root) + 1:]
|
||||
yield (dirpath, dirnames, filenames)
|
||||
|
||||
def glob(self, path, *args):
|
||||
@@ -0,0 +1,21 @@
|
||||
import os
|
||||
|
||||
from munkilib.munkirepo.FileRepo import FileRepo
|
||||
|
||||
class GitFileRepo(FileRepo):
|
||||
'''A subclass of FileRepo that does git commits for pkginfo files'''
|
||||
|
||||
class RepoFile(object):
|
||||
def __init__(self, repo, repo_path, mode):
|
||||
self.repo = repo
|
||||
self.repo_path = repo_path
|
||||
self.repo_mode = mode
|
||||
self.file = open(self.repo_path, mode)
|
||||
self.local_path = self.repo_path
|
||||
|
||||
def __del__(self):
|
||||
if 'w' in self.repo_mode:
|
||||
print "Pretending to do a git commit on %s" % self.repo_path
|
||||
|
||||
def read(self):
|
||||
return self.file.read()
|
||||
@@ -0,0 +1,74 @@
|
||||
import imp
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
class Repo(object):
|
||||
'''Abstract base class for repo'''
|
||||
mounted = False
|
||||
|
||||
def __init__(self, path, url):
|
||||
self.root = path
|
||||
self.url = url
|
||||
|
||||
def available(self):
|
||||
'''if path does not exist, mount to local filesystem'''
|
||||
if not self.exists():
|
||||
retcode = self.mount()
|
||||
if retcode == 0:
|
||||
self.mounted = True
|
||||
#if path still doesn't exist, then cannot find munki_repo
|
||||
if not self.exists():
|
||||
print >> sys.stderr, "repo is missing"
|
||||
return False
|
||||
#check if all subdirectories are there
|
||||
for subdir in ['catalogs', 'manifests', 'pkgs', 'pkgsinfo']:
|
||||
if not self.exists(subdir):
|
||||
print >> sys.stderr, "repo is missing %s" % subdir
|
||||
return False
|
||||
# if we get this far, the repo path looks OK
|
||||
return True
|
||||
|
||||
def exists(self):
|
||||
'''Must be overriden in subclass'''
|
||||
return False
|
||||
|
||||
def mount(self):
|
||||
'''Must be overridden in subclasses'''
|
||||
return -1
|
||||
|
||||
|
||||
class MissingRepo(Repo):
|
||||
'''Stub object to return when we can't find the one requsted'''
|
||||
def available(self):
|
||||
return False
|
||||
|
||||
|
||||
def plugin_named(name):
|
||||
'''Returns a plugin object given a name'''
|
||||
try:
|
||||
module = globals()[name]
|
||||
return getattr(module, name)
|
||||
except (KeyError, AttributeError):
|
||||
print >> sys.stderr, (
|
||||
"ERROR: %s repo plugin not found." % name)
|
||||
return None
|
||||
|
||||
|
||||
def connect(repo_path, repo_url, plugin_name):
|
||||
'''Return a repo object for operations on our Munki repo'''
|
||||
if plugin_name is None:
|
||||
plugin_name = 'FileRepo'
|
||||
plugin = plugin_named(plugin_name)
|
||||
if plugin:
|
||||
return plugin(repo_path, repo_url)
|
||||
else:
|
||||
return MissingRepo(repo_path, repo_url)
|
||||
|
||||
|
||||
# yes, having this at the end is weird. But it allows us to dynamically import
|
||||
# additional modules from our directory
|
||||
__all__ = [os.path.splitext(name)[0]
|
||||
for name in os.listdir(os.path.dirname(os.path.abspath(__file__)))
|
||||
if name.endswith('.py') and not name == '__init__.py']
|
||||
from . import *
|
||||
Reference in New Issue
Block a user