Skip to content
Open
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/vendor/
/.idea/
.phpunit.result.cache
*.phpdisabled
*.phpdisabled
.DS_Store
58 changes: 29 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# Utopia Orchestration
# Utopia Containers

[![Build Status](https://app.travis-ci.com/utopia-php/orchestration.svg?branch=main)](https://app.travis-ci.com/github/utopia-php/orchestration)
![Total Downloads](https://img.shields.io/packagist/dt/utopia-php/orchestration.svg)
[![Build Status](https://app.travis-ci.com/utopia-php/containers.svg?branch=main)](https://app.travis-ci.com/github/utopia-php/containers)
![Total Downloads](https://img.shields.io/packagist/dt/utopia-php/containers.svg)
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord)](https://appwrite.io/discord)

Utopia framework orchestration library is simple and lite library for abstracting the interaction with multiple container orchestrators. This library is aiming to be as simple and easy to learn and use. This library is maintained by the [Appwrite team](https://appwrite.io).
Utopia framework containers library is simple and lite library for abstracting the interaction with multiple container orchestrators. This library is aiming to be as simple and easy to learn and use. This library is maintained by the [Appwrite team](https://appwrite.io).

Although this library is part of the [Utopia Framework](https://github.com/utopia-php/framework) project it is dependency free and can be used as standalone with any other PHP project or framework.
Comment on lines +7 to 9
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor language fixes in introduction

Two small text issues flagged by tooling:

  • “it is dependency free and can be used as standalone” → “it is dependency‑free and can be used as a standalone library”.
  • Consider re‑reading the surrounding paragraph for typos after the DockerCLI section; the linter flagged an orthography issue there.

Not critical, but easy polish for the new package branding.

🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Use a hyphen to join words.
Context: ...-php/framework) project it is dependency free and can be used as standalone with ...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
In README.md around lines 7 to 9, update the phrasing to fix minor language
issues: change “it is dependency free and can be used as standalone” to “it is
dependency‑free and can be used as a standalone library”, and review the
surrounding paragraph (especially after the DockerCLI section) for any remaining
typos or orthography issues and correct them for consistency and clarity.


## Getting Started

Install using composer:
```bash
composer require utopia-php/orchestration
composer require utopia-php/containers
```
Comment on lines +7 to 16
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

README still documents v1 pull() and run() semantics

There are a couple of important drifts between the README and the v2 code:

  • The “Pulling an image” section (Lines 75–80) still says the method “will return a boolean value indicating if the image was pulled successfully,” but the new Containers/Adapter contract is moving toward a void/exception model while DockerAPI::pull() still returns bool. This needs to be clarified once you settle on the final contract.
  • The “Running a container” example (Lines 95–112) continues to use the old positional run signature (image, name, command, entrypoint, workdir, volumes, env, mountFolder, labels, hostname, remove), while the new adapter contract uses run(string $name, string $image, string $hostname, array $command, array $entrypoint, ?string $workdir, array $environment, array $mounts, array $labels, ?string $network, ?float $cpus, ?int $memory, ?int $swap, RestartPolicy $restart, bool $remove) and Mount objects instead of raw volume strings.

Once the v2 API is finalized, please update these sections to match the actual Containers::pull() and Containers::run() signatures and semantics (including examples using Bind/Volume/Tmpfs and RestartPolicy).

Also applies to: 75-112


I'll verify the review comment by checking the actual README content and comparing it with the current API implementation.



🏁 Script executed:

#!/bin/bash
# Read the full README to see lines 75-112
cat README.md | head -120 | tail -50

Length of output: 1208


🏁 Script executed:

#!/bin/bash
# Search for Containers class and pull/run method signatures
find . -type f -name "*.php" | head -20

Length of output: 789


🏁 Script executed:

#!/bin/bash
# Look for the main Containers class file
fd -t f "\.php$" -x grep -l "class Containers" {} \;

Length of output: 314


🏁 Script executed:

#!/bin/bash
# Search for RestartPolicy, Mount, Volume classes
rg -nP "class (RestartPolicy|Mount|Volume|Tmpfs|Bind)" --type php

Length of output: 236


🏁 Script executed:

#!/bin/bash
# Find method signatures for pull and run
rg -nP "function (pull|run)\s*\(" --type php -A 3 -B 1

Length of output: 2371


I need to see the complete run() method signature to verify the parameter structure.



🏁 Script executed:

#!/bin/bash
# Get full run() signature from Containers.php
sed -n '74,95p' src/Containers/Containers.php

Length of output: 850


🏁 Script executed:

#!/bin/bash
# Get full run() signature from Adapter.php
sed -n '40,65p' src/Containers/Adapter.php

Length of output: 660


🏁 Script executed:

#!/bin/bash
# Check RestartPolicy class
cat src/Containers/RestartPolicy.php

Length of output: 193


🏁 Script executed:

#!/bin/bash
# Check Mount interface
cat src/Containers/Mount.php

Length of output: 137


🏁 Script executed:

#!/bin/bash
# Check Bind class structure
cat src/Containers/Mount/Bind.php

Length of output: 411


🏁 Script executed:

#!/bin/bash
# Get full DockerAPI run() signature for comparison
sed -n '486,510p' src/Containers/Adapter/DockerAPI.php

Length of output: 847


README documentation drift confirmed; also note DockerAPI implementation inconsistency

The review comment is correct. The README still documents the v1 API while the Containers/Adapter contract has evolved to v2:

  • Lines 75–80 claim pull() "will return a boolean value," but Containers::pull(string): void and Adapter::pull(string): void now return nothing (throws exceptions on error).
  • Lines 95–112 use the old run() signature with positional parameters (image, name, command, entrypoint, workdir, volumes, env, mountFolder, labels, hostname, remove) and raw volume strings. The new contract is run(string $name, string $image, string $hostname, array $command, array $entrypoint, ?string $workdir, array $environment, array $mounts, array $labels, ?string $network, ?float $cpus, ?int $memory, ?int $swap, RestartPolicy $restart, bool $remove): string, which uses Mount objects (Bind, Volume, Tmpfs) for $mounts and RestartPolicy enum for $restart.

Also note: DockerAPI implementation (src/Containers/Adapter/DockerAPI.php) does not conform to the Adapter contract—it still has the old signatures (pull() returns bool, run() takes image as the first parameter instead of name). This incomplete refactoring should be resolved alongside the README update.

Update README lines 75–112 to document the current v2 API, and ensure DockerAPI implements the Adapter contract correctly.

🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Use a hyphen to join words.
Context: ...-php/framework) project it is dependency free and can be used as standalone with ...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
README.md lines 7-16: the README documents the old v1 Containers API and
mismatches the current v2 contract; update the README to reflect v2 signatures:
note that pull(string) now returns void and throws on error (remove any boolean
return descriptions), replace the old run(...) positional signature with the v2
definition run(string $name, string $image, string $hostname, array $command,
array $entrypoint, ?string $workdir, array $environment, array $mounts, array
$labels, ?string $network, ?float $cpus, ?int $memory, ?int $swap, RestartPolicy
$restart, bool $remove): string and describe that mounts are Mount objects
(Bind, Volume, Tmpfs) and restart is a RestartPolicy enum, update all
examples/snippets to use named/typed parameters and Mount objects and to expect
a string container id on success; concurrently fix
src/Containers/Adapter/DockerAPI.php to match the Adapter contract (make pull
return void and throw on error, change run signature to accept name first and
the new typed parameters, accept Mount objects and RestartPolicy, and return the
container id string) so implementation and README are consistent.


### Example
Expand All @@ -21,26 +21,26 @@ composer require utopia-php/orchestration

require_once 'vendor/autoload.php';

use Utopia\Orchestration\Orchestration;
use Utopia\Orchestration\Adapter\DockerCLI;
use Utopia\Containers\Containers;
use Utopia\Containers\Adapter\DockerCLI;

// Initialise Orchestration with Docker CLI adapter.
$orchestration = new Orchestration(new DockerCLI());
// Initialise Containers with Docker CLI adapter.
$containers = new Containers(new DockerCLI());

// Pull the image.
$orchestration->pull('ubuntu:latest');
$containers->pull('ubuntu:latest');

// Launch a ubuntu container that doesn't end using the tail command.
$containerID = $orchestration->run('ubuntu:latest', 'testContainer', ['tail', '-f', '/dev/null']);
$containerID = $containers->run('ubuntu:latest', 'testContainer', ['tail', '-f', '/dev/null']);

$stderr = '';
$stdout = '';

// Execute a hello world command in the container
$orchestration->execute($containerID, ['echo', 'Hello World!'], $stdout, $stderr);
$containers->execute($containerID, ['echo', 'Hello World!'], $stdout, $stderr);

// Remove the container forcefully since it's still running.
$orchestration->remove($containerID, true);
$containers->remove($containerID, true);
```

## Usage
Expand All @@ -53,30 +53,30 @@ There are currently two orchestrator adapters available and each of them has sli
Directly communicates to the Docker Daemon using the Docker UNIX socket.

```php
use Utopia\Orchestration\Orchestration;
use Utopia\Orchestration\Adapter\DockerAPI;
use Utopia\Containers\Containers;
use Utopia\Containers\Adapter\DockerAPI;

$orchestration = new Orchestration(new DockerAPI($username, $password, $email));
$containers = new Containers(new DockerAPI($username, $password, $email));
```
$username, $password and $email are optional and are only used to pull private images from Docker Hub.

- ### DockerCLI
Uses the Docker CLI to communicate to the Docker Daemon.
```php
use Utopia\Orchestration\Orchestration;
use Utopia\Orchestration\Adapter\DockerCLI;
use Utopia\Containers\Containers;
use Utopia\Containers\Adapter\DockerCLI;

$orchestration = new Orchestration(new DockerCLI($username, $password));
$containers = new Containers(new DockerCLI($username, $password));
```
$username and $password are optional and are only used to pull private images from Docker Hub.

Once you have initialised your Orchestration object the following methods can be used:
Once you have initialised your Containers object the following methods can be used:

- ### Pulling an image
This method pulls the image requested from the orchestrators registry. It will return a boolean value indicating if the image was pulled successfully.

```php
$orchestration->pull('image:tag');
$containers->pull('image:tag');
```

<details>
Expand All @@ -96,7 +96,7 @@ Once you have initialised your Orchestration object the following methods can be
This method creates and runs a new container. On success, it will return a string containing the container ID. On failure, it will throw an exception.

```php
$orchestration->run(
$containers->run(
'image:tag',
'name',
['echo', 'hello world!'],
Expand Down Expand Up @@ -219,7 +219,7 @@ Once you have initialised your Orchestration object the following methods can be
This method removes a container and returns a boolean value indicating if the container was removed successfully.

```php
$orchestration->remove('container_id', true);
$containers->remove('container_id', true);
```

<details>
Expand All @@ -243,7 +243,7 @@ Once you have initialised your Orchestration object the following methods can be
This method returns an array of containers.

```php
$orchestration->list(['label' => 'value']);
$containers->list(['label' => 'value']);
```

<details>
Expand All @@ -263,7 +263,7 @@ Once you have initialised your Orchestration object the following methods can be
This method returns an array of networks.

```php
$orchestration->listNetworks();
$containers->listNetworks();
```

<details>
Expand All @@ -281,7 +281,7 @@ Once you have initialised your Orchestration object the following methods can be
This method creates a new network and returns a boolean value indicating if the network was created successfully.

```php
$orchestration->createNetwork('name', false);
$containers->createNetwork('name', false);
```

<details>
Expand All @@ -305,7 +305,7 @@ Once you have initialised your Orchestration object the following methods can be
This method removes a network and returns a boolean value indicating if the network was removed successfully.

```php
$orchestration->removeNetwork('network_id');
$containers->removeNetwork('network_id');
```

<details>
Expand All @@ -325,7 +325,7 @@ Once you have initialised your Orchestration object the following methods can be
This method connects a container to a network and returns a boolean value indicating if the connection was successful.

```php
$orchestration->connect('container_id', 'network_id');
$containers->connect('container_id', 'network_id');
```

<details>
Expand All @@ -349,7 +349,7 @@ Once you have initialised your Orchestration object the following methods can be
This method disconnects a container from a network and returns a boolean value indicating if the removal was successful.

```php
$orchestration->disconnect('container_id', 'network_id', false);
$containers->disconnect('container_id', 'network_id', false);
```

<details>
Expand Down
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "utopia-php/orchestration",
"description": "Lite & fast micro PHP abstraction library for container orchestration",
"name": "utopia-php/containers",
"description": "Lite & fast micro PHP abstraction library for container containers",
"type": "library",
"keywords": ["php","framework", "upf", "utopia", "orchestration", "docker", "swarm", "kubernetes"],
"keywords": ["php","framework", "upf", "utopia", "containers", "docker", "swarm", "kubernetes"],
"license": "MIT",
Comment on lines +2 to 6
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor metadata fixes: description wording and consistency

The composer.json changes generally look good (namespace and test paths line up with the new Containers layout), but there is one small issue:

  • "description": "Lite & fast micro PHP abstraction library for container containers" has a duplicated word. Consider changing to something like "Lite & fast micro PHP abstraction library for containers" or "for container orchestration".

Otherwise the autoload mappings and keywords are consistent with the new namespace.

Also applies to: 14-19

🤖 Prompt for AI Agents
In composer.json around lines 2-6 (and similarly lines 14-19), the package
description contains a duplicated word "container containers"; update the
"description" value to remove the duplication and improve clarity (e.g., "Lite &
fast micro PHP abstraction library for containers" or "Lite & fast micro PHP
abstraction library for container orchestration") so the metadata reads cleanly
and consistently.

"minimum-stability": "stable",
"scripts": {
Expand All @@ -12,10 +12,10 @@
"format": "./vendor/bin/pint"
},
"autoload": {
"psr-4": {"Utopia\\Orchestration\\": "src/Orchestration"}
"psr-4": {"Utopia\\Containers\\": "src/Containers"}
},
"autoload-dev": {
"psr-4": {"Utopia\\Tests\\": "tests/Orchestration"}
"psr-4": {"Utopia\\Tests\\": "tests/Containers"}
},
"require": {
"php": ">=8.0",
Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ version: '3.8'
services:
tests:
container_name: tests
build:
build:
context: .
networks:
- orchestration
- containers
environment:
HOST_DIR: "$PWD" # Nessessary to mount test resources to child containers
volumes:
- ./:/usr/src/code
- /var/run/docker.sock:/var/run/docker.sock

networks:
orchestration:
containers:
68 changes: 68 additions & 0 deletions src/Containers/Adapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Utopia\Containers;

use Utopia\Containers\Usage;

abstract class Adapter
Comment on lines +3 to +7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Adapter contract mostly looks good, but clean up imports and ensure getUsage is implemented consistently

A few points on this new abstraction:

  • use Utopia\Containers\Usage; is redundant in the same namespace and flagged by Pint (no_unused_imports); you can drop the use and keep the unqualified Usage type hint.
  • getUsage(string $container): ?Usage is part of the contract, but DockerCLI::getUsage currently returns array and throws a placeholder \Exception('getUsage'). That will be incompatible with this signature and effectively unimplemented.
  • disconnect(string $container, string $network, bool $force) has no declared return type, but Networks::disconnect() returns bool based on the adapter result. For clarity, consider adding : bool here and in all implementations.

After fixing the import, aligning getUsage across adapters (returning a Usage|null or throwing on error), and making disconnect’s return type explicit, this interface will provide a solid v2 contract for adapters.

Also applies to: 19-31


🏁 Script executed:

#!/bin/bash
# View Adapter.php file to see full contract
cat src/Containers/Adapter.php

Length of output: 1785


🏁 Script executed:

#!/bin/bash
# Find and view DockerCLI implementation
find . -name "DockerCLI.php" -o -name "DockerAPI.php" | head -5

Length of output: 77


🏁 Script executed:

#!/bin/bash
# Find Networks class
find . -name "Networks.php" | head -5

Length of output: 29


🏁 Script executed:

#!/bin/bash
# Search for other adapter implementations
find . -path "*/Containers/*" -name "*.php" -type f | grep -i adapter

Length of output: 196


🏁 Script executed:

#!/bin/bash
# Check PHP version and Pint configuration for linting
ls -la | grep -E 'pint|phpstan|composer'

Length of output: 187


🏁 Script executed:

#!/bin/bash
# View DockerCLI implementation
cat src/Containers/Adapter/DockerCLI.php

Length of output: 14679


🏁 Script executed:

#!/bin/bash
# View DockerAPI implementation
cat src/Containers/Adapter/DockerAPI.php

Length of output: 21754


🏁 Script executed:

#!/bin/bash
# View Networks class
cat src/Containers/Networks.php

Length of output: 1350


🏁 Script executed:

#!/bin/bash
# Check DockerAPI class declaration and inheritance
head -20 src/Containers/Adapter/DockerAPI.php

Length of output: 508


🏁 Script executed:

#!/bin/bash
# Check pint configuration
cat pint.json

Length of output: 391


Fix Adapter contract: clean import, add return types, align getUsage implementation across adapters

Three confirmed issues:

  • use Utopia\Containers\Usage; (line 5) is redundant in the same namespace and flagged by Pint; remove it and keep the unqualified ?Usage type hint.
  • disconnect(string $container, string $network, bool $force) (line 16) lacks a return type, but Networks::disconnect() returns the adapter's result as bool. Add : bool to the abstract signature and all implementations.
  • getUsage(string $container): ?Usage (line 22) is the contract, but DockerCLI::getUsage() returns array and throws \Exception('getUsage') instead. Implement it to return a Usage object or null, not an array.

{
abstract public function createNetwork(string $name, bool $internal): void;

abstract public function removeNetwork(string $name): void;

abstract public function networkExists(string $name): bool;

abstract public function connect(string $container, string $network): void;

abstract public function disconnect(string $container, string $network, bool $force);

/**
* @return list<Network>
*/
abstract public function listNetworks(): array;

abstract public function getUsage(string $container): ?Usage;

/**
* @param array<string, string> $filters
* @return array<string, Usage> Map of container ids to usage stats
*/
abstract public function listUsage(array $filters): array;

abstract public function pull(string $image): void;

/**
* @param array<string, string> $filters
* @return Container[]
*/
abstract public function list(array $filters): array;

abstract public function run(
string $name,
string $image,
string $hostname,
array $command,
array $entrypoint,
?string $workdir,
array $environment,
array $mounts,
array $labels,
?string $network,
?float $cpus,
?int $memory,
?int $swap,
RestartPolicy $restart,
bool $remove,
): string;

/**
* @param string[] $command
* @param array<string, string> $vars
*/
abstract public function execute(string $name, array $command, string &$output, array $vars, int $timeout): bool;

/**
* Remove Container
*/
abstract public function remove(string $name, bool $force): bool;
}
Loading
Loading