Operator Libs Linux

  • Jon Seager
Channel Revision Published Runs on
latest/stable 2 09 Mar 2023
Ubuntu 22.04 Ubuntu 20.04
latest/stable 1 28 Oct 2021
Ubuntu 22.04 Ubuntu 20.04
juju deploy operator-libs-linux
Show information

Platform:

Ubuntu
22.04 20.04

charms.operator_libs_linux.v0.apt

Abstractions for the system's Debian/Ubuntu package information and repositories.

This module contains abstractions and wrappers around Debian/Ubuntu-style repositories and packages, in order to easily provide an idiomatic and Pythonic mechanism for adding packages and/or repositories to systems for use in machine charms.

A sane default configuration is attainable through nothing more than instantiation of the appropriate classes. DebianPackage objects provide information about the architecture, version, name, and status of a package.

DebianPackage will try to look up a package either from dpkg -L or from apt-cache when provided with a string indicating the package name. If it cannot be located, PackageNotFoundError will be returned, as apt and dpkg otherwise return 100 for all errors, and a meaningful error message if the package is not known is desirable.

To install packages with convenience methods:

try:
    # Run `apt-get update`
    apt.update()
    apt.add_package("zsh")
    apt.add_package(["vim", "htop", "wget"])
except PackageNotFoundError:
    logger.error("a specified package not found in package cache or on system")
except PackageError as e:
    logger.error("could not install package. Reason: %s", e.message)

To find details of a specific package:

try:
    vim = apt.DebianPackage.from_system("vim")

    # To find from the apt cache only
    # apt.DebianPackage.from_apt_cache("vim")

    # To find from installed packages only
    # apt.DebianPackage.from_installed_package("vim")

    vim.ensure(PackageState.Latest)
    logger.info("updated vim to version: %s", vim.fullversion)
except PackageNotFoundError:
    logger.error("a specified package not found in package cache or on system")
except PackageError as e:
    logger.error("could not install package. Reason: %s", e.message)

RepositoryMapping will return a dict-like object containing enabled system repositories and their properties (available groups, baseuri. gpg key). This class can add, disable, or manipulate repositories. Items can be retrieved as DebianRepository objects.

In order add a new repository with explicit details for fields, a new DebianRepository can be added to RepositoryMapping

RepositoryMapping provides an abstraction around the existing repositories on the system, and can be accessed and iterated over like any Mapping object, to retrieve values by key, iterate, or perform other operations.

Keys are constructed as {repo_type}-{}-{release} in order to uniquely identify a repository.

Repositories can be added with explicit values through a Python constructor.

Example:

repositories = apt.RepositoryMapping()

if "deb-example.com-focal" not in repositories:
    repositories.add(DebianRepository(enabled=True, repotype="deb",
                     uri="https://example.com", release="focal", groups=["universe"]))

Alternatively, any valid sources.list line may be used to construct a new DebianRepository.

Example:

repositories = apt.RepositoryMapping()

if "deb-us.archive.ubuntu.com-xenial" not in repositories:
    line = "deb http://us.archive.ubuntu.com/ubuntu xenial main restricted"
    repo = DebianRepository.from_repo_line(line)
    repositories.add(repo)

Index

class Error

Description

Base class of most errors raised by this library. None

Methods

Error. __repr__( self )

Description

Represent the Error. None

Error. name( self )

Description

Return a string representation of the model plus class. None

Error. message( self )

Description

Return the message passed as an argument. None

class PackageError

Description

Raised when there's an error installing or removing a package. None

class PackageNotFoundError

Description

Raised when a requested package is not known to the system. None

class PackageState

Description

A class to represent possible package states. None

class DebianPackage

Represents a traditional Debian package and its utility functions.

Description

DebianPackage wraps information and functionality around a known package, whether installed or available. The version, epoch, name, and architecture can be easily queried and compared against other DebianPackage objects to determine the latest version or to install a specific version.

The representation of this object as a string mimics the output from dpkg for familiarity.

Installation and removal of packages is handled through the state property or ensure method, with the following options:

apt.PackageState.Absent
apt.PackageState.Available
apt.PackageState.Present
apt.PackageState.Latest

When DebianPackage is initialized, the state of a given DebianPackage object will be set to Available, Present, or Latest, with Absent implemented as a convenience for removal (though it operates essentially the same as Available).

Methods

DebianPackage. __init__( self , name: str , version: str , epoch: str , arch: str , state: PackageState )

DebianPackage. __eq__( self , other )

Equality for comparison.

Arguments

other

a DebianPackage object for comparison

Returns

A boolean reflecting equality

DebianPackage. __hash__( self )

Description

Return a hash of this package. None

DebianPackage. __repr__( self )

Description

Represent the package. None

