def __add_bare_helper(openstack_release, pocket_format, final_function):
    """Helper for _add_bare_openstack[_proposed]

    The bulk of the work between the two functions is exactly the same except
    for the pocket format and the function that is run if it's the distro
    version.

    :param openstack_release: the OpenStack codename.  e.g. ussuri
    :type openstack_release: str
    :param pocket_format: the pocket formatter string to construct a pocket str
        from the openstack_release and the current ubuntu version.
    :type pocket_format: str
    :param final_function: the function to call if it is the distro version.
    :type final_function: Callable
    :raises SourceConfigError on error
    """
    ubuntu_version = get_distrib_codename()
    possible_pocket = pocket_format.format(ubuntu_version, openstack_release)
    if possible_pocket in CLOUD_ARCHIVE_POCKETS:
        _add_cloud_pocket(possible_pocket)
        return
    # Otherwise it's almost certainly the distro version; verify that it
    # exists.
    try:
        assert UBUNTU_OPENSTACK_RELEASE[ubuntu_version] == openstack_release
    except KeyError:
        raise SourceConfigError(
            "Invalid ubuntu version {} isn't known to this library".format(
                ubuntu_version))
    except AssertionError:
        raise SourceConfigError(
            'Invalid OpenStack release specified: {} for Ubuntu version {}'.
            format(openstack_release, ubuntu_version))
    final_function()
Example #2
0
def _verify_is_ubuntu_rel(release, os_release):
    """Verify that the release is in the same as the current ubuntu release.

    :param release: String, lowercase for the release.
    :param os_release: String, the os_release being asked for
    :raises: SourceConfigError if the release is not the same as the ubuntu
        release.
    """
    ubuntu_rel = lsb_release()['DISTRIB_CODENAME']
    if release != ubuntu_rel:
        raise SourceConfigError(
            'Invalid Cloud Archive release specified: {}-{} on this Ubuntu'
            'version ({})'.format(release, os_release, ubuntu_rel))
Example #3
0
def _add_proposed():
    """Add the PROPOSED_POCKET as /etc/apt/source.list.d/proposed.list

    Uses lsb_release()['DISTRIB_CODENAME'] to determine the correct staza for
    the deb line.

    For intel architecutres PROPOSED_POCKET is used for the release, but for
    other architectures PROPOSED_PORTS_POCKET is used for the release.
    """
    release = lsb_release()['DISTRIB_CODENAME']
    arch = platform.machine()
    if arch not in six.iterkeys(ARCH_TO_PROPOSED_POCKET):
        raise SourceConfigError(
            "Arch {} not supported for (distro-)proposed".format(arch))
    with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt:
        apt.write(ARCH_TO_PROPOSED_POCKET[arch].format(release))
Example #4
0
def _add_proposed():
    """Add the PROPOSED_POCKET as /etc/apt/source.list.d/proposed.list

    Uses get_distrib_codename to determine the correct stanza for
    the deb line.

    For Intel architectures PROPOSED_POCKET is used for the release, but for
    other architectures PROPOSED_PORTS_POCKET is used for the release.
    """
    release = get_distrib_codename()
    arch = platform.machine()
    if arch not in ARCH_TO_PROPOSED_POCKET.keys():
        raise SourceConfigError(
            "Arch {} not supported for (distro-)proposed".format(arch))
    with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt:
        apt.write(ARCH_TO_PROPOSED_POCKET[arch].format(release))
Example #5
0
def _add_cloud_pocket(pocket):
    """Add a cloud pocket as /etc/apt/sources.d/cloud-archive.list

    Note that this overwrites the existing file if there is one.

    This function also converts the simple pocket in to the actual pocket using
    the CLOUD_ARCHIVE_POCKETS mapping.

    :param pocket: string representing the pocket to add a deb spec for.
    :raises: SourceConfigError if the cloud pocket doesn't exist or the
        requested release doesn't match the current distro version.
    """
    apt_install(filter_installed_packages(['ubuntu-cloud-keyring']),
                fatal=True)
    if pocket not in CLOUD_ARCHIVE_POCKETS:
        raise SourceConfigError('Unsupported cloud: source option %s' % pocket)
    actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket]
    with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt:
        apt.write(CLOUD_ARCHIVE.format(actual_pocket))
