Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 24 additions & 12 deletions .agents/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
# Architecture (Agent Reference)

Read this before making changes to `ControlInterface`, `Ankaios`, the
protocol layer, or exception handling.

## Control Interface

The SDK communicates with the Ankaios agent via a Unix socket at
`/run/ankaios/control_interface` (two FIFOs: `input` and `output`). Messages
are length-delimited protobuf (`_control_api` wrapping `_ank_base`).

`ControlInterface` runs a background reader thread that deserializes
incoming messages and dispatches them to `Ankaios` via callbacks. `Ankaios`
routes responses to the correct caller using a request-ID queue.
Read this before making changes to `ControlInterfaceConnection`,
`CommandInterfaceConnection`, `Ankaios`, the protocol layer, or exception
handling.

## Connections

`Ankaios` talks to Ankaios through one of two interchangeable connections,
both implementing the `Connection` abstract base class
(`ankaios_sdk/_components/connection/connection.py`), picked via
`ConnectionType` at construction time:

- **Control Interface** (`ConnectionType.CONTROL_INTERFACE`, default) — used
from inside an Ankaios-managed workload. Communicates via named pipes at
`/run/ankaios/control_interface` (two FIFOs: `input` and `output`).
Messages are length-delimited protobuf (`_control_api` wrapping
`_ank_base`). Implemented by `ControlInterfaceConnection`.
- **Command Interface** (`ConnectionType.COMMAND_INTERFACE`) — used from
outside a workload, connecting directly to the Ankaios server over gRPC.
Only available if the SDK was installed with the `command` extra. Implemented
by `CommandInterfaceConnection`.

Both run a background reader thread that deserializes incoming messages and
dispatches them to `Ankaios` via callbacks. `Ankaios` routes responses to the
correct caller using a request-ID queue.

`Ankaios` is the primary entry point, typically used as a context manager:

Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ Read these only when they apply to the task at hand:
before making any code or test change: API compatibility, coverage
philosophy, lint/PEP 8 enforcement, generated proto file handling.
- [.agents/ARCHITECTURE.md](.agents/ARCHITECTURE.md) — required before
touching `ControlInterface`, `Ankaios`, the protocol layer, or exception
handling.
touching `ControlInterfaceConnection`, `CommandInterfaceConnection`,
`Ankaios`, the protocol layer, or exception handling.
9 changes: 5 additions & 4 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ The [tools/](tools/) folder contains helper scripts for specific tasks — see [
- Tests mirror the `ankaios_sdk/_components/` structure
- Use `unittest.mock.patch` / `MagicMock` for all external dependencies
- Each test module exports a `generate_test_<thing>()` helper for fixtures
- Accessing private members in tests (e.g. `ankaios._control_interface`) is normal
- Accessing private members in tests (e.g. `ankaios._connection`) is normal

**Typical test setup pattern:**

Expand All @@ -56,12 +56,13 @@ from unittest.mock import patch, PropertyMock
from ankaios_sdk import Ankaios, ControlInterfaceState

def generate_test_ankaios() -> Ankaios:
with patch("ankaios_sdk.ControlInterface.connect"), patch(
"ankaios_sdk.ControlInterface.connected", new_callable=PropertyMock
with patch("ankaios_sdk.ControlInterfaceConnection.connect"), patch(
"ankaios_sdk.ControlInterfaceConnection.connected",
new_callable=PropertyMock,
) as mock_connected:
mock_connected.return_value = True
ankaios = Ankaios()
ankaios._control_interface._state = ControlInterfaceState.CONNECTED
ankaios._connection._state = ControlInterfaceState.CONNECTED
return ankaios
```

Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ are using. For information regarding versioning, please refer to this table:
After installation, you can use the Ankaios SDK to configure and run workloads
and request the state of the Ankaios system and the connected agents.

### Connecting over The Control Interface

The following example assumes that the code is running in a managed by
Ankaios workload with configured control interface access:

Expand Down Expand Up @@ -134,6 +136,33 @@ with Ankaios() as ankaios:
[workload_name][workload_id].state))
```

### Connecting over The Command Interface

To connect to an Ankaios server directly from outside a workload (e.g. from
a CI job or a management tool), use the Command Interface instead, which
uses a direct gRPC connection:

```python
from ankaios_sdk import Ankaios, ConnectionType

with Ankaios(
connection_type=ConnectionType.COMMAND_INTERFACE,
server_url="http://127.0.0.1:25551",
) as ankaios:
...
```

This requires the `command` extra:

```sh
pip install ankaios-sdk[command]
```

For mTLS-secured connections, also pass `ca_pem`, `crt_pem` and `key_pem`
(the PEM-encoded CA certificate, client certificate and client key content).

### Resources

For more details, please visit:

* [Ankaios documentation](https://eclipse-ankaios.github.io/ankaios/latest/)
Expand Down
2 changes: 1 addition & 1 deletion ankaios_sdk/_components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
from .manifest import *
from .log_campaign import *
from .event_campaign import *
from .control_interface import *
from .connection import *
from .file import *

__all__ = [name for name in globals() if not name.startswith("_")]
50 changes: 50 additions & 0 deletions ankaios_sdk/_components/connection/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) 2026 Elektrobit Automotive GmbH
#
# This program and the accompanying materials are made available under the
# terms of the Apache License, Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0.
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# SPDX-License-Identifier: Apache-2.0

"""
This module initializes the connection package by importing the
Connection abstraction and its interface implementations.

Imports
-------

- Connection component:
the abstract base class for a connection to Ankaios.
- ControlInterfaceConnection component:
the control interface (named pipes) implementation of Connection.
- CommandInterfaceConnection component:
the command interface (gRPC server interface) implementation of
Connection. Only available if the 'grpc' extra is installed.
"""

import types

from .connection import *
from .control_interface import *

try:
from .command_interface import *
except ImportError:
# The 'grpc' extra is not installed; CommandInterfaceConnection
# stays unavailable, but the rest of the SDK must still work.
Comment thread
christoph-hamm marked this conversation as resolved.
pass

# A submodule sharing its name with this package (connection/connection.py)
# gets bound as an attribute of the package itself by Python's import
# system. This is not desired, so we remove it from the package's namespace.
__all__ = [
name
for name, value in globals().items()
if not name.startswith("_") and not isinstance(value, types.ModuleType)
]
Loading
Loading