def refer(conf, spec): """Take the package installed in venv_dir and refer to it. Returns True if it modified the filesystem. """ package_dir = expand.package_dir(conf, spec) target_dir = service_dir(conf, spec) if not os.path.isdir(package_dir): raise InstallerError( "{package_dir} not found".format(package_dir=package_dir)) if os.path.isdir(target_dir): # We assume there's nothing to do return False if os.path.exists(target_dir): raise InstallerError( "{target_dir} already exists".format(target_dir=target_dir)) try: os.mkdir(target_dir, 0o755) os.symlink(package_dir, os.path.join(target_dir, _VENV_DIR)) os.mkdir(os.path.join(target_dir, _ETC_DIR), 0o755) except Exception as e: raise InstallerError( "{package_dir} could not be installed as {target_dir}".format( package_dir=package_dir, target_dir=target_dir), e) return True
def activate(base_location, spec): """Symlink to an installed version Given the availability of some pre-installed version, create the appropriate symlink. Raise an error if the pre-installed version does not exist. Raise an error (note, do we want to do this?) if there's already something installed there. """ location = os.path.join(base_location, spec.service) current_version = active_version(base_location, spec) if current_version is not None: raise InstallerError( "{location} already has an installed version: {version}" .format(location=location, version=current_version)) spec = ensure_suffix(base_location, spec) target = spec.service + "-" + spec.suffix if not os.path.isdir(location + "-" + spec.suffix): raise InstallerError( "No pre-expanded version {version} for {location} found" .format(location=location, version=str(spec.version))) try: os.symlink(target, location) except OSError: raise InstallerError( "Cannot symlink {location} to {target}" .format(location=location, target=target))
def deactivate(base_location, spec): """Deactivate an installed version Given an installed version at the given location, deactivate it. If an explicit version is specified, then it is an error if any other version is present. Otherwise, we'll simply deactivate whatever is found there. """ location = os.path.join(base_location, spec.service) current_version = active_version(base_location, spec) if spec.version is not None and current_version != spec.version: msg = ("Attempt to deinstall {version} from {location} -" " {current} found instead") raise InstallerError(msg .format(location=location, version=str(spec.version), current=current_version)) if os.path.islink(location): try: os.unlink(location) except IOError as e: raise InstallerError("Can't unlink {location}" .format(location=location), e)
def remove(config, spec): """Remove an exploded version This is an error if the version's currently activated. There's no error if the version is already not there. Returns True if it modified the filesystem """ location = service_pointer(config, spec) current_version = active_version(config.SERVICE_LOCATION, spec) if current_version == spec.version: raise InstallerError( "Cannot remove {version} for {location} since it is current active" .format(location=location, version=spec.version)) ensure_suffix(config.SERVICE_LOCATION, spec) target = service_dir(config, spec) if not os.path.exists(target): return False if not os.path.isdir(target): msg = ("Cannot remove {version} for {location} since it is not" " a directory".format(location=location, version=spec.version)) raise InstallerError(msg) try: # Delete recursively shutil.rmtree(target) except Exception as e: raise InstallerError("Could not delete {target}".format(target=target), e) return True
def explode(config, spec): """Take the package installed in cache_dir and expand it. This will be a no-op if there's already something at the target. We require the source package to be present in the cache_dir, with the name $(basename $location)-suffix.tgz Returns (True, spec) if it modified the filesystem. """ spec = cache.assert_package_present(config, spec) cache_file = cache.cache_file(config, spec) target_dir = package_dir(config, spec) if not os.path.isfile(cache_file): raise InstallerError( "{cache_file} not found".format(cache_file=cache_file)) if not tarfile.is_tarfile(cache_file): raise InstallerError( "{cache_file} is not in the correct format".format( cache_file=cache_file)) if os.path.isdir(target_dir): # We assume there's nothing to do return (False, spec) if os.path.exists(target_dir): raise InstallerError( "{target_dir} already exists".format(target_dir=target_dir)) try: os.mkdir(target_dir, 0o755) gname = config['group_name'] group = grp.getgrnam(gname) gid = group.gr_gid with tarfile.open(cache_file) as tar: members = tar.getmembers() for m in members: m.uid = 0 m.gid = gid m.uname = 'root' m.gname = gname m.mode |= config['extra_mode_bits'] tar.extractall(path=target_dir, members=members) except Exception as e: raise InstallerError( "{cache_file} could not be exploded to {target_dir}".format( cache_file=cache_file, target_dir=target_dir), e) return (True, spec)
def guess_from_suffix(file_name, format, group=2): """Return a best guess, given a directory or tarball name""" file = os.path.basename(file_name) match = format.match(file) if not match: raise InstallerError( "{} doesn't have a viable suffix".format(file_name)) version = match.group(group) # The suffix is from an older ISO image guess = best_guess(version) if guess == version: raise InstallerError("{} has an unknown suffix".format(file_name)) return from_str(guess)
def active_version(base_location, spec): """Is there a package installed at the specified location? If so, return its version; otherwise None. If it looks like something's not right (eg, bad symlink or some other file there), throw an exception. """ location = os.path.join(base_location, spec.service) try: if not os.path.exists(location): return None target = os.readlink(location) if not target.startswith(spec.service + "-"): raise InstallerError( "{location} is not an installed venv; target is {target}" .format(location=location, target=target)) return from_service_dir(os.path.join(base_location, target)) except OSError: raise InstallerError("{location} is not a symlink" .format(location=location))
def assert_package_present(config, spec): index = indexer.load_index(config.CACHE_DIR) if 'packages' not in index: raise InstallerError("Badly formed index file in {cache_dir}".format( cache_dir=config.CACHE_DIR)) packages = index['packages'] if spec.package not in packages: raise InstallerError( "{package} not listed in index in {cache_dir}".format( package=spec.package, cache_dir=config.CACHE_DIR)) available = index['packages'][spec.package] if spec.version is VERSION_LATEST: try: v = max(available.keys(), key=from_str) spec.version = from_str(best_guess(v)) except KeyError: raise InstallerError( "no versions of {package} are not available".format( package=spec.package)) if str(spec.version) not in available: raise InstallerError("{version} of {package} is not available".format( package=spec.package, version=str(spec.version))) spec.tarball = available[str(spec.version)]['file'] spec.suffix = available[str(spec.version)]['suffix'] source = config.repo_url + spec.tarball target = os.path.join(config.CACHE_DIR, spec.tarball) if not os.path.isfile(target): # If the file's there, assume we've nothing to do _download(source, target) return spec
def ensure_suffix(base_location, spec): try: if spec.suffix is not None: return spec except AttributeError: pass # Locate the appropriate suffix associated with this verison. for file_name in os.listdir(base_location): match = DIR_FORMAT.match(file_name) if not match: continue #if not file_name.startswith(spec.service + "-"): if spec.service != match.group(1): continue dir = os.path.join(base_location, file_name) if spec.version != from_service_dir(dir): continue spec.suffix = match.group(2) return spec raise InstallerError( "Cannot find version {version} for service {service}" .format(version=str(spec.version), service=spec.service))