API-Docker

 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 |

CLAUDE.md  view on Meta::CPAN

    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

CLAUDE.md  view on Meta::CPAN


## 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

CLAUDE.md  view on Meta::CPAN

- **`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.

META.json  view on Meta::CPAN

         },
         {
            "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"
         },

META.yml  view on Meta::CPAN

      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');

t/basic.t  view on Meta::CPAN

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,



( run in 3.038 seconds using v1.01-cache-2.11-cpan-cdf2f3d4e48 )