Open Container Interface Support

The OCI Layer

The OCI module contains abstract interfaces for dealing with the container ecosystem. OciImage represents an image that can be pushed to a registry. OciContainer represents a single container. While an OciContainer is a Machine, and a full OS environment including ssh can run on such a container, that is atypical. Instead it is more common to set oci_command and run a single application.

class carthage.oci.OciContainer(*, readonly=None, **kwargs)
exposed_ports()

Return a sequence of OciExposedPort for any container ports that should be exposed.

By default, instantiate all OciExposedPort instances in the injector.

mounts()

Sequence of OciMount objects to be mounted to the container.

By default instantiate OciMount objects in the injector hierarchy.

oci_command()

Override the container command if non-None. Defaults to checking on the model

oci_interactive = False

Should stdin be kept open?

oci_tty = False

Allocate a tty for stdio

class carthage.oci.OciEnviron(assignment: 'str', scope: 'str' = 'all')
default_instance_injection_key()

Called when an instance of an injectable is used to add a dependency provider bby the single argument form of Injectable.add_provider()

class carthage.oci.OciExposedPort(container_port: 'int', host_ip: 'str' = '0.0.0.0', host_port: 'int' = '', proto: 'str' = 'tcp')
default_instance_injection_key()

Called when an instance of an injectable is used to add a dependency provider bby the single argument form of Injectable.add_provider()

class carthage.oci.OciImage(*, oci_image_tag=None, id=None, **kwargs)
classmethod default_class_injection_key()

Called by Injector.add_provider() in the single argument form to get the injection key to use when this class is added to provide a dependency.

property deployable_names

Uses each of self.deployable_name_prefixes as a name_type for self.name and self.id.

classmethod supplementary_injection_keys(k)

Returns an iteration of InjectionKeys that should be added to an injector when this class is added. The current injection key is taken as an argument so that constraints applied can modify what keys are added.

class carthage.oci.OciManaged(*, readonly=None, **kwargs)
property deployable_names

Uses each of self.deployable_name_prefixes as a name_type for self.name and self.id.

async find()

Returns falsy if the object does not exist. Ideally returns the creation time in unix time, otherwise returns True if the creation time cannot be determined.

class carthage.oci.OciMount(destination: str, source: str = None, options: str = '', mount_type: str = 'volume')

Represents a mount for a container.

  • If source is a string, config and environment variables will be substituted in it.

  • If source is an InjectionKey, it will be instantiated.

  • Container implementations may permit a source to be a managed object. For example carthage.podman.PodmanContainer permits source to be a carthage.podman.PodmanVolume.

classmethod default_class_injection_key()

Called by Injector.add_provider() in the single argument form to get the injection key to use when this class is added to provide a dependency.

default_instance_injection_key()

Called when an instance of an injectable is used to add a dependency provider bby the single argument form of Injectable.add_provider()

class carthage.oci.OciPod(name=None, id=None, **kwargs)
exposed_ports()

Return a sequence of OciExposedPort for any container ports that should be exposed.

By default, instantiate all OciExposedPort instances in the injector.

id: str = None

the ID of the pod

name: str = None

The name of the pod

carthage.oci.oci_container_image = InjectionKey('oci/container_image')

This key provides a string whose result is the container image name to pull or an image ID that is locally available. Examples include docker.io/library/debian:latest or debian:unstable

Podman

Podman is a container frontend that will realize OCI containers on a single system. Currently Carthage only supports Podman running on the local system through LocalPodmanContainerHost. Long term, support is expected for remote Podman as well.

class carthage.podman.ContainerfileImage(container_context=None, **kwargs)

Build an image using podman build from a context directory with a Containerfile.

Parameters:
  • container_context – A directory with a Containerfile and potentially other files used by the Containerfile. This can be specified either in a call to the constructor or in a subclass definition. In the constructor, this is resolved relative to the current directory. In a subclass, this is resolved relative to the package (or module) in which the class is defined.

  • build_args – A dictionary; keys will be the name of ARGs and

the value will be the value passed in with --build-arg. The dictionary will be run through resolve_deferred. As a special case, if one of the values is an OciImage, then value of the build_arg will be the image tag of that image. This can be used for handling base images.

This class does respect OciMount and OciEnviron in the injector hierarchy.

async find()

Returns falsy if the object does not exist. Ideally returns the creation time in unix time, otherwise returns True if the creation time cannot be determined.

podman_options = ()

Options to pass to podman

property stamp_subdir

The part of stamp_path() after config.stamp_dir. For example for a podman container called example.com this might be podman/containers/example.com

