Rewrite token handling to simplify the "registry" interface (especially now that we need to expand that interface with more requests)

This commit is contained in:
Tianon Gravi 2016-06-10 14:25:13 -07:00
parent dfb9527030
commit a558264f9c
1 changed files with 52 additions and 32 deletions

View File

@ -19,7 +19,7 @@ sub ua_req {
do { do {
--$tries; --$tries;
$tx = $ua->$method(@_); $tx = $ua->$method(@_);
return $tx if $tx->success; return $tx if $tx->success or $tx->res->code == 401 or $tx->res->code == 404;
} while ($tries > 0); } while ($tries > 0);
return $tx; return $tx;
} }
@ -43,15 +43,32 @@ sub split_image_name {
die "unrecognized image name format in: $image"; die "unrecognized image name format in: $image";
} }
sub get_token { sub registry_req {
my $method = shift;
my $repo = shift; my $repo = shift;
my $url = shift;
my %extHeaders = @_;
state %tokens; state %tokens;
return $tokens{$repo} if $tokens{$repo};
my $realmTx = $ua->get("https://registry-1.docker.io/v2/$repo/tags/list"); $url = "https://registry-1.docker.io/v2/$repo/$url";
my $auth = $realmTx->res->headers->www_authenticate;
for (;;) {
my %headers = (
%extHeaders,
);
if (my $token = $tokens{$repo}) {
$headers{Authorization} = "Bearer $token";
}
my $tx = ua_req($method => $url => \%headers);
if ($tx->res->code == 401) {
my $auth = $tx->res->headers->www_authenticate;
die "unexpected WWW-Authenticate header: $auth" unless $auth =~ m{ ^ Bearer \s+ (\S.*) $ }x; die "unexpected WWW-Authenticate header: $auth" unless $auth =~ m{ ^ Bearer \s+ (\S.*) $ }x;
my $realm = $1; my $realm = $1;
my $url = Mojo::URL->new; my $authUrl = Mojo::URL->new;
while ($realm =~ m{ while ($realm =~ m{
# key="val", # key="val",
([^=]+) ([^=]+)
@ -61,27 +78,32 @@ sub get_token {
}xg) { }xg) {
my ($key, $val) = ($1, $2); my ($key, $val) = ($1, $2);
if ($key eq 'realm') { if ($key eq 'realm') {
$url->base(Mojo::URL->new($val)); $authUrl->base(Mojo::URL->new($val));
} else { } else {
$url->query->append($key => $val); $authUrl->query->append($key => $val);
} }
} }
$url = $url->to_abs; $authUrl = $authUrl->to_abs;
my $tokenTx = ua_req(get => $url); my $tokenTx = ua_req(get => $authUrl);
die "failed to fetch token for $repo" unless $tokenTx->success; die "failed to fetch token for $repo" unless $tokenTx->success;
return $tokens{$repo} = $tokenTx->res->json->{token}; $tokens{$repo} = $tokenTx->res->json->{token};
next;
}
return $tx;
}
} }
sub get_manifest { sub get_manifest {
my ($repo, $tag) = @_; my ($repo, $tag) = @_;
my $image = "$repo:$tag"; my $image = "$repo:$tag";
state %manifests; state %manifests;
return $manifests{$image} if $manifests{$image}; return $manifests{$image} if $manifests{$image};
my $token = get_token($repo); my $manifestTx = registry_req(get => $repo => "manifests/$tag" => (
my $authorizationHeader = { Authorization => "Bearer $token" }; #Accept => 'application/vnd.docker.distribution.manifest.v2+json',
));
my $manifestTx = ua_req(get => "https://registry-1.docker.io/v2/$repo/manifests/$tag" => $authorizationHeader);
return () if $manifestTx->res->code == 404; # tag doesn't exist return () if $manifestTx->res->code == 404; # tag doesn't exist
die "failed to get manifest for $image" unless $manifestTx->success; die "failed to get manifest for $image" unless $manifestTx->success;
return ( return (
@ -92,14 +114,12 @@ sub get_manifest {
sub get_blob_headers { sub get_blob_headers {
my ($repo, $blob) = @_; my ($repo, $blob) = @_;
my $key = $repo . '@' . $blob; my $key = $repo . '@' . $blob;
state %headers; state %headers;
return $headers{$key} if $headers{$key}; return $headers{$key} if $headers{$key};
my $token = get_token($repo); my $headersTx = registry_req(head => $repo => "blobs/$blob" => ());
my $authorizationHeader = { Authorization => "Bearer $token" };
my $headersTx = ua_req(head => "https://registry-1.docker.io/v2/$repo/blobs/$blob" => $authorizationHeader);
die "failed to get headers for $key" unless $headersTx->success; die "failed to get headers for $key" unless $headersTx->success;
return $headers{$key} = $headersTx->res->headers; return $headers{$key} = $headersTx->res->headers;
} }