API-Docker
view release on metacpan or search on metacpan
lib/API/Docker/API/Images.pm view on Meta::CPAN
# Tag and push
$docker->images->tag('nginx:latest', repo => 'myrepo/nginx', tag => 'v1');
$docker->images->push('myrepo/nginx', tag => 'v1');
# Remove image
$docker->images->remove('nginx:latest', force => 1);
# Snapshot a container into an image
my $new = $docker->images->commit(container => $id, repo => 'myapp', tag => 'snap');
# Air-gapped roundtrip: export here, carry the tar over, load there
my $export = $docker->images->get('myapp:snap'); # raw tar bytes
$docker->images->load($export);
# Reclaim the build cache (not the same thing as prune)
$docker->images->build_prune(all => 1);
=head1 DESCRIPTION
This module provides methods for managing Docker images including pulling,
listing, tagging, pushing to registries, and removal.
C<list> and C<inspect> return generated L<API::Docker::Type> objects carrying
the convenience methods of L<API::Docker::Role::Entity::Image>, so
C<< $image->tag >> and C<< $image->remove >> work on either. Which class each
returns, and where the two disagree, is below.
Accessed via C<< $docker->images >>, or through
L<API::Docker::Role::Using/using> for a run of calls that needs its own
transport bound: C<< $docker->images->using(read_timeout => 5) >>.
=head2 The two image shapes
The daemon describes an image two ways and the swagger has two definitions
for it, so this class returns two classes:
=over
=item * L</list> returns L<API::Docker::Type::ImageSummary> objects -- one
per entry of C<GET /images/json>.
=item * L</inspect> returns an L<API::Docker::Type::ImageInspect> -- the body
of C<GET /images/{name}/json>.
=back
They overlap but do not line up, and the field names are the swagger's own
spelling in snake_case (C<Id> is C<< ->id >>, C<RepoTags> is
C<< ->repo_tags >>, C<SharedSize> is C<< ->shared_size >>). The differences
worth knowing before reading a value off the wrong one:
=over
=item * C<< ->created >> is an integer Unix epoch on a summary and an
RFC 3339 string on an inspect. Same field name, two types -- C<Int> and
C<Str> in the model, which is the swagger's own answer, not a normalisation
this client applies. The same split a container has, see
L<API::Docker::API::Containers/"The two container shapes">.
=item * The parent layer is C<< ->parent_id >> on a summary and
C<< ->parent >> on an inspect. Both are empty for an image pulled from a
registry rather than built locally, and the swagger marks the inspect one
deprecated.
=item * C<< ->labels >> is top-level on a summary only. An inspect carries
the labels under C<< ->config->labels >>, where C<< ->config >> is the
L<API::Docker::Type::ImageConfig> the image runs containers with --
C<< ->cmd >>, C<< ->env >>, C<< ->entrypoint >>, C<< ->exposed_ports >> and
the rest.
=item * C<< ->containers >> (how many containers use the image) and
C<< ->shared_size >> come from a summary only. The swagger says of both that
C<-1> means the value was not calculated, and of C<SharedSize> that it is not
calculated by default -- so treat C<-1> as "unknown", not as a count.
=item * C<< ->architecture >>, C<< ->os >>, C<< ->os_version >>,
C<< ->variant >>, C<< ->author >>, C<< ->comment >>, C<< ->docker_version >>,
C<< ->config >>, C<< ->root_fs >>, C<< ->graph_driver >> and
C<< ->metadata >> come from an inspect only.
=item * C<< ->id >>, C<< ->repo_tags >>, C<< ->repo_digests >>, C<< ->size >>,
C<< ->descriptor >> and C<< ->manifests >> are on both and mean the same
thing. The swagger declares every field of a summary required and no field of
an inspect, which the model records but does not enforce -- see
L<API::Docker::Type/"C<since> is documentation">.
=back
There is no C<< ->virtual_size >>: the swagger dropped C<VirtualSize> from
both definitions after v1.44, and engines that still send it -- the Podman on
this machine does -- have it kept verbatim in
C<< ->unknown_fields->{VirtualSize} >>, where C<TO_JSON> writes it back
unchanged. F<t/type_fixture_passthrough.t> pins that.
=head2 client
Reference to L<API::Docker> client. Weak reference to avoid circular dependencies.
=head2 list
my $images = $images->list(all => 1);
List images. Returns an ArrayRef of L<API::Docker::Type::ImageSummary>
objects, each carrying the methods of L<API::Docker::Role::Entity::Image>.
Options:
=over
=item * C<all> - Show all images (default hides intermediate images)
=item * C<digests> - Include digest information
=item * C<filters> - HashRef of filter name to ArrayRef of string values, e.g.
C<< { dangling => ['true'] } >>. Shape-checked and normalised by
L<API::Docker::Role::Filters>
=back
=head2 build
lib/API/Docker/API/Images.pm view on Meta::CPAN
newline-delimited JSON stream, even when the stream carried a single object.
A failed push croaks, by one of two routes depending on the engine -- an
unauthorised push to a private registry is the common case, and it is exactly
the one that must not be reported as a success.
Docker reports it inside a 200 stream as an C<errorDetail> object, which
croaks with an L<API::Docker::Error::Stream> carrying the progress events.
Podman puts an C<errorDetail> body behind a real error status instead:
measured against the rootless socket (5.4.2, API 1.41), a push to an
unreachable registry answers C<500 Internal Server Error> with
C<< {"errorDetail":{"message":"... connection refused"},"error":"..."} >>, so
the transport's status handling croaks with an L<API::Docker::Error::HTTP> --
which is that same string to anything inspecting C<$@> as text -- before the
stream is ever decoded. That body carries no C<message> key, so the whole
JSON object ends up as the croak text.
Either way the failure is loud. Inspect C<$@> as a string rather than testing
for the exception class, which only the first route produces.
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.
Options:
=over
=item * C<tag> - Tag to push
=item * C<auth> - Registry credentials, as above
=item * C<on_event> - CodeRef called with each progress event as it arrives --
layer by layer, rather than the whole upload in one silence -- instead of the
ArrayRef being collected and returned. The return value is then the summary
HashRef and a stream failure croaks one event in, exactly as for L</build>;
see L</"Progress as it arrives">
=back
=head2 tag
$images->tag('nginx:latest', repo => 'myrepo/nginx', tag => 'v1');
Tag an image with a new repository and/or tag name.
=head2 remove
$images->remove('nginx:latest', force => 1);
Remove an image.
Options:
=over
=item * C<force> - Force removal
=item * C<noprune> - Do not delete untagged parents
=back
=head2 search
my $results = $images->search('nginx', limit => 25);
Search Docker Hub for images. Returns ArrayRef of search results.
Options:
=over
=item * C<limit> - Maximum number of results
=item * C<filters> - HashRef of filter name to ArrayRef of string values; the
engine accepts C<is-official>, C<is-automated> and C<stars> here. The boolean
ones want the string, C<< { 'is-official' => ['true'] } >>, and C<stars> a
number written as one -- L<API::Docker::Role::Filters> takes care of both and
croaks on a shape the daemon would refuse
=back
=head2 prune
my $result = $images->prune(filters => { dangling => ['true'] });
Delete unused images. Returns hashref with C<ImagesDeleted> and C<SpaceReclaimed>.
Options:
=over
=item * C<filters> - HashRef of filter name to ArrayRef of string values; the
engine accepts C<dangling>, C<until> and C<label> here. Shape-checked and
normalised by L<API::Docker::Role::Filters>
=back
=head2 get
use Path::Tiny;
my $tar = $images->get('alpine:3');
path('alpine.tar')->spew_raw($tar);
Export one image, and the history behind it, as a tar archive -- the endpoint
behind C<docker image save>. Together with L</load> it is the only way in or
out of a daemon that does not go through a registry.
B<The return value is raw bytes, not a decoded structure.> The engine answers
with the tar stream itself, and the transport is told to hand it back
untouched (C<< raw => 1 >>), so what arrives is byte for byte what the daemon
wrote. Write it with a binary-safe file handle -- C<< path(...)->spew_raw >>,
or C<binmode> on a handle of your own. Treating it as text corrupts it, and
nothing about the value announces that it is binary.
The archive holds one tarball per layer, a config JSON per image,
C<manifest.json> and C<repositories>. Measured against Podman 5.4.2 (API
1.41): the response is chunked with
C<< Content-Type: application/octet; charset=us-ascii >>, where Docker sends
( run in 2.176 seconds using v1.01-cache-2.11-cpan-80ec619307d )