class carthage.podman.ContainerfileImageModel(container_context=None, **kwargs)
class carthage.podman.LocalPodmanContainerHost(**kwargs)
filesystem_access(*args)

Gain filesystem access to a podman resource. Arguments are passed to podman; for things to work needs to be a container or volume mount. If both filesystem_access_container and filesystem_access_volume are defined (and do not call this method), subclasses need not implement this method.

tar_volume_context(volume)

An asynchronous context manager that tars up a volume and provides a path to that tar file usable in podman import. Typical usage:

async with container_host.tar_volume_context(container_image) as path:
    await container_host.podman('import', path)

On local systems this manages temporary directories. For remote container hosts, this manages to get the tar file to the remote system and clean up later.

class carthage.podman.LocalPodmanSocket(**kwargs)

Use the podman socket in /run/podman.sock or $XDG_RUNTIME_DIR. Requires that containers have a sftp server installed, but avoids the need for podman unshare.

filesystem_access_container(container_name)

an asynchronous context for accessing the filesystem of a container.

filesystem_access_volume(volume_name)

An asynchronous context manager for accessing the filesystem of a volume

class carthage.podman.PodmanContainer(**kwargs)

An OCI container implemented using podman. While it is possible to set up a container to be accessible via ssh and to meet all the interfaces of SshMixin, this is relatively uncommon. Such containers often have an entry point that is not a full init, and only run one service or program. Typically container_exec() is used to execute an additional command in the scope of a container rather than using ssh().

check_stamp(stamp, raise_on_error=False)
Returns:

a tuple containing the unix time of the stamp and the text contents of the stamp. The first element of the tuple is False if the stamp does not exist

container_exec(*args, _user=None, _fg=False)

‘Execute a command in a running container and return stdout. This function intentionally has a differentname than carthage.container.Container.container_command() because that method does not expect the container to be running.

filesystem_access(user='root')

An asynchronous context manager that makes the filesystem of the Machine available on a local path.

Returns:

Path at which the filesystem can be accessed while in the context.

async find()

Returns falsy if the object does not exist. Ideally returns the creation time in unix time, otherwise returns True if the creation time cannot be determined.

async is_machine_running()
Returns:

Whether the machine is running

Probe whether the machine is running and set self.running appropriately. It is most important that running be set accurately when start_machine() and stop_machine() can start or stop the machine. For BareMetalMachine it is reasonable to assume that the machine is running. This interface point should not call ssh_online() or confirm the machine is on the network.

machine_running_ssh_online: bool = False

Should machine_running default to calling ssh_online

podman_options()

Extra options to be passed into podman create as a list

podman_restart = 'no'

restart containers (no, always, on-failure)

rsync_uses_filesystem_access: bool = True

If true, use self.filesystem_access for rsync, otherwise use ssh.

run_command(*args, _user=None, _fg=False)

‘Execute a command in a running container and return stdout. This function intentionally has a differentname than carthage.container.Container.container_command() because that method does not expect the container to be running.

shell(*args, _user=None, _fg=False)

‘Execute a command in a running container and return stdout. This function intentionally has a differentname than carthage.container.Container.container_command() because that method does not expect the container to be running.

ssh_port: int

The port on which to connect to for ssh

stamp_subdir()

The part of stamp_path() after config.stamp_dir. For example for a podman container called example.com this might be podman/containers/example.com

async start_machine()

Must be overridden. Start the machine.

async stop_machine()

Must be overridden; stop the machine.

stop_timeout = 10

Timeout in seconds to wait when stopping a container

class carthage.podman.PodmanFromScratchImage(**kwargs)
class carthage.podman.PodmanImage(**kwargs)

Represents an OCI container image and provides facilities for building the image.

customizations can be turned into image layers using the image_layer_customization() function. Note that setup_tasks are only run when images are actually built. By default, the image is only built if it does not exist, although see should_build() to override.

async async_ready()

This may need to be overridden, but is provided as a default

async delete()

Try deleting an image. If we get an error (probably because it is in use by a container), untag instead.

async deploy()

Bring image to ready, and if nonlocal and pushable, push.

async find()

Returns falsy if the object does not exist. Ideally returns the creation time in unix time, otherwise returns True if the creation time cannot be determined.

async find_or_create()

See if image exists otherwise rebuild the image. Note that this is not a setup_task() even though it is in the parent. This is always run from async_ready()

image_layer_context(commit_message='')

Generate a container to produce a new image layer:

  • The image of the container will be either self.last_layer or self.base_image if last_layer is not set.

  • The container will be a PodmanImageBuilderContainer, and as such will simply pause when started so that container_exec() can be used to run commands in the container.

Usage:

