API-Docker
view release on metacpan or search on metacpan
lib/API/Docker/API/Images.pm view on Meta::CPAN
# wire names and nothing else, so a key it has not heard of keeps its own
# spelling instead of being read as the Perl name of one it has, and a value
# that disagrees with the swagger costs its own field rather than the whole
# response. `client` is ours rather than the engine's, so it goes beside the
# data instead of into it.
sub _wrap {
my ($self, $class, $data) = @_;
return $class->from_data($data, client => $self->client);
}
sub _wrap_list {
my ($self, $class, $list) = @_;
return [ map { $self->_wrap($class, $_) } @$list ];
}
sub list {
my ($self, %opts) = @_;
my %params;
$params{all} = $opts{all} ? 1 : 0 if defined $opts{all};
$params{digests} = $opts{digests} ? 1 : 0 if defined $opts{digests};
$params{filters} = $self->_normalise_filters($opts{filters})
if defined $opts{filters};
my $result = $self->client->get('/images/json',
params => \%params,
%{ $self->_request_options },
);
return $self->_wrap_list('API::Docker::Type::ImageSummary', $result // []);
}
sub build {
my ($self, %opts) = @_;
my $context = delete $opts{context};
croak "Build context required (tar archive as scalar ref or raw bytes)" unless defined $context;
my %params;
$params{dockerfile} = $opts{dockerfile} if defined $opts{dockerfile};
$params{t} = $opts{t} if defined $opts{t};
$params{q} = $opts{q} ? 1 : 0 if defined $opts{q};
$params{nocache} = $opts{nocache} ? 1 : 0 if defined $opts{nocache};
$params{pull} = $opts{pull} if defined $opts{pull};
$params{rm} = defined $opts{rm} ? ($opts{rm} ? 1 : 0) : 1;
$params{forcerm} = $opts{forcerm} ? 1 : 0 if defined $opts{forcerm};
$params{memory} = $opts{memory} if defined $opts{memory};
$params{memswap} = $opts{memswap} if defined $opts{memswap};
$params{cpushares} = $opts{cpushares} if defined $opts{cpushares};
$params{cpusetcpus} = $opts{cpusetcpus} if defined $opts{cpusetcpus};
$params{cpuperiod} = $opts{cpuperiod} if defined $opts{cpuperiod};
$params{cpuquota} = $opts{cpuquota} if defined $opts{cpuquota};
$params{shmsize} = $opts{shmsize} if defined $opts{shmsize};
$params{networkmode} = $opts{networkmode} if defined $opts{networkmode};
$params{platform} = $opts{platform} if defined $opts{platform};
$params{target} = $opts{target} if defined $opts{target};
$params{buildargs} = encode_json($opts{buildargs}) if $opts{buildargs};
$params{labels} = encode_json($opts{labels}) if $opts{labels};
my $raw = ref $context eq 'SCALAR' ? $$context : $context;
# A build's registry credentials ride in X-Registry-Config, not
# X-Registry-Auth: the map lets `FROM private.registry/...` authenticate,
# and a build may draw base images from several registries at once. Sent
# only when given -- an anonymous build needs no header.
my %headers;
$headers{'X-Registry-Config'} =
$self->_registry_config_header($opts{registry_config})
if defined $opts{registry_config};
# exists, not truth: an unset callback is a caller bug, and falling back to
# the buffered path for it would hand a long build back as silence.
return $self->client->_request('POST', '/build',
raw_body => $raw,
content_type => 'application/x-tar',
params => \%params,
%headers ? ( headers => \%headers ) : (),
%{ $self->_request_options },
exists $opts{on_event} ? ( on_event => $opts{on_event} ) : ( ndjson => 1 ),
);
}
# The tag query parameter is not a default to hand out unconditionally: the
# engine appends it to whatever reference `fromImage` already carries. Docker
# lets tag take precedence and silently rewrites `nginx:1.25` to `nginx:latest`
# (a wrong image, reported as success); Podman concatenates to
# `nginx:1.25:latest` and answers 500 `invalid reference format`. A digest
# reference breaks the same way on both. So `tag` is defaulted only when the
# reference carries neither -- a `:tag` in the segment after the last `/`, or an
# `@digest` anywhere. The colon in a registry `host:port/` is before that
# segment, so it is not mistaken for a tag.
sub _reference_has_tag_or_digest {
my ($self, $ref) = @_;
return 1 if $ref =~ /\@/;
my ($last_segment) = $ref =~ m{([^/]*)\z};
return $last_segment =~ /:/ ? 1 : 0;
}
# Only when credentials were given: an anonymous pull needs no header, and the
# engine reads X-Registry-Auth off /images/create only to reach a private
# registry. This is the plugins/distribution policy, not push's always-send --
# push must send even the anonymous {} because the engine rejects a push with
# no header at all.
sub _auth_headers {
my ($self, $opts) = @_;
return () unless defined $opts->{auth};
return (headers => { 'X-Registry-Auth' => $self->_registry_auth_header($opts->{auth}) });
}
sub pull {
my ($self, %opts) = @_;
croak "fromImage required" unless $opts{fromImage};
my %params;
$params{fromImage} = $opts{fromImage};
if (defined $opts{tag}) {
$params{tag} = $opts{tag};
}
elsif (!$self->_reference_has_tag_or_digest($opts{fromImage})) {
$params{tag} = 'latest';
}
return $self->client->post('/images/create', undef,
params => \%params,
$self->_auth_headers(\%opts),
%{ $self->_request_options },
exists $opts{on_event} ? ( on_event => $opts{on_event} ) : ( ndjson => 1 ),
);
}
sub inspect {
my ($self, $name) = @_;
croak "Image name required" unless $name;
my $result = $self->client->get("/images/$name/json",
%{ $self->_request_options },
);
return $self->_wrap('API::Docker::Type::ImageInspect', $result);
}
sub history {
my ($self, $name) = @_;
croak "Image name required" unless $name;
return $self->client->get("/images/$name/history",
%{ $self->_request_options },
);
}
sub push {
my ($self, $name, %opts) = @_;
croak "Image name required" unless $name;
my %params;
$params{tag} = $opts{tag} if defined $opts{tag};
my $auth_header = $self->_registry_auth_header($opts{auth});
return $self->client->post(
"/images/$name/push",
undef,
params => \%params,
headers => { 'X-Registry-Auth' => $auth_header },
%{ $self->_request_options },
exists $opts{on_event} ? ( on_event => $opts{on_event} ) : ( ndjson => 1 ),
);
}
sub tag {
my ($self, $name, %opts) = @_;
croak "Image name required" unless $name;
my %params;
$params{repo} = $opts{repo} if defined $opts{repo};
$params{tag} = $opts{tag} if defined $opts{tag};
return $self->client->post("/images/$name/tag", undef,
params => \%params,
%{ $self->_request_options },
);
}
sub remove {
my ($self, $name, %opts) = @_;
croak "Image name required" unless $name;
my %params;
$params{force} = $opts{force} ? 1 : 0 if defined $opts{force};
$params{noprune} = $opts{noprune} ? 1 : 0 if defined $opts{noprune};
return $self->client->delete_request("/images/$name",
params => \%params,
%{ $self->_request_options },
);
}
sub search {
my ($self, $term, %opts) = @_;
croak "Search term required" unless $term;
my %params;
$params{term} = $term;
$params{limit} = $opts{limit} if defined $opts{limit};
$params{filters} = $self->_normalise_filters($opts{filters})
if defined $opts{filters};
return $self->client->get('/images/search',
params => \%params,
%{ $self->_request_options },
);
}
sub prune {
my ($self, %opts) = @_;
my %params;
$params{filters} = $self->_normalise_filters($opts{filters})
if defined $opts{filters};
return $self->client->post('/images/prune', undef,
params => \%params,
%{ $self->_request_options },
);
}
sub get {
lib/API/Docker/API/Images.pm view on Meta::CPAN
}
sub get_all {
my ($self, @names) = @_;
# The list form has nowhere to put an option: get_all('a', 'b') is names all
# the way down, and a trailing `on_chunk => sub {...}` in it would be two
# more image names as far as this method can tell. So options ride behind
# the ArrayRef form, which already exists for exactly one list.
my %opts;
if (ref $names[0] eq 'ARRAY') {
my $list = shift @names;
croak __PACKAGE__ . '->get_all takes options as pairs after the ArrayRef '
. 'of names; got an odd number of them' if @names % 2;
%opts = @names;
@names = @$list;
}
croak "At least one image name required" unless @names;
# `names` is a repeated query parameter -- names=a&names=b -- and nothing
# else is accepted: measured against Podman 5.4.2, the comma-joined spelling
# answers 500 with 'parsing reference "alpine:3,registry:2": invalid
# reference format'. An ArrayRef param value is exactly that repetition;
# _request escapes each element with its own _uri_encode, which leaves `/`
# and `:` raw so an image reference survives intact.
return $self->client->get('/images/get', params => { names => \@names },
%{ $self->_request_options },
exists $opts{on_chunk} ? ( on_chunk => $opts{on_chunk} ) : ( raw => 1 ));
}
sub load {
my ($self, $tar, %opts) = @_;
croak "Tar archive required (raw bytes or a scalar ref)" unless defined $tar;
my %params;
$params{quiet} = $opts{quiet} ? 1 : 0 if defined $opts{quiet};
my $raw = ref $tar eq 'SCALAR' ? $$tar : $tar;
return $self->client->_request('POST', '/images/load',
raw_body => $raw,
content_type => 'application/x-tar',
params => \%params,
%{ $self->_request_options },
exists $opts{on_event} ? ( on_event => $opts{on_event} ) : ( ndjson => 1 ),
);
}
sub commit {
my ($self, %opts) = @_;
croak "container required" unless $opts{container};
my %params;
$params{container} = $opts{container};
$params{repo} = $opts{repo} if defined $opts{repo};
$params{tag} = $opts{tag} if defined $opts{tag};
$params{comment} = $opts{comment} if defined $opts{comment};
$params{author} = $opts{author} if defined $opts{author};
$params{pause} = $opts{pause} ? 1 : 0 if defined $opts{pause};
# `changes` is a repeated query parameter on the wire, but the engine parses
# each value as a Dockerfile snippet and a snippet may span lines, so one
# newline-joined value carries a list just as well. Measured against Podman
# 5.4.2: changes=LABEL%20a%3Db%0AEXPOSE%208080 and two separate changes=
# pairs produce the same image. The joined form is used because it fits the
# transport's one-value-per-key params encoder.
if (defined $opts{changes}) {
$params{changes} = ref $opts{changes} eq 'ARRAY'
? join("\n", @{$opts{changes}})
: $opts{changes};
}
return $self->client->post('/commit', $opts{config},
params => \%params,
%{ $self->_request_options },
);
}
sub build_prune {
my ($self, %opts) = @_;
my %params;
# The engine spells this one with a hyphen, and an unquoted
# `keep-storage => $n` is not even valid Perl -- the fat comma quotes a
# bareword identifier, and keep-storage is a subtraction. So keep_storage is
# the documented spelling, the wire name is accepted beside it for anyone
# copying out of the Engine reference, and the hyphen is what goes on the
# wire. _uri_encode leaves `-` alone, so the key survives unmangled.
my $keep_storage = $opts{keep_storage} // $opts{'keep-storage'};
$params{'keep-storage'} = $keep_storage if defined $keep_storage;
$params{all} = $opts{all} ? 1 : 0 if defined $opts{all};
$params{filters} = $self->_normalise_filters($opts{filters})
if defined $opts{filters};
return $self->client->post('/build/prune', undef,
params => \%params,
%{ $self->_request_options },
);
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
API::Docker::API::Images - Docker Engine Images API
=head1 VERSION
version 0.004
lib/API/Docker/API/Images.pm view on Meta::CPAN
=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
# Build from a tar archive
my $tar_data = path('context.tar')->slurp_raw;
my $events = $docker->images->build(
context => $tar_data,
t => 'myimage:latest',
dockerfile => 'Dockerfile',
);
# Build with build args
my $events = $docker->images->build(
context => $tar_data,
t => 'myapp:v1',
buildargs => { APP_VERSION => '1.0' },
nocache => 1,
);
lib/API/Docker/API/Images.pm view on Meta::CPAN
my $events = eval { $images->build(context => $tar, t => 'myapp:latest') };
if (my $err = $@) {
warn "$err"; # the reason, with Carp's location suffix
for my $event (@{ $err->events }) { # the build output up to the failure
print $event->{stream} if defined $event->{stream};
}
}
The exception stringifies to what a plain C<croak> would have produced, so
existing C<eval>-and-inspect-C<$@> code needs no change.
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
=item * C<cpushares> - CPU shares (relative weight)
=item * C<cpusetcpus> - CPUs to use (e.g. C<0-3>, C<0,1>)
=item * C<cpuperiod> - CPU CFS period (microseconds)
=item * C<cpuquota> - CPU CFS quota (microseconds)
=item * C<shmsize> - Size of /dev/shm in bytes
=item * C<networkmode> - Network mode during build
=item * C<platform> - Platform (e.g. C<linux/amd64>)
=item * C<target> - Multi-stage build target
=item * C<registry_config> - Registry credentials for the base images the build
pulls, sent as C<X-Registry-Config>. A HashRef mapping each registry hostname
to its AuthConfig --
C<< { 'registry.example:5000' => { username => 'me', password => 'secret' } } >>
-- so a C<FROM private.registry/...> can authenticate, and a build drawing from
several registries can carry all of them at once. A pre-encoded base64 string
is also accepted. Sent only when given. This is B<not> C<auth>/C<X-Registry-Auth>,
which carries a single AuthConfig; C</build> uses the map form. See
L<API::Docker::Role::RegistryAuth>
=item * C<on_event> - CodeRef called with each build event as it arrives,
instead of the ArrayRef being collected and returned; see below
=back
=head2 Progress as it arrives
Without a callback the whole stream is read before anything is parsed, so a
build that takes two minutes is two minutes of silence followed by all of its
output at once. Pass C<on_event> and the events are handed over as the daemon
sends them:
my $summary = $images->build(
context => $tar,
t => 'myapp:latest',
on_event => sub {
my ($event, $stop) = @_;
print $event->{stream} if defined $event->{stream};
},
);
$summary; # { delivered => 41, stopped => 0 }
With a callback the return value is that summary HashRef, not the events:
C<delivered> is how many went to the callback, C<stopped> is 1 when the
callback ended the stream and 0 when the daemon did. Nothing is accumulated,
so a caller that wants the C<aux> event with the image id in it must keep that
event itself as it goes by. See
L<API::Docker::Role::HTTP/"Streaming a response as it arrives">.
The same section applies to L</pull>, L</push> and L</load>, which take
C<on_event> on the same terms.
=head3 A failed build still croaks, one event earlier
The C<errorDetail> check runs either way, so a failed build croaks with an
L<API::Docker::Error::Stream> on both paths. What differs is when, and what
the exception carries:
=over
=item * Buffered, the stream is scanned once it is complete, and
C<< $err->events >> is the B<whole> event list -- all the build output that
led up to the failure.
=item * Streamed, the check runs per event, so the croak happens at the event
that reports the failure rather than when the daemon eventually closes. The
exception then carries B<that one event> alone: a callback stream keeps no
history, having handed every earlier event to the callback already. The
failing event itself is not delivered.
=back
So a caller that reads the progress out of C<< $err->events >> must, on this
path, collect it in the callback instead:
my @output;
lib/API/Docker/API/Images.pm view on Meta::CPAN
warn "$err"; # the reason, as before
# $err->events is the failing event; @output is what preceded it
}
=head2 pull
my $events = $images->pull(fromImage => 'nginx', tag => 'latest');
my $events = $images->pull(fromImage => 'nginx:1.25'); # tag rides in the name
my $events = $images->pull(fromImage => 'alpine@sha256:...'); # by digest
Pull an image from a registry.
C<tag> defaults to C<latest> B<only when C<fromImage> carries no tag or digest
of its own>. The engine appends C<tag> to the reference rather than treating it
as a fallback, so defaulting it onto an already-qualified name breaks the pull:
measured against Docker 29.7.2 (API 1.55) C<< pull(fromImage => 'nginx:1.25')
>> would silently fetch C<nginx:latest> and report success, and against Podman
5.8.4 (compat API 1.44) the same request answers C<500 invalid reference
format> for C<nginx:1.25:latest>. A digest reference breaks the same way on
both. So C<tag> is sent only if given explicitly, or defaulted to C<latest>
when the name carries neither a C<:tag> (in the segment after the last C</>)
nor an C<@digest>. A registry C<host:port/> prefix is not mistaken for a tag.
Returns an ArrayRef of progress events, one per object in the engine's
newline-delimited JSON stream, even when the stream carried a single object.
A failed pull croaks either way, but which way depends on the engine, so do
not write code that expects one of them:
=over
=item * Docker reports it in the stream. The response is HTTP 200 and the
failure is an C<errorDetail> object among the progress events; this method
croaks with an L<API::Docker::Error::Stream>, whose C<< ->events >> holds the
progress that preceded the failure.
=item * Podman reports it in the status line. Measured against the rootless
socket (5.4.2, API 1.41): pulling a repository that does not exist answers
C<403 Forbidden> with C<< {"message":"denied: requested access to the resource
is denied"} >>, and an existing repository with a missing tag answers
C<404 Not Found> with C<< {"message":"manifest unknown: manifest unknown"} >>.
Neither reaches the stream at all -- the transport's own status handling
croaks with an L<API::Docker::Error::HTTP> -- which is that same string to
anything inspecting C<$@> as text -- first.
=back
Catching L<API::Docker::Error::Stream> specifically is therefore not a
reliable way to catch a failed pull. C<eval> and inspect C<$@> as a string,
which both cases satisfy.
Options:
=over
=item * C<fromImage> - Image name to pull (required)
=item * C<tag> - Tag to pull. Defaulted to C<latest> only when C<fromImage>
carries no tag or digest of its own; see above
=item * C<auth> - Registry credentials for pulling from a private registry,
sent as C<X-Registry-Auth>. A HashRef of the usual keys (C<username>,
C<password>, C<serveraddress>, or C<identitytoken>) or a pre-encoded base64
string, exactly as L</push> takes it. Unlike C<push>, the header is sent
B<only> when C<auth> is given -- an anonymous pull carries none, which the
engine reads as the anonymous case. See L<API::Docker::Role::RegistryAuth>
=item * C<on_event> - CodeRef called with each progress event as it arrives,
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 inspect
my $image = $images->inspect('nginx:latest');
Get detailed information about an image. Returns an
L<API::Docker::Type::ImageInspect>, which is B<not> the class L</list>
returns -- see L</"The two image shapes">.
=head2 history
my $history = $images->history('nginx:latest');
Get image history (layers). Returns ArrayRef of layer information.
=head2 push
my $events = $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>.
Returns an ArrayRef of progress events, one per object in the engine's
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
lib/API/Docker/API/Images.pm view on Meta::CPAN
C<< {"stream":"Loaded image: ..."} >> object. Should an engine answer a quiet
load with a body of no bytes at all, the transport still returns C<[]>, not
C<undef> -- the C<ndjson> branch in L<API::Docker::Role::HTTP/_request> runs
before the empty-body check that would return C<undef>, so a caller that
iterates the result unconditionally needs no guard for this case.
A failed load croaks, but by which route depends on the engine, the same split
L</pull> and L</push> have. Docker reports it as an C<errorDetail> object
inside a 200 stream, which croaks with an L<API::Docker::Error::Stream>
carrying the events. Podman reports it in the status line instead: measured
against 5.4.2, a body that is not an image archive answers C<500 Internal
Server Error> with C<< {"message":"failed to load image: payload does not
match any of the supported image formats: ..."} >>, and 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 any stream is decoded.
Inspect C<$@> as a string rather than testing for the exception class.
The archive is sent as one buffered request body, so loading a large image
costs its full size in RAM.
=head2 commit
my $result = $images->commit(
container => $container_id,
repo => 'myapp',
tag => 'snapshot',
comment => 'after the migration ran',
);
my $image_id = $result->{Id};
# With a config override and Dockerfile instructions
$images->commit(
container => $container_id,
repo => 'myapp',
tag => 'v2',
config => { Cmd => [ '/bin/sh' ], Labels => { built => 'here' } },
changes => [ 'EXPOSE 8080', 'LABEL stage=release' ],
);
Create an image from a container's current filesystem. This is the one
image-producing path that does not go through a build context, and it is how a
caller snapshots a container it has been exec-ing into.
Returns the raw daemon response, a HashRef with an C<Id> key. Measured against
Podman 5.4.2 (API 1.41) the status is C<201 Created> and C<Id> is a bare hex
digest with no C<sha256:> prefix; Docker prefixes it. Do not compare it
literally against an id from C<inspect> without normalising.
Options:
=over
=item * C<container> - Container id or name to commit (required)
=item * C<repo> - Repository for the new image, e.g. C<myapp>
=item * C<tag> - Tag for the new image
=item * C<comment> - Commit message stored in the image history
=item * C<author> - Author, e.g. C<< Jane <jane@example.com> >>
=item * C<pause> - Pause the container while committing (engine default is true)
=item * C<changes> - Dockerfile instructions to apply to the new image, as a
single string or an ArrayRef of them; an ArrayRef is joined with newlines,
which is what the engine's parser expects
=item * C<config> - HashRef of container configuration to override on the new
image (C<Cmd>, C<Env>, C<Labels>, C<ExposedPorts>, ...), sent as the request
body. Measured against Podman 5.4.2: C<Cmd> replaces the container's, C<Env>
is merged onto the environment the container inherited, and a C<Labels> here
lands alongside a C<LABEL> given in C<changes> -- the two are applied
together, not one instead of the other
=back
=head2 build_prune
my $result = $images->build_prune(all => 1);
my $freed = $result->{SpaceReclaimed};
# Keep 5 GB of cache
$images->build_prune(keep_storage => 5 * 1024 * 1024 * 1024);
Clear the BuildKit build cache. B<This is not L</prune>>, and the two are not
interchangeable: L</prune> deletes unused I<images>, this deletes the
intermediate I<build cache> that L</build> writes. Neither touches the other's
storage, and on a machine that builds often the build cache is usually the
larger of the two.
Returns the raw daemon response, a HashRef with C<CachesDeleted> and
C<SpaceReclaimed>.
B<Podman does not implement this endpoint.> Measured against 5.4.2 (API 1.41):
C<POST /build/prune> answers C<404 Not Found> with a C<text/plain> body of
C<Not Found> -- not the JSON C<< {"message":...} >> shape its other errors use
-- at every version prefix tried, and there is no C<libpod> equivalent either.
The transport croaks with C<Docker API error (404): Not Found>, the plain body
verbatim, because it is not JSON to unwrap. A caller that must work on both
engines has to treat that 404 as "no build cache to clear here" rather than as
a transport fault.
Options:
=over
=item * C<keep_storage> - Bytes of cache to keep. Sent as the engine's
C<keep-storage>, which is also accepted as the option name; the underscore
form exists because the hyphenated one has to be quoted in a Perl hash
=item * C<all> - Remove all cache, not just the dangling entries
=item * C<filters> - HashRef of filters, e.g. C<< { until => ['24h'] } >>;
values are ArrayRefs of strings, shape-checked and normalised by
L<API::Docker::Role::Filters>, and passed to the transport unencoded because
it JSON-encodes a HashRef params value itself
=back
=head1 SEE ALSO
( run in 1.068 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )