service_mesh

Service mesh interface library.

This library facilitates adding your charmed application to a service mesh, leveraging the service_mesh and cross_model_mesh interfaces to provide secure, policy-driven traffic management between applications.

What is this library for?

This library is for enrolling a charm onto a Charmed Service Mesh solution and automatically provisioning network policies that restrict cluster-internal network traffic between charms.

Service meshes provide capabilities for routing, controlling, and monitoring traffic between applications. A key feature is the ability to enforce authorization policies that govern which pods can communicate with each other and on which ports, paths, and HTTP methods. For example, you can define that a metrics scraper pod is allowed to GET /metrics on port 9090 from a producer pod, while preventing all other pods from accessing it.

The ServiceMeshConsumer subscribes a charm to a related service mesh by declaring access policies based on the charm’s Juju relations. Since application relations often reflect traffic flow patterns (e.g. a database consumer connecting to a database provider), the consumer automatically generates the appropriate mesh traffic rules. It also handles labelling the charm’s Kubernetes resources to enroll them in the mesh, and supports cross-model relations for multi-model deployments.

The ServiceMeshProvider publishes mesh enrollment labels and the mesh type to consumers, and collects the aggregated policies requested by all related consumer charms so the mesh control plane can enforce them.

Consumer usage:

from charmlibs.interfaces.service_mesh import (
    Method, Endpoint, AppPolicy, UnitPolicy, ServiceMeshConsumer,
)

class MyCharm(CharmBase):
    def __init__(self, *args):
        super().__init__(*args)
        self._mesh = ServiceMeshConsumer(
            self,
            policies=[
                AppPolicy(
                    relation="data",
                    endpoints=[
                        Endpoint(
                            ports=[HTTP_LISTEN_PORT],
                            methods=[Method.get],
                            paths=["/data"],
                        ),
                    ],
                ),
                UnitPolicy(relation="metrics", ports=[HTTP_LISTEN_PORT]),
            ],
        )

Provider usage:

from charmlibs.interfaces.service_mesh import ServiceMeshProvider, MeshType

class MyServiceMeshCharm(CharmBase):
    def __init__(self, *args):
        super().__init__(*args)
        self._mesh = ServiceMeshProvider(
            charm=self,
            labels={"istio.io/dataplane-mode": "ambient"},
            mesh_type=MeshType.istio,
        )
class AppPolicy(
*,
relation: str,
endpoints: list[Endpoint],
service: str | None = None,
)

Bases: BaseModel

Data type for defining a policy for your charm application.

relation: str
endpoints: list[Endpoint]
service: str | None
class CMRData(*, app_name: str, juju_model_name: str)

Bases: BaseModel

Data type containing the info required for cross-model relations.

app_name: str
juju_model_name: str
class Endpoint(
*,
hosts: list[str] | None = None,
ports: list[int] | None = None,
methods: list[Method] | None = None,
paths: list[str] | None = None,
)

Bases: BaseModel

Data type for a policy endpoint.

hosts: list[str] | None
ports: list[int] | None
methods: list[Method] | None
paths: list[str] | None
class MeshPolicy(
*,
source_namespace: str,
source_app_name: str,
target_namespace: str,
target_app_name: str | None = None,
target_selector_labels: dict[str,
str] | None=None,
target_service: str | None = None,
target_type: PolicyTargetType = PolicyTargetType.app,
endpoints: list[Endpoint] = <factory>,
)

Bases: BaseModel

A generic MeshPolicy data type that describes mesh policies.

This is agnostic to the mesh type and defines a standard interface for charmed mesh managed policies.

source_namespace: str
source_app_name: str
target_namespace: str
target_app_name: str | None
target_selector_labels: dict[str, str] | None
target_service: str | None
target_type: PolicyTargetType
endpoints: list[Endpoint]
class MeshType(*values)

Bases: str, Enum

Supported service mesh types.

istio = 'istio'
class Method(*values)

Bases: str, Enum

HTTP method.

connect = 'CONNECT'
delete = 'DELETE'
get = 'GET'
head = 'HEAD'
options = 'OPTIONS'
patch = 'PATCH'
post = 'POST'
put = 'PUT'
trace = 'TRACE'
class Policy(
*,
relation: str,
endpoints: list[Endpoint],
service: str | None = None,
)

Bases: BaseModel

Data type for defining a policy for your charm.

Deprecated since version Use: AppPolicy for fine-grained application-level policies or UnitPolicy to allow access to charm units.

relation: str
endpoints: list[Endpoint]
service: str | None
class PolicyTargetType(*values)

Bases: str, Enum

Target type for policy classes.

app = 'app'
unit = 'unit'
class ServiceMeshConsumer(
charm: CharmBase,
mesh_relation_name: str = 'service-mesh',
cross_model_mesh_requires_name: str = 'require-cmr-mesh',
cross_model_mesh_provides_name: str = 'provide-cmr-mesh',
policies: list[Policy | AppPolicy | UnitPolicy] | None = None,
auto_join: bool = True,
)

Bases: Object

Class used for joining a service mesh.

update_service_mesh()

Update the service mesh.

Gathers information from all relations of the charm and updates the mesh appropriately to allow communication.

labels() dict[str, str]

Labels required for a pod to join the mesh.

property enabled: bool

Return if the consumer is currently in the mesh.

mesh_type() MeshType | None

Return the type of the service mesh.

property lightkube_client: Client

Returns a lightkube client configured for this library.

class ServiceMeshProvider(
charm: CharmBase,
labels: dict[str, str],
mesh_type: MeshType,
mesh_relation_name: str = 'service-mesh',
)

Bases: Object

Provide a service mesh to applications.

update_relations()

Update all relations with the labels needed to use the mesh.

mesh_info() list[MeshPolicy]

Return the relation data that defines Policies requested by the related applications.

class ServiceMeshProviderAppData(
*,
labels: dict[str, str],
mesh_type: MeshType,
)

Bases: BaseModel

Data provided by the provider side of the service-mesh interface.

labels: dict[str, str]
mesh_type: MeshType
class UnitPolicy(*, relation: str, ports: list[int] | None = None)

Bases: BaseModel

Data type for defining a policy for your charm unit.

relation: str
ports: list[int] | None
build_mesh_policies(
relation_mapping: RelationMapping,
target_app_name: str,
target_namespace: str,
policies: list[Policy | AppPolicy | UnitPolicy],
cmr_application_data: dict[str, CMRData] | None = None,
) list[MeshPolicy]

Generate MeshPolicy objects for the currently related applications.

Parameters:
  • relation_mapping – Charm’s RelationMapping object, for example self.model.relations.

  • target_app_name – The name of the target application, for example self.app.name.

  • target_namespace – The namespace of the target application, for example self.model.name.

  • policies – List of AppPolicy, or UnitPolicy objects defining the access rules.

  • cmr_application_data – Data for cross-model relations, mapping app names to CMRData.

get_data_from_cmr_relation(
cmr_relations: list[Relation],
) dict[str, CMRData]

Return a dictionary of CMRData from the established cross-model relations.