API-Docker
view release on metacpan or search on metacpan
t/plugins.t view on Meta::CPAN
# `strconv.Atoi: parsing "": invalid syntax`. Making this parameter
# conditional, the way force and filters are, would break every enable.
$c->plugins->enable('vieux/sshfs:latest', timeout => 30);
is query_param($c->written, 'timeout'), '30', 'an explicit timeout is used';
$c->plugins->enable('vieux/sshfs:latest', timeout => 0);
is query_param($c->written, 'timeout'), '0', 'an explicit 0 stays 0';
};
subtest 'disable: force is optional' => sub {
my $c = fake_client('');
$c->plugins->disable('vieux/sshfs:latest');
is request_line($c->written),
'POST /v1.41/plugins/vieux/sshfs:latest/disable HTTP/1.1',
'no force parameter by default';
$c->plugins->disable('vieux/sshfs:latest', force => 1);
is query_param($c->written, 'force'), '1', 'force => 1';
};
# ---------------------------------------------------------------------------
subtest 'privileges: remote in the query, list in the response' => sub {
my $c = fake_client(JSON::MaybeXS->new->encode($PRIVILEGES));
my $got = $c->plugins->privileges('vieux/sshfs');
is request_line($c->written),
'GET /v1.41/plugins/privileges?remote=vieux/sshfs HTTP/1.1',
'remote is a query parameter, and its slash is not escaped';
is_deeply $got, $PRIVILEGES, 'the privilege list comes back as an ArrayRef';
unlike $c->written, qr/X-Registry-Auth/i,
'no auth header without auth: the plugin router discards an '
. 'undecodable one, so anonymous needs none';
};
subtest 'privileges: a plugin that demands nothing answers null' => sub {
# computePrivileges starts from `var privileges types.PluginPrivileges` and
# appends only what the config asks for, so a plugin needing nothing sends
# a nil Go slice, which marshals to a bare `null`.
my $c = fake_client('null');
# karr k30 (fixed): the transport used to decode a body only when it
# started with { or [, so a bare JSON scalar came back as its own bytes --
# the four-character string 'null'. It now decodes any JSON body, scalars
# included, so this comes back as undef, same as decode_json('null') would.
is $c->get('/plugins/privileges', params => { remote => 'x' }), undef,
'the transport decodes the bare null to undef (karr k30)';
# Unguarded, that undef would be POSTed to /plugins/pull as a JSON null
# where the engine expects an array.
is_deeply $c->plugins->privileges('vieux/sshfs'), [],
'privileges normalises it to the empty list it means';
};
subtest 'privileges: auth is sent as padded base64url X-Registry-Auth' => sub {
my $c = fake_client('[]');
$c->plugins->privileges('private.example.com/p/sshfs',
auth => { username => 'me', password => 'secret' });
my ($hdr) = $c->written =~ /^X-Registry-Auth: (\S+)\r$/m;
ok defined $hdr, 'header present when auth was given';
is length($hdr) % 4, 0, 'padded, as Go base64.URLEncoding requires';
is_deeply decode_json(b64url_decode($hdr)),
{ username => 'me', password => 'secret' },
'header decodes to the credentials passed';
};
# ---------------------------------------------------------------------------
subtest 'install: the privilege list is the request body' => sub {
my $c = fake_client(qq({"status":"Pulling plugin"}\n));
my $events = $c->plugins->install('vieux/sshfs:latest',
privileges => $PRIVILEGES);
like request_line($c->written), qr{\APOST /v1\.41/plugins/pull\?},
'install is POST /plugins/pull';
is query_param($c->written, 'remote'), 'vieux/sshfs:latest',
'remote is a query parameter';
like $c->written, qr{^Content-Type: application/json\r$}m,
'the body is JSON, not a tarball';
is_deeply decode_json(request_body($c->written)), $PRIVILEGES,
'the privileges go back to the engine verbatim -- it compares them '
. 'against what the plugin demands and refuses a mismatch';
is_deeply $events, [ { status => 'Pulling plugin' } ],
'the NDJSON progress stream comes back as an ArrayRef of events';
};
subtest 'install: local name and auth' => sub {
my $c = fake_client(qq({"status":"Pulling plugin"}\n));
$c->plugins->install('vieux/sshfs:latest',
privileges => [],
name => 'sshfs',
auth => { identitytoken => 'tok-123' },
);
is query_param($c->written, 'name'), 'sshfs', 'local name sent';
is request_body($c->written), '[]',
'an empty privilege list is sent as an empty JSON array, not omitted';
my ($hdr) = $c->written =~ /^X-Registry-Auth: (\S+)\r$/m;
is_deeply decode_json(b64url_decode($hdr)), { identitytoken => 'tok-123' },
'identitytoken auth reaches the header';
};
subtest 'install: a failure inside the 200 stream croaks' => sub {
my $c = fake_client(
qq({"status":"Pulling plugin"}\n)
. qq({"errorDetail":{"message":"incorrect privileges"},"error":"incorrect privileges"}\n)
);
eval {
$c->plugins->install('vieux/sshfs:latest', privileges => []);
1;
};
my $err = $@;
ok $err, 'a failed install does not return quietly';
like "$err", qr/incorrect privileges/,
'the engine reason survives into the exception';
isa_ok $err, 'API::Docker::Error::Stream';
is_deeply [ map { $_->{status} } grep { $_->{status} } @{ $err->events } ],
['Pulling plugin'], 'the progress that preceded the failure is kept';
};
( run in 1.691 second using v1.01-cache-2.11-cpan-54e63673c56 )