view release on metacpan or search on metacpan
.claude/CLAUDE.md view on Meta::CPAN
dzil build
dzil test
prove -lv t/
```
## Test Architecture
Unified mock/live tests controlled by environment variables:
```bash
# Mock mode (default):
prove -l t/
# Read tests against real Docker:
API_DOCKER_TEST_HOST=unix:///var/run/docker.sock prove -l t/
# Full live mode (read + write):
API_DOCKER_TEST_HOST=unix:///var/run/docker.sock API_DOCKER_TEST_WRITE=1 prove -l t/
```
| Env Var | Effect |
version automatically â never bump it by hand before a release.
12. **`{{$NEXT}}` in `Changes` is the placeholder for the upcoming
release.** Add entries under it as you change behavior; `dzil
release` replaces it with the version + timestamp.
## What this distribution is
A pure-Perl client for the Docker Engine API. No LWP, no shell-outs â
HTTP/1.1 (incl. chunked) is spoken directly over the daemon's Unix
socket (default) or a TCP endpoint.
The synchronous `_request` core lives in
`API::Docker::Role::HTTP`; resource-specific API methods live in
`API::Docker::API::*`. Entity wrappers (`API::Docker::Container`,
`API::Docker::Image`, ...) hang off the resource APIs.
## Layout
```
lib/API/Docker.pm # main client, version negotiation
## Build and test
```bash
dzil build # build the dist
dzil test # full test suite
prove -lv t/images.t # single test
cpanm --installdeps . # install deps from cpanfile
```
By default tests are fixture-driven (no Docker daemon needed). Set
`API_DOCKER_TEST_HOST=unix:///var/run/docker.sock` to also exercise the
read-only live paths; add `API_DOCKER_TEST_WRITE=1` to enable mutating
tests (create/remove containers, etc.).
## API conventions
- **Resource accessors live under the client:** `$docker->images`,
`$docker->containers`, etc. Each returns a `*::API::*` instance.
- **List/inspect endpoints return entity objects** (e.g.
`$docker->images->list` returns `[API::Docker::Image, ...]`); raw
- **`X-Registry-Auth` is required on every push** by the Docker Engine â
even anonymous attempts. `images->push` always sends the header; pass
`auth => { username, password, serveraddress, identitytoken }` to
authenticate, omit it for the empty-`{}` form.
## Testing notes
- New tests should use the `Test::API::Docker::Mock` helper. Pass a
`'METHOD /path' => $fixture_or_coderef` route table; the helper
monkey-patches `_request` to dispatch against it.
- Don't add network-dependent assertions to default test runs. Gate them
on `is_live()` / `can_write()` from the mock helper.
- Fixtures live in `t/fixtures/*.json`. Capture them from a real daemon
rather than hand-rolling â keeps drift detectable.
## When changing behavior
- Add a `Changes` entry under `{{$NEXT}}`.
- Update the POD on the affected class. POD lives next to the code
(`=method`, `=attr`, `=head1 SYNOPSIS` ...) and is woven by the
`@Author::GETTY` bundle.
},
{
"class" : "Dist::Zilla::Plugin::ShareDir",
"name" : "@Author::GETTY/@Filter/ShareDir",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::MakeMaker",
"config" : {
"Dist::Zilla::Role::TestRunner" : {
"default_jobs" : 1
}
},
"name" : "@Author::GETTY/@Filter/MakeMaker",
"version" : "6.037"
},
{
"class" : "Dist::Zilla::Plugin::Manifest",
"name" : "@Author::GETTY/@Filter/Manifest",
"version" : "6.037"
},
name: '@Author::GETTY/@Filter/ExecDir'
version: '6.037'
-
class: Dist::Zilla::Plugin::ShareDir
name: '@Author::GETTY/@Filter/ShareDir'
version: '6.037'
-
class: Dist::Zilla::Plugin::MakeMaker
config:
Dist::Zilla::Role::TestRunner:
default_jobs: 1
name: '@Author::GETTY/@Filter/MakeMaker'
version: '6.037'
-
class: Dist::Zilla::Plugin::Manifest
name: '@Author::GETTY/@Filter/Manifest'
version: '6.037'
-
class: Dist::Zilla::Plugin::TestRelease
name: '@Author::GETTY/@Filter/TestRelease'
version: '6.037'
lib/API/Docker.pm view on Meta::CPAN
use API::Docker::API::System;
use API::Docker::API::Containers;
use API::Docker::API::Images;
use API::Docker::API::Networks;
use API::Docker::API::Volumes;
use API::Docker::API::Exec;
has host => (
is => 'ro',
default => sub { $ENV{DOCKER_HOST} // 'unix:///var/run/docker.sock' },
);
has api_version => (
is => 'rwp',
default => undef,
);
has tls => (
is => 'ro',
default => 0,
);
has cert_path => (
is => 'ro',
default => sub { $ENV{DOCKER_CERT_PATH} },
);
has _version_negotiated => (
is => 'rw',
default => 0,
);
with 'API::Docker::Role::HTTP';
has system => (
is => 'lazy',
builder => sub { API::Docker::API::System->new(client => $_[0]) },
);
lib/API/Docker.pm view on Meta::CPAN
=head2 host
Docker daemon connection URL. Defaults to C<$ENV{DOCKER_HOST}> or
C<unix:///var/run/docker.sock>.
Supported formats:
=over
=item * C<unix:///path/to/socket> - Unix socket (default)
=item * C<tcp://host:port> - TCP connection
=back
=head2 api_version
Docker API version to use (e.g., C<1.41>). If not set, the client will
automatically negotiate the highest API version supported by the daemon.
lib/API/Docker.pm view on Meta::CPAN
After negotiation, L</api_version> will contain the negotiated version
(e.g., C<1.41>).
=head1 ENVIRONMENT VARIABLES
=over
=item C<DOCKER_HOST>
Docker daemon connection URL. Used as default for L</host> if not explicitly set.
Examples: C<unix:///var/run/docker.sock>, C<tcp://localhost:2375>
=item C<DOCKER_CERT_PATH>
Path to TLS certificates directory. Used as default for L</cert_path>.
=back
=head1 SEE ALSO
=over
=item * L<API::Docker::Role::HTTP> - HTTP transport implementation
=item * L<API::Docker::API::System> - System and daemon operations
lib/API/Docker/API/Containers.pm view on Meta::CPAN
=head2 list
my $containers = $containers->list(%opts);
List containers. Returns ArrayRef of L<API::Docker::Container> objects.
Options:
=over
=item * C<all> - Show all containers (default shows just running)
=item * C<limit> - Limit results to N most recently created containers
=item * C<size> - Include size information
=item * C<filters> - Hashref of filters
=back
=head2 create
lib/API/Docker/API/Containers.pm view on Meta::CPAN
=head2 stop
$containers->stop($id, timeout => 10);
Stop a container.
Options:
=over
=item * C<timeout> - Seconds to wait before killing (default 10)
=item * C<signal> - Signal to send (default SIGTERM)
=back
=head2 restart
$containers->restart($id, timeout => 10);
Restart a container. Optionally specify C<timeout> in seconds.
=head2 kill
lib/API/Docker/API/Containers.pm view on Meta::CPAN
=head2 logs
my $logs = $containers->logs($id, tail => 100, timestamps => 1);
Get container logs.
Options:
=over
=item * C<stdout> - Include stdout (default 1)
=item * C<stderr> - Include stderr (default 1)
=item * C<since> - Show logs since timestamp
=item * C<until> - Show logs before timestamp
=item * C<timestamps> - Include timestamps
=item * C<tail> - Number of lines from end (e.g., C<100> or C<all>)
=back
lib/API/Docker/API/Images.pm view on Meta::CPAN
=head2 list
my $images = $images->list(all => 1);
List images. Returns ArrayRef of L<API::Docker::Image> objects.
Options:
=over
=item * C<all> - Show all images (default hides intermediate images)
=item * C<digests> - Include digest information
=item * C<filters> - Hashref of filters
=back
=head2 build
# Build from a tar archive
lib/API/Docker/API/Images.pm view on Meta::CPAN
The C<context> parameter is required and must contain the raw bytes of a tar
archive (or a scalar reference to one).
Options:
=over
=item * C<context> - Tar archive bytes (required)
=item * C<dockerfile> - Path to Dockerfile within the archive (default: C<Dockerfile>)
=item * C<t> - Tag for the image (e.g. C<name:tag>)
=item * C<q> - Suppress verbose build output
=item * C<nocache> - Do not use cache when building
=item * C<pull> - Always pull base image
=item * C<rm> - Remove intermediate containers (default: true)
=item * C<forcerm> - Always remove intermediate containers
=item * C<buildargs> - HashRef of build-time variables
=item * C<labels> - HashRef of labels to set on the image
=item * C<memory> - Memory limit in bytes
=item * C<memswap> - Total memory (memory + swap), -1 to disable swap
lib/API/Docker/API/Images.pm view on Meta::CPAN
=item * C<platform> - Platform (e.g. C<linux/amd64>)
=item * C<target> - Multi-stage build target
=back
=head2 pull
$images->pull(fromImage => 'nginx', tag => 'latest');
Pull an image from a registry. C<tag> defaults to C<latest>.
=head2 inspect
my $image = $images->inspect('nginx:latest');
Get detailed information about an image. Returns L<API::Docker::Image> object.
=head2 history
my $history = $images->history('nginx:latest');
use_ok('API::Docker::API::Containers');
use_ok('API::Docker::API::Images');
use_ok('API::Docker::API::Networks');
use_ok('API::Docker::API::Volumes');
use_ok('API::Docker::API::Exec');
use_ok('API::Docker::Container');
use_ok('API::Docker::Image');
use_ok('API::Docker::Network');
use_ok('API::Docker::Volume');
# Test default construction
my $docker = API::Docker->new(api_version => '1.47');
isa_ok($docker, 'API::Docker');
is($docker->host, 'unix:///var/run/docker.sock', 'default host');
is($docker->api_version, '1.47', 'api_version set');
is($docker->tls, 0, 'tls off by default');
# Test custom host
my $docker_tcp = API::Docker->new(
host => 'tcp://remote:2375',
api_version => '1.47',
);
is($docker_tcp->host, 'tcp://remote:2375', 'custom host');
# Test API accessors exist
can_ok($docker, qw(system containers images networks volumes exec));
t/fixtures/container_inspect.json view on Meta::CPAN
"Error": "",
"StartedAt": "2025-01-15T08:00:01.000000000Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
"Image": "sha256:abc123def456",
"Name": "/my-container",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"HostConfig": {
"NetworkMode": "default",
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
}
},
"Config": {
"Hostname": "abc123def456",
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.25.3"
t/fixtures/containers_list.json view on Meta::CPAN
"PublicPort": 8080,
"Type": "tcp"
}
],
"Labels": {
"maintainer": "NGINX"
},
"SizeRw": 12288,
"SizeRootFs": 187654321,
"HostConfig": {
"NetworkMode": "default"
},
"NetworkSettings": {
"Networks": {
"bridge": {
"IPAddress": "172.17.0.2",
"Gateway": "172.17.0.1"
}
}
},
"Mounts": []
t/fixtures/containers_list.json view on Meta::CPAN
"ImageID": "sha256:def789",
"Command": "docker-entrypoint.sh redis-server",
"Created": 1705290000,
"State": "exited",
"Status": "Exited (0) 1 hour ago",
"Ports": [],
"Labels": {},
"SizeRw": 0,
"SizeRootFs": 130000000,
"HostConfig": {
"NetworkMode": "default"
},
"NetworkSettings": {
"Networks": {
"bridge": {
"IPAddress": "",
"Gateway": ""
}
}
},
"Mounts": []
t/fixtures/networks_list.json view on Meta::CPAN
[
{
"Name": "bridge",
"Id": "f2de39df4171b0dc801e8002f6c14cc3c20fd116b2f45e85f06f448d97b48f2b",
"Created": "2025-01-10T08:00:00.000000000Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.name": "docker0"
},
"Labels": {}
},
{
"Name": "my-network",
"Id": "a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890a1b2c3d4e5f67890",
"Created": "2025-01-12T10:00:00.000000000Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,