API-Docker

 view release on metacpan or  search on metacpan

.gitignore  view on Meta::CPAN

API-Docker-*
.build

# Claude Code — commit: skills/, agents/, hooks/, settings.json
# Ignore: local overrides, credentials, session data
.claude/*.local.*
.claude/local/
.claude/.credentials.json
.claude/statsig/
.claude/todos/
.claude/projects/

lib/API/Docker/API/Containers.pm  view on Meta::CPAN



has client => (
  is       => 'ro',
  required => 1,
  weak_ref => 1,
);


sub _wrap {
  my ($self, $data) = @_;
  return API::Docker::Container->new(
    client => $self->client,
    %$data,
  );
}

sub _wrap_list {
  my ($self, $list) = @_;
  return [ map { $self->_wrap($_) } @$list ];
}

sub list {
  my ($self, %opts) = @_;

lib/API/Docker/API/Images.pm  view on Meta::CPAN



has client => (
  is       => 'ro',
  required => 1,
  weak_ref => 1,
);


sub _wrap {
  my ($self, $data) = @_;
  return API::Docker::Image->new(
    client => $self->client,
    %$data,
  );
}

sub _wrap_list {
  my ($self, $list) = @_;
  return [ map { $self->_wrap($_) } @$list ];
}

sub list {
  my ($self, %opts) = @_;

lib/API/Docker/API/Images.pm  view on Meta::CPAN


=item * C<digests> - Include digest information

=item * C<filters> - Hashref of filters

=back

=head2 build

    # Build from a tar archive
    my $tar_data = path('context.tar')->slurp_raw;
    my $result = $docker->images->build(
        context    => $tar_data,
        t          => 'myimage:latest',
        dockerfile => 'Dockerfile',
    );

    # Build with build args
    my $result = $docker->images->build(
        context   => $tar_data,
        t         => 'myapp:v1',
        buildargs => { APP_VERSION => '1.0' },
        nocache   => 1,
    );

Build an image from a tar archive containing a Dockerfile and build context.

The C<context> parameter is required and must contain the raw bytes of a tar
archive (or a scalar reference to one).

lib/API/Docker/API/Networks.pm  view on Meta::CPAN



has client => (
  is       => 'ro',
  required => 1,
  weak_ref => 1,
);


sub _wrap {
  my ($self, $data) = @_;
  return API::Docker::Network->new(
    client => $self->client,
    %$data,
  );
}

sub _wrap_list {
  my ($self, $list) = @_;
  return [ map { $self->_wrap($_) } @$list ];
}

sub list {
  my ($self, %opts) = @_;

lib/API/Docker/API/System.pm  view on Meta::CPAN

=item * C<until> - Show events created before this timestamp

=item * C<filters> - Hashref of filters (e.g., C<< { type => ['container', 'image'] } >>)

=back

=head2 df

    my $usage = $system->df;

Get data usage information (disk usage by images, containers, and volumes).

Returns hashref with C<LayersSize>, C<Images>, C<Containers>, and C<Volumes> arrays.

=head1 SEE ALSO

=over

=item * L<API::Docker> - Main Docker client

=back

lib/API/Docker/API/Volumes.pm  view on Meta::CPAN



has client => (
  is       => 'ro',
  required => 1,
  weak_ref => 1,
);


sub _wrap {
  my ($self, $data) = @_;
  return API::Docker::Volume->new(
    client => $self->client,
    %$data,
  );
}

sub _wrap_list {
  my ($self, $list) = @_;
  return [ map { $self->_wrap($_) } @$list ];
}

sub list {
  my ($self, %opts) = @_;

lib/API/Docker/Image.pm  view on Meta::CPAN


has SharedSize   => (is => 'ro');
has VirtualSize  => (is => 'ro');
has Labels       => (is => 'ro');
has Containers   => (is => 'ro');

has Architecture => (is => 'ro');
has Os           => (is => 'ro');
has Config       => (is => 'ro');
has RootFS       => (is => 'ro');
has Metadata     => (is => 'ro');

sub inspect {
  my ($self) = @_;
  return $self->client->images->inspect($self->Id);
}


sub history {
  my ($self) = @_;
  return $self->client->images->history($self->Id);

lib/API/Docker/Role/HTTP.pm  view on Meta::CPAN

  $self->_clear_socket;

  my ($status_code, $status_text, $headers, $body) = @$response;

  $log->debugf("Response: %s %s", $status_code, $status_text);

  if ($status_code >= 400) {
    my $error_msg = $body;
    if ($body && $body =~ /^\s*[\{\[]/) {
      eval {
        my $data = decode_json($body);
        $error_msg = $data->{message} // $body;
      };
    }
    croak "Docker API error ($status_code): $error_msg";
  }

  if ($status_code == 204 || !defined($body) || $body eq '') {
    return undef;
  }

  if ($body =~ /^\s*[\{\[]/) {

lib/API/Docker/Role/HTTP.pm  view on Meta::CPAN

    my $read = 0;
    while ($read < $chunk_size) {
      my $buf;
      my $n = read($sock, $buf, $chunk_size - $read);
      last unless $n;
      $chunk .= $buf;
      $read += $n;
    }
    $body .= $chunk;

    # Read trailing \r\n after chunk data
    <$sock>;
  }

  return $body;
}

sub _uri_encode {
  my ($str) = @_;
  $str =~ s/([^A-Za-z0-9\-_.~:\/])/sprintf("%%%02X", ord($1))/ge;
  return $str;

lib/API/Docker/Role/HTTP.pm  view on Meta::CPAN


    package MyDockerClient;
    use Moo;

    has host => (is => 'ro', required => 1);
    has api_version => (is => 'ro');

    with 'API::Docker::Role::HTTP';

    # Now use get, post, put, delete_request methods
    my $data = $self->get('/containers/json');

=head1 DESCRIPTION

This role provides HTTP transport for the Docker Engine API. It implements
HTTP/1.1 communication over Unix sockets and TCP sockets without depending on
heavy HTTP client libraries like LWP.

Features:

=over

lib/API/Docker/Role/HTTP.pm  view on Meta::CPAN

=item * Request/response logging via L<Log::Any>

=item * Automatic connection management

=back

Consuming classes must provide C<host> and C<api_version> attributes.

=head2 get

    my $data = $client->get($path, %opts);

Perform HTTP GET request. Returns decoded JSON or raw response body.

Options: C<params> (hashref of query parameters),
C<headers> (hashref of extra HTTP headers, e.g. C<< { 'X-Registry-Auth' => $b64 } >>).

=head2 post

    my $data = $client->post($path, $body, %opts);

Perform HTTP POST request. C<$body> is automatically JSON-encoded if provided.

Options: C<params> (hashref of query parameters),
C<headers> (hashref of extra HTTP headers).

=head2 put

    my $data = $client->put($path, $body, %opts);

Perform HTTP PUT request. C<$body> is automatically JSON-encoded if provided.

Options: C<params> (hashref of query parameters).

=head2 delete_request

    my $data = $client->delete_request($path, %opts);

Perform HTTP DELETE request.

Options: C<params> (hashref of query parameters).

=head1 SEE ALSO

=over

=item * L<API::Docker> - Main client using this role

t/fixtures/volumes_list.json  view on Meta::CPAN

{
  "Volumes": [
    {
      "Name": "my-data",
      "Driver": "local",
      "Mountpoint": "/var/lib/docker/volumes/my-data/_data",
      "CreatedAt": "2025-01-10T08:00:00Z",
      "Status": {},
      "Labels": {
        "project": "test"
      },
      "Scope": "local",
      "Options": {}
    },
    {
      "Name": "postgres-data",
      "Driver": "local",
      "Mountpoint": "/var/lib/docker/volumes/postgres-data/_data",
      "CreatedAt": "2025-01-11T09:00:00Z",
      "Status": {},
      "Labels": {},
      "Scope": "local",
      "Options": {}
    }
  ],
  "Warnings": []
}

t/images.t  view on Meta::CPAN

    $tar .= $dockerfile;
    $tar .= "\0" x (512 - ($size % 512)) if $size % 512;
    $tar .= "\0" x 1024;

    my $tag = 'api-docker-test-build:latest';
    my $result = $docker->images->build(context => $tar, t => $tag, q => 1);
    ok($result, 'build returned result');
    register_cleanup(sub { eval { $docker->images->remove($tag, force => 1) } });
  } else {
    my $result = $docker->images->build(
      context    => 'fake-tar-data',
      t          => 'myapp:latest',
      dockerfile => 'Dockerfile',
    );
    ok($result, 'build returned a result');
    like($result->{stream}, qr/Successfully built/, 'build output contains success');

    $docker->images->pull(fromImage => 'nginx', tag => 'latest');
    pass('pull completed');

    $docker->images->tag('nginx:latest', repo => 'myrepo/nginx', tag => 'v1');

t/system.t  view on Meta::CPAN

  my $docker = test_docker(
    'GET /system/df' => {
      LayersSize => 1000000000,
      Images     => [
        { Id => 'sha256:abc', Size => 500000000, SharedSize => 200000000 },
      ],
      Containers => [
        { Id => 'abc123', SizeRw => 10000, SizeRootFs => 500000000 },
      ],
      Volumes => [
        { Name => 'my-data', UsageData => { Size => 100000000 } },
      ],
    },
  );

  my $df = $docker->system->df;

  ok(defined $df->{LayersSize}, 'has LayersSize');
  is(ref $df->{Images}, 'ARRAY', 'has Images array');
  is(ref $df->{Containers}, 'ARRAY', 'has Containers array');
  is(ref $df->{Volumes}, 'ARRAY', 'has Volumes array');

t/volumes.t  view on Meta::CPAN

  is(ref $volumes, 'ARRAY', 'returns array');
  if (@$volumes) {
    isa_ok($volumes->[0], 'API::Docker::Volume');
    ok($volumes->[0]->Name, 'has Name');
  }

  unless (is_live()) {
    is(scalar @$volumes, 2, 'two volumes');

    my $first = $volumes->[0];
    is($first->Name, 'my-data', 'volume name');
    is($first->Driver, 'local', 'volume driver');
    is($first->Scope, 'local', 'volume scope');
    is_deeply($first->Labels, { project => 'test' }, 'volume labels');
    like($first->Mountpoint, qr{/var/lib/docker/volumes/my-data}, 'mountpoint');
  }
};

# --- Write Tests (mock always, live only with WRITE) ---

subtest 'volume lifecycle' => sub {
  skip_unless_write();

  my $docker = test_docker(
    'POST /volumes/create' => sub {
      my ($method, $path, %opts) = @_;
      is($opts{body}{Name}, 'test-vol', 'volume name in body') unless is_live();
      return {
        Name       => 'test-vol',
        Driver     => 'local',
        Mountpoint => '/var/lib/docker/volumes/test-vol/_data',
        CreatedAt  => '2025-01-15T12:00:00Z',
        Labels     => {},
        Scope      => 'local',
        Options    => {},
      };
    },
    'GET /volumes/test-vol' => {
      Name       => 'test-vol',
      Driver     => 'local',
      Mountpoint => '/var/lib/docker/volumes/test-vol/_data',
      CreatedAt  => '2025-01-10T08:00:00Z',
      Labels     => {},
      Scope      => 'local',
      Options    => {},
    },
    'DELETE /volumes/test-vol' => undef,
  );

  my $name = is_live() ? 'api-docker-test-vol-' . $$ : 'test-vol';
  my $volume = $docker->volumes->create(Name => $name);



( run in 0.813 second using v1.01-cache-2.11-cpan-c966e8aa7e8 )