API-Docker
view release on metacpan or search on metacpan
lib/API/Docker/API/Plugins.pm view on Meta::CPAN
%{ $self->_request_options },
);
# A plugin that demands nothing answers a bare `null`: computePrivileges
# builds its result with `var privileges types.PluginPrivileges` and
# appends only what the config asks for, so a nil Go slice reaches the
# wire. The transport decodes that to undef, which no caller can iterate
# and which accept_privileges => 1 would post straight back to
# /plugins/pull as a JSON null. Normalised to the empty list it means.
return [] unless ref $result eq 'ARRAY';
return $result;
}
sub install {
my ($self, $remote, %opts) = @_;
croak __PACKAGE__ . '->install remote reference required' unless $remote;
my $privileges = $self->_privileges_body('install', $remote, %opts);
my %params = ( remote => $remote );
$params{name} = $opts{name} if defined $opts{name};
# exists, not truth: an unset callback is a caller bug, and falling back to
# the buffered path for it would hand a long pull back as silence.
return $self->client->post('/plugins/pull', $privileges,
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 __PACKAGE__ . '->inspect plugin name required' unless $name;
return $self->_wrap('API::Docker::Type::Plugin',
$self->client->get("/plugins/$name/json",
%{ $self->_request_options },
));
}
sub remove {
my ($self, $name, %opts) = @_;
croak __PACKAGE__ . '->remove plugin name required' unless $name;
my %params;
$params{force} = $opts{force} ? 1 : 0 if defined $opts{force};
return $self->client->delete_request("/plugins/$name",
params => \%params,
%{ $self->_request_options },
);
}
sub enable {
my ($self, $name, %opts) = @_;
croak __PACKAGE__ . '->enable plugin name required' unless $name;
# Always sent, and not conditional on the caller passing it: the daemon
# parses this parameter with strconv.Atoi and has no default, so an absent
# timeout is parsed as the empty string and answers 400. See the POD.
my %params = ( timeout => $opts{timeout} // 0 );
return $self->client->post("/plugins/$name/enable", undef,
params => \%params,
%{ $self->_request_options },
);
}
sub disable {
my ($self, $name, %opts) = @_;
croak __PACKAGE__ . '->disable plugin name required' unless $name;
my %params;
$params{force} = $opts{force} ? 1 : 0 if defined $opts{force};
return $self->client->post("/plugins/$name/disable", undef,
params => \%params,
%{ $self->_request_options },
);
}
sub upgrade {
my ($self, $name, %opts) = @_;
croak __PACKAGE__ . '->upgrade plugin name required' unless $name;
my $remote = $opts{remote} // $name;
my $privileges = $self->_privileges_body('upgrade', $remote, %opts);
return $self->client->post("/plugins/$name/upgrade", $privileges,
params => { remote => $remote },
$self->_auth_headers(\%opts),
%{ $self->_request_options },
exists $opts{on_event} ? ( on_event => $opts{on_event} ) : ( ndjson => 1 ),
);
}
sub push {
my ($self, $name, %opts) = @_;
croak __PACKAGE__ . '->push plugin name required' unless $name;
return $self->client->post("/plugins/$name/push", undef,
$self->_auth_headers(\%opts),
%{ $self->_request_options },
exists $opts{on_event} ? ( on_event => $opts{on_event} ) : ( ndjson => 1 ),
);
}
sub configure {
my ($self, $name, @settings) = @_;
croak __PACKAGE__ . '->configure plugin name required' unless $name;
# One ArrayRef or a plain list, and nothing after either: this method reads
# no options at all. The ArrayRef form used to be where the transport bounds
# went, because a trailing `read_timeout => 2` in the plain list would be two
# more settings as far as this method can tell -- they now go on the resource
# class instead (karr k74), and what is left is a form, not a split.
if (ref $settings[0] eq 'ARRAY') {
my $list = shift @settings;
croak __PACKAGE__ . '->configure takes nothing after the ArrayRef of '
lib/API/Docker/API/Plugins.pm view on Meta::CPAN
L<API::Docker::Type::Plugin::Config> whose C<< ->env >> is a list of
L<API::Docker::Type::PluginEnv> objects describing those same variables. The
entity's methods thread the plugin's name back through this class.
Everything else returns the decoded engine response as it came: L</privileges>
an ArrayRef of privilege HashRefs, L</install>, L</upgrade> and L</push> an
ArrayRef of progress events, and L</enable>, L</disable>, L</remove> and
L</configure> C<undef>.
=head2 client
Reference to L<API::Docker> client. Weak reference to avoid circular dependencies.
=head2 list
my $plugins = $plugins->list;
my $enabled = $plugins->list(filters => { enabled => ['true'] });
List installed plugins. Returns an ArrayRef of L<API::Docker::Type::Plugin>
objects, each carrying the methods of L<API::Docker::Role::Entity::Plugin>.
An engine with no plugins installed answers C<[]>, never C<null>, so this is
an empty ArrayRef rather than C<undef>.
Options:
=over
=item * C<filters> - HashRef of filters, JSON-encoded by the transport. Values
are ArrayRefs of strings even for booleans -- L<API::Docker::Role::Filters>
shape-checks and normalises that, but not the names, which the daemon
validates itself
=back
The accepted filter names are C<enabled> and C<capability>. B<It is C<enabled>,
not C<enable>> -- the published Engine API reference says C<enable>, and the
daemon validates plugin filter names against its own list, so the documented
spelling is refused outright rather than silently matching nothing. C<enabled>
takes C<['true']> or C<['false']>; C<capability> takes a capability name such
as C<['volumedriver']>.
=head2 privileges
my $privileges = $plugins->privileges('vieux/sshfs:latest');
Get the privileges a plugin demands, without installing it. Returns an
ArrayRef of HashRefs:
[ { Name => 'network', Description => '', Value => ['host'] },
{ Name => 'mount', Description => '', Value => ['/var/lib/docker/plugins/'] } ]
This is the first half of the install; see L</"Installing is two calls, and
the engine enforces it">. Reading it is the point -- the result is what you
hand to L</install>, and the daemon accepts the install only if the two
lists agree.
A plugin that demands nothing answers with an empty ArrayRef.
The C<remote> reference is normalised by the daemon, so C<vieux/sshfs> and
C<docker.io/vieux/sshfs:latest> name the same plugin; C<:latest> is the
default when no tag is given.
Options:
=over
=item * C<auth> - Registry credentials for a plugin in a private registry;
HashRef of C<username> / C<password> / C<serveraddress> / C<identitytoken>,
or a pre-encoded base64 string. Sent as C<X-Registry-Auth>. The Engine API
reference does not document this header on this endpoint, but the daemon
reads it here exactly as it does on the pull
=back
=head2 install
my $privileges = $plugins->privileges('vieux/sshfs:latest');
$plugins->install('vieux/sshfs:latest', privileges => $privileges);
# blanket grant, in one call
$plugins->install('vieux/sshfs:latest', accept_privileges => 1);
Pull and install a plugin (C<< POST /plugins/pull >>). The plugin is installed
disabled -- call L</enable> afterwards.
C<privileges> is required. Without it this croaks and names both ways
forward; see L</"Installing is two calls, and the engine enforces it"> for
why it is not defaulted.
Options:
=over
=item * C<privileges> - ArrayRef of privilege HashRefs from L</privileges>.
Required, unless C<accept_privileges> is set
=item * C<accept_privileges> - Fetch the privileges and grant them, in one
call. A blanket grant: use it where the call site is allowed to trust the
plugin, and know that it reads as consent to whatever the plugin demands
=item * C<name> - Local name for the installed plugin, if it should differ
from C<remote>. A digest is not allowed here
=item * C<auth> - Registry credentials, as for L</privileges>
=item * C<on_event> - CodeRef called with each progress event as it arrives,
instead of the ArrayRef being collected and returned; see below
=back
Returns an ArrayRef of progress events, one per object in the engine's
newline-delimited JSON stream, C<[]> when the engine sent no progress
at all.
=head2 Progress as it arrives
Without a callback the whole stream is read before anything is parsed, so
pulling a plugin is silence until it is done. Pass C<on_event> and the events
are handed over as the daemon sends them:
my $summary = $plugins->install('vieux/sshfs:latest',
privileges => $privileges,
on_event => sub {
my ($event, $stop) = @_;
print $event->{status}, "\n" if defined $event->{status};
},
);
$summary; # { delivered => 18, 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.
See L<API::Docker::Role::HTTP/"Streaming a response as it arrives">.
The C<errorDetail> check runs on this path too, per event rather than over the
finished list, so a failure inside the 200 stream still croaks with an
L<API::Docker::Error::Stream> -- at the event that reports it, and carrying
that one event alone rather than the whole stream. It is the difference
L<API::Docker::API::Images/"A failed build still croaks, one event earlier">
describes, and it applies here identically. A caller that wants the progress
that preceded a failure must collect it in the callback.
L</upgrade> and L</push> take C<on_event> on the same terms.
A failed install croaks by one of two routes, exactly as
L<API::Docker::API::Images/pull> does, because the daemon commits to HTTP 200
the moment it flushes the first progress object. A failure before that point
arrives as a real error status -- C<incorrect privileges> is reported this
way, since it is decided before anything is pulled -- and one after it
arrives as an C<errorDetail> object inside the 200 stream, which croaks with
an L<API::Docker::Error::Stream>. C<eval> and inspect C<$@> as a string
rather than testing for the exception class.
=head2 inspect
my $plugin = $plugins->inspect('vieux/sshfs:latest');
say $plugin->enabled;
say join ', ', @{ $plugin->settings->env };
Get detailed information about an installed plugin. Returns an
L<API::Docker::Type::Plugin> -- the same class L</list> returns; see
L</"What this class returns">.
The name may carry a registry host, a repository path and a tag
(C<docker.io/vieux/sshfs:latest>) and is interpolated into the request path
as given: the daemon routes this endpoint as C<< /plugins/{name:.*}/json >>,
so the slashes and the colon must survive unescaped, and they do.
=head2 remove
$plugins->remove('vieux/sshfs:latest');
$plugins->remove('vieux/sshfs:latest', force => 1);
Remove an installed plugin. A plugin that is still enabled is refused unless
C<force> is set.
Options:
=over
=item * C<force> - Disable the plugin before removing it. Removing a plugin
that containers are still using will break them
=back
Returns C<undef>. The Engine API reference documents a C<Plugin> object as
the 200 response body here; the daemon writes no body at all.
=head2 enable
$plugins->enable('vieux/sshfs:latest');
$plugins->enable('vieux/sshfs:latest', timeout => 30);
Enable an installed plugin. Returns C<undef>.
Options:
=over
=item * C<timeout> - Seconds to wait for the plugin to come up, C<0> for no
timeout (the default)
=back
C<timeout> is B<always> sent, whether or not the caller passes it. The Engine
API reference gives it a default of C<0>, but the daemon has none: it reads
the raw query value and parses it with Go's C<strconv.Atoi>, so an absent
parameter is parsed as the empty string and the request fails with
C<strconv.Atoi: parsing "": invalid syntax> as an invalid-parameter error.
This is the one endpoint in the family where omitting an optional parameter
is fatal.
=head2 disable
$plugins->disable('vieux/sshfs:latest');
$plugins->disable('vieux/sshfs:latest', force => 1);
Disable an enabled plugin. Returns C<undef>.
Options:
=over
=item * C<force> - Disable even while the plugin is in use. Mounts held by
the plugin stay behind, which is what makes a later L</remove> fail
=back
=head2 upgrade
my $privileges = $plugins->privileges('vieux/sshfs:latest');
$plugins->upgrade('vieux/sshfs:latest', privileges => $privileges);
# upgrade a locally renamed plugin from its upstream reference
$plugins->upgrade('sshfs', remote => 'vieux/sshfs:v2',
accept_privileges => 1);
Upgrade an installed plugin in place. The plugin must be disabled first.
Like L</install> this carries the privilege list in its body and the daemon
checks it against what the new version demands, so C<privileges> is required
here too -- an upgrade is where a plugin's demands can B<change>, which is
the case worth looking at.
Options:
=over
=item * C<privileges> - ArrayRef of privilege HashRefs. Required, unless
C<accept_privileges> is set
=item * C<accept_privileges> - Fetch the privileges for C<remote> and grant
them, in one call
=item * C<remote> - Remote reference to upgrade to. Defaults to C<$name>,
which is what you want unless the plugin was installed under a local name
=item * C<auth> - Registry credentials, as for L</privileges>
=item * C<on_event> - CodeRef called with each progress event as it arrives.
The return value is then the summary HashRef; see
L</"Progress as it arrives">
=back
Returns an ArrayRef of progress events, C<[]> when the engine sent no
( run in 1.053 second using v1.01-cache-2.11-cpan-c221a9de4ec )