async with self.image_layer_context() as layer_container:
    # Apply customizations/run commands in layer_container
#Now, self.last_layer is the image ID of the new layer
async should_build()

If the image exists, this is called. If it returns True, then the image will be rebuilt even though it exists. If a caller wants to force a rebuild, build_image() can also be called.

Local images that exist are not rebuilt by default. Remote images are rebuilt under the assumption that if building is enabled at all, the goal is to build and push a new image.

Images may overide if they have a good way to tell if an image is out of date.

stamp_subdir()

The part of stamp_path() after config.stamp_dir. For example for a podman container called example.com this might be podman/containers/example.com

classmethod supplementary_injection_keys(k)

Returns an iteration of InjectionKeys that should be added to an injector when this class is added. The current injection key is taken as an argument so that constraints applied can modify what keys are added.

class carthage.podman.PodmanImageModel(**kwargs)

Like a PodmanImage excetp:

  • This is an InjectableModel so modeling language constructs can be used

  • Any FilesystemCustomization or ContainerCustomization that are registered with the injector are automatically treated as image layers after any explicit setup_tasks.

class carthage.podman.PodmanPod(**kwargs)
async find()

Returns falsy if the object does not exist. Ideally returns the creation time in unix time, otherwise returns True if the creation time cannot be determined.

podman_pod_options = []

A list of extra options to pass to pod create

class carthage.podman.PodmanPodModel(**kwargs)

A container that can group a number of MachineModels representing Podman containers.

  • By default, machine_implementation_key within a PodmanPodModel is PodmanContainer.

  • By default, when added to an enclosing injector, this model does not provide PodmanPod in that context, although it does provide PodmanPod within its own injector. An example illustrates:

    class Layout(CarthageLayout):
    
        #Even though this layout includes  PodmanPods, PodmanPod is not provided in the injector of the layout.  That means containers outside of a PodmanPodModel will not be associated with a pod by default
    
        class pod1(PodmanPodModel):
    
            # These containers are within pod1 and because within its own scope pod1 provides PodmanPod, the containers will be added to the pod.
    
            class container1(MachineModel): pass
    
            class container2(MachineModel): pass
    
        # But this container is not in a pod.
    
        class container3(MachineModel): pass
    

In contrast to using PodmanPodModel, PodmanPod can be used along side containers in another modeling grouping. When used this way PodmanPod does provide PodmanPod in the enclosing injector:

class layout(CarthageLayout):

    class group1(ModelGroup):

        # Everything in this group is pod1

        class pod1(PodmanPod): name = 'pod1'

        class container1(MachineModel): pass
async resolve_networking(force: bool = False)

Like carthage.machine.NetworkedModel.resolve_networking() except that it looks for oci_container_network_config. If that key is present, that network config is used instead of InjectionKey(NetworkConfig). Doing so allows containers that are lexically contained in their host to have their own NetworkConfig.

classmethod supplementary_injection_keys(k)

Returns an iteration of InjectionKeys that should be added to an injector when this class is added. The current injection key is taken as an argument so that constraints applied can modify what keys are added.

class carthage.podman.PodmanVolume(name: str = None, **kwargs)
filesystem_access()

Like carthage.Machine.filesystem_access() except gains access to a podman volume.

Usage:

async with volume.filesystem_access() as path:
     # Path points to a mount for the volume inside the context manager.
async find()

Returns falsy if the object does not exist. Ideally returns the creation time in unix time, otherwise returns True if the creation time cannot be determined.

stamp_subdir()

The part of stamp_path() after config.stamp_dir. For example for a podman container called example.com this might be podman/containers/example.com

class carthage.podman.RemotePodmanHost(machine, user=None, **kwargs)
property extra_args

Tell Ansible about container_host

filesystem_access(*args)

Gain filesystem access to a podman resource. Arguments are passed to podman; for things to work needs to be a container or volume mount. If both filesystem_access_container and filesystem_access_volume are defined (and do not call this method), subclasses need not implement this method.

async find()

Return True if self.machine is found as a deployable.

async podman_nosocket(*args, _log=True, **kwargs)

Run podman directly on the container host rather than using a podman socket. Used for example for container commits because for example podman 4.9 does not appear to understand the commit results from podman 4.6.

tar_volume_context(volume)

An asynchronous context manager that tars up a volume and provides a path to that tar file usable in podman import. Typical usage:

async with container_host.tar_volume_context(container_image) as path:
    await container_host.podman('import', path)

On local systems this manages temporary directories. For remote container hosts, this manages to get the tar file to the remote system and clean up later.

carthage.podman.image_layer_task(customization)

Wrap a BaseCustomization as a layer in a PodmanImage.