API-Docker
view release on metacpan or search on metacpan
- **`$docker->_request($method, $path, %opts)`** is the single transport
entry point. Opts: `body` (auto-JSON-encoded), `raw_body` +
`content_type` (e.g. tarballs), `params` (query string),
`headers` (extra HTTP headers â used by push for `X-Registry-Auth`).
- **`/build`, `/images/create`, `/images/.../push`** are streaming
endpoints. `_request` parses newline-delimited JSON and returns an
arrayref of events; callers iterate and look for `errorDetail`,
`progress`, `aux`, etc.
- **`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
Revision history for API-Docker
0.002 2026-05-17 05:36:20Z
- HTTP role: `_request` now accepts a `headers => {}` option to set
extra HTTP request headers. Headers are sanitised against CR/LF
injection. Used by `images->push` to send `X-Registry-Auth`, and
available to any caller that needs custom headers.
- `images->push` now always sends an `X-Registry-Auth` header â the
Docker Engine refuses pushes without it (`HTTP 400: missing
X-Registry-Auth: invalid X-Registry-Auth header: EOF`). A new `auth`
option accepts a hashref of credentials (`username`, `password`,
`serveraddress`, or `identitytoken`) which is JSON-encoded and
base64url-wrapped per the Docker Engine spec. Without `auth` the
header carries an empty JSON object so unauthenticated/public
pushes succeed where they previously failed at the HTTP layer.
0.001 2026-04-29 00:40:43Z
- Initial release as API::Docker
- Docker Engine API client with Unix socket and TCP support
- Auto-negotiate API version from daemon
- Container, Image, Network, Volume, System, and Exec APIs
lib/API/Docker/API/Images.pm view on Meta::CPAN
my $history = $images->history('nginx:latest');
Get image history (layers). Returns ArrayRef of layer information.
=head2 push
$images->push('myrepo/nginx', tag => 'v1');
$images->push('myrepo/nginx', auth => {
username => 'me',
password => 'secret',
serveraddress => 'https://index.docker.io/v1/',
});
Push an image to a registry. Optionally specify C<tag>.
The Docker Engine requires an C<X-Registry-Auth> header on every push,
even for anonymous attempts; the header is always sent. Pass C<auth> as
a hashref of credentials (typical keys: C<username>, C<password>,
C<serveraddress>, or C<identitytoken>), or as a pre-encoded base64 string.
Without C<auth> the header carries an empty JSON object.
=head2 tag
$images->tag('nginx:latest', repo => 'myrepo/nginx', tag => 'v1');
Tag an image with a new repository and/or tag name.
=head2 remove
t/images_push_auth.t view on Meta::CPAN
subtest 'empty/undef auth -> base64url("{}")' => sub {
my $hdr = API::Docker::API::Images::_build_registry_auth_header(undef);
ok length($hdr), 'header is non-empty for undef';
is_deeply(decode_json(b64url_decode($hdr)), {},
'decodes to empty JSON object');
};
subtest 'hashref auth -> JSON-encoded credentials' => sub {
my $auth = {
username => 'me',
password => 'secret',
serveraddress => 'https://index.docker.io/v1/',
};
my $hdr = API::Docker::API::Images::_build_registry_auth_header($auth);
is_deeply(decode_json(b64url_decode($hdr)), $auth,
'header roundtrips through base64url + JSON');
};
subtest 'identitytoken auth' => sub {
my $auth = { identitytoken => 'tok-123', serveraddress => 'ghcr.io' };
my $hdr = API::Docker::API::Images::_build_registry_auth_header($auth);
t/images_push_auth.t view on Meta::CPAN
my ($self, $method, $path, %opts) = @_;
$captured = { method => $method, path => $path, %opts };
return [];
};
no warnings 'redefine';
local *API::Docker::_request = $mock;
$docker->images->push(
'raudssus/karr:user',
auth => { username => 'u', password => 'p' },
tag => 'user',
);
is $captured->{method}, 'POST', 'POST issued';
like $captured->{path}, qr{^/images/raudssus/karr:user/push}, 'push path';
ok exists $captured->{headers}{'X-Registry-Auth'},
'X-Registry-Auth header present';
is_deeply(
decode_json(b64url_decode($captured->{headers}{'X-Registry-Auth'})),
{ username => 'u', password => 'p' },
'header decodes to passed credentials',
);
is $captured->{params}{tag}, 'user', 'tag param present';
};
done_testing;
( run in 2.706 seconds using v1.01-cache-2.11-cpan-5735350b133 )