DebianPackage. __str__( self )

Description

Return a human-readable representation of the package. None

DebianPackage. name( self )

Description

Returns the name of the package. None

DebianPackage. ensure( self , state: PackageState )

Ensure that a package is in a given state.

Description

Args: state: a PackageState to reconcile the package to

Raises: PackageError from the underlying call to apt

DebianPackage. present( self )

Description

Returns whether or not a package is present. None

DebianPackage. latest( self )

Description

Returns whether the package is the most recent version. None

DebianPackage. state( self )

Description

Returns the current package state. None

DebianPackage. state( self , state: PackageState )

Set the package state to a given value.

Description

Args: state: a PackageState to reconcile the package to

Raises: PackageError from the underlying call to apt

DebianPackage. version( self )

Description

Returns the version for a package. None

DebianPackage. epoch( self )

Description

Returns the epoch for a package. May be unset. None

DebianPackage. arch( self )

Description

Returns the architecture for a package. None

DebianPackage. fullversion( self )

Description

Returns the name+epoch for a package. None

DebianPackage. from_system( cls , package: str , version , arch )

Locates a package, either on the system or known to apt, and serializes the information.

Arguments

package

a string representing the package

version

an optional string if a specific version is requested

arch

an optional architecture, defaulting to dpkg --print-architecture. If an architecture is not specified, this will be used for selection.

DebianPackage. from_installed_package( cls , package: str , version , arch )

Check whether the package is already installed and return an instance.

Arguments

package

a string representing the package

version

an optional string if a specific version is requested

arch

an optional architecture, defaulting to dpkg --print-architecture. If an architecture is not specified, this will be used for selection.

DebianPackage. from_apt_cache( cls , package: str , version , arch )

Check whether the package is already installed and return an instance.

Arguments

package

a string representing the package

version

an optional string if a specific version is requested

arch

an optional architecture, defaulting to dpkg --print-architecture. If an architecture is not specified, this will be used for selection.

class Version

An abstraction around package versions.

Description

This seems like it should be strictly unnecessary, except that apt_pkg is not usable inside a venv, and wedging version comparisons into DebianPackage would overcomplicate it.

This class implements the algorithm found here: https://www.debian.org/doc/debian-policy/ch-controlfields.html#version

Methods

Version. __init__( self , version: str , epoch: str )

Version. __repr__( self )

Description

Represent the package. None

Version. __str__( self )

Description

Return human-readable representation of the package. None

Version. epoch( self )

Description

Returns the epoch for a package. May be empty. None

Version. number( self )

Description

Returns the version number for a package. None

Version. __lt__( self , other )

Description

Less than magic method impl. None

Version. __eq__( self , other )

Description

Equality magic method impl. None

Version. __gt__( self , other )

Description

Greater than magic method impl. None

Version. __le__( self , other )

Description

Less than or equal to magic method impl. None

Version. __ge__( self , other )

Description

Greater than or equal to magic method impl. None

Version. __ne__( self , other )

Description

Not equal to magic method impl. None

def add_package(
    package_names,
    version,
    arch,
    update_cache
)

Add a package or list of packages to the system.

Description

Args: package_names: single package name, or list of package names name: the name(s) of the package(s) version: an (Optional) version as a string. Defaults to the latest known arch: an optional architecture for the package update_cache: whether or not to run apt-get update prior to operating

Raises: TypeError if no package name is given, or explicit version is set for multiple packages PackageNotFoundError if the package is not in the cache. PackageError if packages fail to install

def remove_package(package_names)

Remove package(s) from the system.

Arguments

package_names

the name of a package

def update()

Description

Update the apt cache via apt-get update. None

def import_key(key: str)

Import an ASCII Armor key.

Description

A Radix64 format keyid is also supported for backwards compatibility. In this case Ubuntu keyserver will be queried for a key via HTTPS by its keyid. This method is less preferable because https proxy servers may require traffic decryption which is equivalent to a man-in-the-middle attack (a proxy server impersonates keyserver TLS certificates and has to be explicitly trusted by the system).

Args: key: A GPG key in ASCII armor format, including BEGIN and END markers or a keyid.

Returns: The GPG key filename written.

Raises: GPGKeyError if the key could not be imported

class InvalidSourceError

Description

Exceptions for invalid source entries. None

class GPGKeyError

Description

Exceptions for GPG keys. None

class DebianRepository

Description

An abstraction to represent a repository. None

Methods

DebianRepository. __init__( self , enabled: bool , repotype: str , uri: str , release: str , groups , filename: str , gpg_key_filename: str , options )

DebianRepository. enabled( self )

Description

Return whether or not the repository is enabled. None

DebianRepository. repotype( self )

Description

Return whether it is binary or source. None

DebianRepository. uri( self )

