certificate_transfer

Transfer x.509 certificates using the certificate-transfer interface (V1).

This is a port of certificate_transfer_interface.certificate_transfer v1.15.

This library contains the Requires and Provides classes for handling the certificate-transfer interface. It supports both v0 and v1 of the interface.

For requirers, they will set version 1 in their application databag as a hint to the provider. They will read the databag from the provider first as v1, and fallback to v0 if the format does not match.

For providers, they will check the version in the requirer’s application databag, and send v1 if that version is set to 1, otherwise it will default to 0 for backwards compatibility.

Getting Started

From a charm directory, fetch the library using charmcraft:

charmcraft fetch-lib charms.certificate_transfer_interface.v1.certificate_transfer

Provider charm

The provider charm is the charm providing public certificates to another charm that requires them.

Example:

from ops.charm import CharmBase, RelationJoinedEvent
from ops.main import main

from lib.charms.certificate_transfer_interface.v1.certificate_transfer import (
    CertificateTransferProvides,
)

class DummyCertificateTransferProviderCharm(CharmBase):
    def __init__(self, *args):
        super().__init__(*args)
        self.ct = CertificateTransferProvides(self, "certificates")
        self.framework.observe(
            self.on.certificates_relation_joined, self._on_certificates_relation_joined
        )

    def _on_certificates_relation_joined(self, event: RelationJoinedEvent):
        certificate = "my certificate"
        self.ct.add_certificates(certificate)


if __name__ == "__main__":
    main(DummyCertificateTransferProviderCharm)

Requirer charm

The requirer charm is the charm requiring certificates from another charm that provides them.

Example:

import logging

from ops.charm import CharmBase
from ops.main import main

from lib.charms.certificate_transfer_interface.v1.certificate_transfer import (
    CertificatesAvailableEvent,
    CertificatesRemovedEvent,
    CertificateTransferRequires,
)


class DummyCertificateTransferRequirerCharm(CharmBase):
    def __init__(self, *args):
        super().__init__(*args)
        self.ct = CertificateTransferRequires(self, "certificates")
        self.framework.observe(
            self.ct.on.certificate_set_updated, self._on_certificates_available
        )
        self.framework.observe(
            self.ct.on.certificates_removed, self._on_certificates_removed
        )

    def _on_certificates_available(self, event: CertificatesAvailableEvent):
        logging.info(event.certificates)
        logging.info(event.relation_id)

    def _on_certificates_removed(self, event: CertificatesRemovedEvent):
        logging.info(event.relation_id)


if __name__ == "__main__":
    main(DummyCertificateTransferRequirerCharm)

You can integrate both charms by running:

juju integrate <certificate_transfer provider charm> <certificate_transfer requirer charm>
class CertificateTransferProvides(charm: CharmBase, relationship_name: str)

Bases: Object

Certificate Transfer provider class to be instantiated by charms sending certificates.

add_certificates(
certificates: set[str],
relation_id: int | None = None,
) None

Add certificates from a set to relation data.

Adds certificate to all relations if relation_id is not provided.

Parameters:
  • certificates (Set[str]) – A set of certificate strings in PEM format

  • relation_id (int) – Juju relation ID

Returns:

None

remove_all_certificates(
relation_id: int | None = None,
) None

Remove all certificates from relation data.

Removes all certificates from all relations if relation_id not given

Parameters:

relation_id (int) – Relation ID

Returns:

None

remove_certificate(
certificate: str,
relation_id: int | None = None,
) None

Remove a given certificate from relation data.

Removes certificate from all relations if relation_id not given

Parameters:
  • certificate (str) – Certificate in PEM format that’s in the list

  • relation_id (int) – Relation ID

Returns:

None

class CertificateTransferRequires(charm: CharmBase, relationship_name: str)

Bases: Object

Certificate transfer requirer class to be instantiated by charms expecting certificates.

on

List of events that the Certificate Transfer requirer charm can leverage.

get_all_certificates(
relation_id: int | None = None,
) set[str]

Get transferred certificates.

If no relation id is given, certificates from all relations will be provided in a concatenated list.

Parameters:

relation_id – The id of the relation to get the certificates from.

get_all_certificates_by_relation(
relation_id: int | None = None,
) dict[int, list[str]]

Get a deterministic list of certificates grouped by relation.

  • Grouped by relation_id.

  • The list order is sorted lexicographically by PEM content.

Parameters:

relation_id – If provided, only certificates for this relation are returned.

Returns:

Dict where keys are relation IDs and values are ordered lists of PEMs.

is_ready(relation: Relation) bool

Check if the relation is ready by checking that it has valid relation data.

class CertificatesAvailableEvent(
handle: Handle,
certificates: set[str],
relation_id: int,
)

Bases: EventBase

Charm Event triggered when the set of provided certificates is updated.

snapshot() dict

Return snapshot.

restore(snapshot: dict)

Restores snapshot.

class CertificatesRemovedEvent(handle: Handle, relation_id: int)

Bases: EventBase

Charm Event triggered when the set of provided certificates is removed.

snapshot() dict

Return snapshot.

restore(snapshot: dict)

Restores snapshot.

exception DataValidationError

Bases: TLSCertificatesError

Raised when data validation fails.

exception TLSCertificatesError

Bases: Exception

Base class for custom errors raised by this library.