Example #6
0
def add_source(source, key=None, fail_invalid=False):
    """Add a package source to this system.

    @param source: a URL or sources.list entry, as supported by
    add-apt-repository(1). Examples::

        ppa:charmers/example
        deb https://stub:[email protected]/ubuntu trusty main

    In addition:
        'proposed:' may be used to enable the standard 'proposed'
        pocket for the release.
        'cloud:' may be used to activate official cloud archive pockets,
        such as 'cloud:icehouse'
        'distro' may be used as a noop

    Full list of source specifications supported by the function are:

    'distro': A NOP; i.e. it has no effect.
    'proposed': the proposed deb spec [2] is wrtten to
      /etc/apt/sources.list/proposed
    'distro-proposed': adds <version>-proposed to the debs [2]
    'ppa:<ppa-name>': add-apt-repository --yes <ppa_name>
    'deb <deb-spec>': add-apt-repository --yes deb <deb-spec>
    'http://....': add-apt-repository --yes http://...
    'cloud-archive:<spec>': add-apt-repository -yes cloud-archive:<spec>
    'cloud:<release>[-staging]': specify a Cloud Archive pocket <release> with
      optional staging version.  If staging is used then the staging PPA [2]
      with be used.  If staging is NOT used then the cloud archive [3] will be
      added, and the 'ubuntu-cloud-keyring' package will be added for the
      current distro.

    Otherwise the source is not recognised and this is logged to the juju log.
    However, no error is raised, unless sys_error_on_exit is True.

    [1] deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main
        where {} is replaced with the derived pocket name.
    [2] deb http://archive.ubuntu.com/ubuntu {}-proposed \
        main universe multiverse restricted
        where {} is replaced with the lsb_release codename (e.g. xenial)
    [3] deb http://ubuntu-cloud.archive.canonical.com/ubuntu <pocket>
        to /etc/apt/sources.list.d/cloud-archive-list

    @param key: A key to be added to the system's APT keyring and used
    to verify the signatures on packages. Ideally, this should be an
    ASCII format GPG public key including the block headers. A GPG key
    id may also be used, but be aware that only insecure protocols are
    available to retrieve the actual public key from a public keyserver
    placing your Juju environment at risk. ppa and cloud archive keys
    are securely added automtically, so sould not be provided.

    @param fail_invalid: (boolean) if True, then the function raises a
    SourceConfigError is there is no matching installation source.

    @raises SourceConfigError() if for cloud:<pocket>, the <pocket> is not a
    valid pocket in CLOUD_ARCHIVE_POCKETS
    """
    _mapping = OrderedDict([
        (r"^distro$", lambda: None),  # This is a NOP
        (r"^(?:proposed|distro-proposed)$", _add_proposed),
        (r"^cloud-archive:(.*)$", _add_apt_repository),
        (r"^((?:deb |http:|https:|ppa:).*)$", _add_apt_repository),
        (r"^cloud:(.*)-(.*)\/staging$", _add_cloud_staging),
        (r"^cloud:(.*)-(.*)$", _add_cloud_distro_check),
        (r"^cloud:(.*)$", _add_cloud_pocket),
        (r"^snap:.*-(.*)-(.*)$", _add_cloud_distro_check),
    ])
    if source is None:
        source = ''
    for r, fn in six.iteritems(_mapping):
        m = re.match(r, source)
        if m:
            # call the assoicated function with the captured groups
            # raises SourceConfigError on error.
            fn(*m.groups())
            if key:
                try:
                    import_key(key)
                except GPGKeyError as e:
                    raise SourceConfigError(str(e))
            break
    else:
        # nothing matched.  log an error and maybe sys.exit
        err = "Unknown source: {!r}".format(source)
        log(err)
        if fail_invalid:
            raise SourceConfigError(err)
Example #7
0
def add_source(source, key=None):
    """Add a package source to this system.

    @param source: a URL or sources.list entry, as supported by
    add-apt-repository(1). Examples::

        ppa:charmers/example
        deb https://stub:[email protected]/ubuntu trusty main

    In addition:
        'proposed:' may be used to enable the standard 'proposed'
        pocket for the release.
        'cloud:' may be used to activate official cloud archive pockets,
        such as 'cloud:icehouse'
        'distro' may be used as a noop

    @param key: A key to be added to the system's APT keyring and used
    to verify the signatures on packages. Ideally, this should be an
    ASCII format GPG public key including the block headers. A GPG key
    id may also be used, but be aware that only insecure protocols are
    available to retrieve the actual public key from a public keyserver
    placing your Juju environment at risk. ppa and cloud archive keys
    are securely added automtically, so sould not be provided.
    """
    if source is None:
        log('Source is not present. Skipping')
        return

    if (source.startswith('ppa:') or source.startswith('http')
            or source.startswith('deb ')
            or source.startswith('cloud-archive:')):
        subprocess.check_call(['add-apt-repository', '--yes', source])
    elif source.startswith('cloud:'):
        install(filter_installed_packages(['ubuntu-cloud-keyring']),
                fatal=True)
        pocket = source.split(':')[-1]
        if pocket not in CLOUD_ARCHIVE_POCKETS:
            raise SourceConfigError('Unsupported cloud: source option %s' %
                                    pocket)
        actual_pocket = CLOUD_ARCHIVE_POCKETS[pocket]
        with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt:
            apt.write(CLOUD_ARCHIVE.format(actual_pocket))
    elif source == 'proposed':
        release = lsb_release()['DISTRIB_CODENAME']
        with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt:
            apt.write(PROPOSED_POCKET.format(release))
    elif source == 'distro':
        pass
    else:
        log("Unknown source: {!r}".format(source))

    if key:
        if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key:
            with NamedTemporaryFile('w+') as key_file:
                key_file.write(key)
                key_file.flush()
                key_file.seek(0)
                subprocess.check_call(['apt-key', 'add', '-'], stdin=key_file)
        else:
            # Note that hkp: is in no way a secure protocol. Using a
            # GPG key id is pointless from a security POV unless you
            # absolutely trust your network and DNS.
            subprocess.check_call([
                'apt-key', 'adv', '--keyserver',
                'hkp://keyserver.ubuntu.com:80', '--recv', key
            ])