Description

Return the URI. None

DebianRepository. release( self )

Description

Return which Debian/Ubuntu releases it is valid for. None

DebianRepository. groups( self )

Description

Return the enabled package groups. None

DebianRepository. filename( self )

Description

Returns the filename for a repository. None

DebianRepository. filename( self , fname: str )

Set the filename used when a repo is written back to disk.

Arguments

fname

a filename to write the repository information to.

DebianRepository. gpg_key( self )

Description

Returns the path to the GPG key for this repository. None

DebianRepository. options( self )

Description

Returns any additional repo options which are set. None

DebianRepository. make_options_string( self , include_signed_by: bool )

Generate the complete one-line-style options string for a repository.

Description

Combining gpg_key, if set (and include_signed_by is True), with any other provided options to form the options section of a one-line-style definition.

DebianRepository. prefix_from_uri( uri: str )

Description

Get a repo list prefix from the uri, depending on whether a path is set. None

DebianRepository. from_repo_line( repo_line: str , write_file )

Instantiate a new DebianRepository from a sources.list entry line.

Arguments

repo_line

a string representing a repository entry

write_file

boolean to enable writing the new repo to disk. True by default. Expect it to result in an add-apt-repository call under the hood, like: add-apt-repository --no-update --sourceslist="$repo_line"

DebianRepository. disable( self )

Remove this repository by disabling it in the source file.

Description

WARNING: This method does NOT alter the self.enabled flag.

WARNING: disable is currently not implemented for repositories defined by a deb822 stanza. Raises a NotImplementedError in this case.

DebianRepository. import_key( self , key: str )

Import an ASCII Armor key.

Description

A Radix64 format keyid is also supported for backwards compatibility. In this case Ubuntu keyserver will be queried for a key via HTTPS by its keyid. This method is less preferable because https proxy servers may require traffic decryption which is equivalent to a man-in-the-middle attack (a proxy server impersonates keyserver TLS certificates and has to be explicitly trusted by the system).

Args: key: A GPG key in ASCII armor format, including BEGIN and END markers or a keyid.

Raises: GPGKeyError if the key could not be imported

class RepositoryMapping

An representation of known repositories.

Description

Instantiation of RepositoryMapping will iterate through the filesystem, parse out repository files in /etc/apt/..., and create DebianRepository objects in this list.

Typical usage:

repositories = apt.RepositoryMapping()
repositories.add(DebianRepository(
    enabled=True, repotype="deb", uri="https://example.com", release="focal",
    groups=["universe"]
))

Methods

RepositoryMapping. __init__( self )

RepositoryMapping. __contains__( self , key: Any )

Magic method for checking presence of repo in mapping.

Description

Checks against the string names used to identify repositories.

RepositoryMapping. __len__( self )

Description

Return number of repositories in map. None

RepositoryMapping. __iter__( self )

Return iterator for RepositoryMapping.

Description

Iterates over the DebianRepository values rather than the string names. FIXME: this breaks the expectations of the Mapping abstract base class for example when it provides methods like keys and items

RepositoryMapping. __getitem__( self , repository_uri: str )

Description

Return a given DebianRepository. None

RepositoryMapping. __setitem__( self , repository_uri: str , repository: DebianRepository )

Description

Add a DebianRepository to the cache. None

RepositoryMapping. load_deb822( self , filename: str )

Load a deb822 format repository source file into the cache.

Description

In contrast to one-line-style, the deb822 format specifies a repository using a multi-line stanza. Stanzas are separated by whitespace, and each definition consists of lines that are either key: value pairs, or continuations of the previous value.

Read more about the deb822 format here: https://manpages.ubuntu.com/manpages/noble/en/man5/sources.list.5.html For instance, ubuntu 24.04 (noble) lists its sources using deb822 style in: /etc/apt/sources.list.d/ubuntu.sources

RepositoryMapping. load( self , filename: str )

Load a one-line-style format repository source file into the cache.

Arguments

filename

the path to the repository file

RepositoryMapping. add( self , repo: DebianRepository , default_filename )

Add a new repository to the system using add-apt-repository.

Arguments

repo

a DebianRepository object if repo.enabled is falsey, will return without adding the repository

RepositoryMapping. disable( self , repo: DebianRepository )

Remove a repository by disabling it in the source file.

Description

WARNING: disable is currently not implemented for repositories defined by a deb822 stanza, and will raise a NotImplementedError if called on one.

WARNING: This method does NOT alter the .enabled flag on the DebianRepository.

class MissingRequiredKeyError

Description

Missing a required value in a source file. None

Methods

MissingRequiredKeyError. __init__( self , message: str )

class BadValueError

Description

Bad value for an entry in a source file. None

Methods

BadValueError. __init__( self , message: str )