
Juju 2.0 provides support for display of the version of an application deployed by a charm in juju status. Insert the os_application_version_set function into the existing assess_status function - this gets called after all hook executions, and periodically after that, so any changes in package versions due to normal system updates will also be reflected in the status output. This review also includes a resync of charm-helpers to pickup hookenv and contrib.openstack support for this feature. Change-Id: Ie8c969718b8e34f4b27e7d75f2b58167bf04c14d
197 lines
6.1 KiB
Python
197 lines
6.1 KiB
Python
# Copyright 2014-2015 Canonical Limited.
|
|
#
|
|
# 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
|
|
#
|
|
# http://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.
|
|
|
|
import importlib
|
|
from charmhelpers.osplatform import get_platform
|
|
from yaml import safe_load
|
|
from charmhelpers.core.hookenv import (
|
|
config,
|
|
log,
|
|
)
|
|
|
|
import six
|
|
if six.PY3:
|
|
from urllib.parse import urlparse, urlunparse
|
|
else:
|
|
from urlparse import urlparse, urlunparse
|
|
|
|
|
|
# The order of this list is very important. Handlers should be listed in from
|
|
# least- to most-specific URL matching.
|
|
FETCH_HANDLERS = (
|
|
'charmhelpers.fetch.archiveurl.ArchiveUrlFetchHandler',
|
|
'charmhelpers.fetch.bzrurl.BzrUrlFetchHandler',
|
|
'charmhelpers.fetch.giturl.GitUrlFetchHandler',
|
|
)
|
|
|
|
|
|
class SourceConfigError(Exception):
|
|
pass
|
|
|
|
|
|
class UnhandledSource(Exception):
|
|
pass
|
|
|
|
|
|
class AptLockError(Exception):
|
|
pass
|
|
|
|
|
|
class BaseFetchHandler(object):
|
|
|
|
"""Base class for FetchHandler implementations in fetch plugins"""
|
|
|
|
def can_handle(self, source):
|
|
"""Returns True if the source can be handled. Otherwise returns
|
|
a string explaining why it cannot"""
|
|
return "Wrong source type"
|
|
|
|
def install(self, source):
|
|
"""Try to download and unpack the source. Return the path to the
|
|
unpacked files or raise UnhandledSource."""
|
|
raise UnhandledSource("Wrong source type {}".format(source))
|
|
|
|
def parse_url(self, url):
|
|
return urlparse(url)
|
|
|
|
def base_url(self, url):
|
|
"""Return url without querystring or fragment"""
|
|
parts = list(self.parse_url(url))
|
|
parts[4:] = ['' for i in parts[4:]]
|
|
return urlunparse(parts)
|
|
|
|
|
|
__platform__ = get_platform()
|
|
module = "charmhelpers.fetch.%s" % __platform__
|
|
fetch = importlib.import_module(module)
|
|
|
|
filter_installed_packages = fetch.filter_installed_packages
|
|
install = fetch.install
|
|
upgrade = fetch.upgrade
|
|
update = fetch.update
|
|
purge = fetch.purge
|
|
add_source = fetch.add_source
|
|
|
|
if __platform__ == "ubuntu":
|
|
apt_cache = fetch.apt_cache
|
|
apt_install = fetch.install
|
|
apt_update = fetch.update
|
|
apt_upgrade = fetch.upgrade
|
|
apt_purge = fetch.purge
|
|
apt_mark = fetch.apt_mark
|
|
apt_hold = fetch.apt_hold
|
|
apt_unhold = fetch.apt_unhold
|
|
elif __platform__ == "centos":
|
|
yum_search = fetch.yum_search
|
|
|
|
|
|
def configure_sources(update=False,
|
|
sources_var='install_sources',
|
|
keys_var='install_keys'):
|
|
"""Configure multiple sources from charm configuration.
|
|
|
|
The lists are encoded as yaml fragments in the configuration.
|
|
The fragment needs to be included as a string. Sources and their
|
|
corresponding keys are of the types supported by add_source().
|
|
|
|
Example config:
|
|
install_sources: |
|
|
- "ppa:foo"
|
|
- "http://example.com/repo precise main"
|
|
install_keys: |
|
|
- null
|
|
- "a1b2c3d4"
|
|
|
|
Note that 'null' (a.k.a. None) should not be quoted.
|
|
"""
|
|
sources = safe_load((config(sources_var) or '').strip()) or []
|
|
keys = safe_load((config(keys_var) or '').strip()) or None
|
|
|
|
if isinstance(sources, six.string_types):
|
|
sources = [sources]
|
|
|
|
if keys is None:
|
|
for source in sources:
|
|
add_source(source, None)
|
|
else:
|
|
if isinstance(keys, six.string_types):
|
|
keys = [keys]
|
|
|
|
if len(sources) != len(keys):
|
|
raise SourceConfigError(
|
|
'Install sources and keys lists are different lengths')
|
|
for source, key in zip(sources, keys):
|
|
add_source(source, key)
|
|
if update:
|
|
fetch.update(fatal=True)
|
|
|
|
|
|
def install_remote(source, *args, **kwargs):
|
|
"""Install a file tree from a remote source.
|
|
|
|
The specified source should be a url of the form:
|
|
scheme://[host]/path[#[option=value][&...]]
|
|
|
|
Schemes supported are based on this modules submodules.
|
|
Options supported are submodule-specific.
|
|
Additional arguments are passed through to the submodule.
|
|
|
|
For example::
|
|
|
|
dest = install_remote('http://example.com/archive.tgz',
|
|
checksum='deadbeef',
|
|
hash_type='sha1')
|
|
|
|
This will download `archive.tgz`, validate it using SHA1 and, if
|
|
the file is ok, extract it and return the directory in which it
|
|
was extracted. If the checksum fails, it will raise
|
|
:class:`charmhelpers.core.host.ChecksumError`.
|
|
"""
|
|
# We ONLY check for True here because can_handle may return a string
|
|
# explaining why it can't handle a given source.
|
|
handlers = [h for h in plugins() if h.can_handle(source) is True]
|
|
for handler in handlers:
|
|
try:
|
|
return handler.install(source, *args, **kwargs)
|
|
except UnhandledSource as e:
|
|
log('Install source attempt unsuccessful: {}'.format(e),
|
|
level='WARNING')
|
|
raise UnhandledSource("No handler found for source {}".format(source))
|
|
|
|
|
|
def install_from_config(config_var_name):
|
|
"""Install a file from config."""
|
|
charm_config = config()
|
|
source = charm_config[config_var_name]
|
|
return install_remote(source)
|
|
|
|
|
|
def plugins(fetch_handlers=None):
|
|
if not fetch_handlers:
|
|
fetch_handlers = FETCH_HANDLERS
|
|
plugin_list = []
|
|
for handler_name in fetch_handlers:
|
|
package, classname = handler_name.rsplit('.', 1)
|
|
try:
|
|
handler_class = getattr(
|
|
importlib.import_module(package),
|
|
classname)
|
|
plugin_list.append(handler_class())
|
|
except NotImplementedError:
|
|
# Skip missing plugins so that they can be ommitted from
|
|
# installation if desired
|
|
log("FetchHandler {} not found, skipping plugin".format(
|
|
handler_name))
|
|
return plugin_list
|