line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Docker::Registry::Call::Repositories; |
2
|
5
|
|
|
5
|
|
161080
|
use Moo; |
|
5
|
|
|
|
|
11554
|
|
|
5
|
|
|
|
|
26
|
|
3
|
5
|
|
|
5
|
|
3440
|
use Types::Standard qw/Int Str/; |
|
5
|
|
|
|
|
74504
|
|
|
5
|
|
|
|
|
35
|
|
4
|
|
|
|
|
|
|
has n => (is => 'ro', isa => Int); |
5
|
|
|
|
|
|
|
has last => (is => 'ro', isa => Str); |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
package Docker::Registry::Result::Repositories; |
8
|
5
|
|
|
5
|
|
4030
|
use Moo; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
24
|
|
9
|
5
|
|
|
5
|
|
1732
|
use Types::Standard qw/ArrayRef Str/; |
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
46
|
|
10
|
|
|
|
|
|
|
has repositories => (is => 'ro', isa => ArrayRef[Str]); |
11
|
|
|
|
|
|
|
has last => (is => 'ro', isa => Str); |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
package Docker::Registry::Call::RepositoryTags; |
14
|
5
|
|
|
5
|
|
3347
|
use Moo; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
21
|
|
15
|
5
|
|
|
5
|
|
1699
|
use Types::Standard qw/Int Str/; |
|
5
|
|
|
|
|
13
|
|
|
5
|
|
|
|
|
29
|
|
16
|
|
|
|
|
|
|
has repository => (is => 'ro', isa => Str, required => 1); |
17
|
|
|
|
|
|
|
has n => (is => 'ro', isa => Int); |
18
|
|
|
|
|
|
|
has last => (is => 'ro', isa => Str); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
package Docker::Registry::Result::RepositoryTags; |
21
|
5
|
|
|
5
|
|
3181
|
use Moo; |
|
5
|
|
|
|
|
11
|
|
|
5
|
|
|
|
|
22
|
|
22
|
5
|
|
|
5
|
|
1662
|
use Types::Standard qw/ArrayRef Str/; |
|
5
|
|
|
|
|
22
|
|
|
5
|
|
|
|
|
43
|
|
23
|
|
|
|
|
|
|
has name => (is => 'ro', isa => Str, required => 1); |
24
|
|
|
|
|
|
|
has tags => (is => 'ro', isa => ArrayRef[Str], required => 1); |
25
|
|
|
|
|
|
|
has last => (is => 'ro', isa => Str); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
package Docker::Registry::V2; |
28
|
5
|
|
|
5
|
|
3116
|
use Moo; |
|
5
|
|
|
|
|
19
|
|
|
5
|
|
|
|
|
23
|
|
29
|
5
|
|
|
5
|
|
3529
|
use Docker::Registry::Types qw(DockerRegistryURI); |
|
5
|
|
|
|
|
18
|
|
|
5
|
|
|
|
|
48
|
|
30
|
5
|
|
|
5
|
|
1755
|
use Types::Standard qw/Str ConsumerOf InstanceOf/; |
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
25
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
has url => (is => 'ro', coerce => 1, isa => DockerRegistryURI, required => 1); |
33
|
|
|
|
|
|
|
has api_base => (is => 'ro', default => 'v2'); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
has caller => (is => 'ro', isa => ConsumerOf['Docker::Registry::IO'], default => sub { |
36
|
|
|
|
|
|
|
require Docker::Registry::IO::Simple; |
37
|
|
|
|
|
|
|
Docker::Registry::IO::Simple->new; |
38
|
|
|
|
|
|
|
}); |
39
|
|
|
|
|
|
|
has auth => (is => 'ro', isa => ConsumerOf['Docker::Registry::Auth'], lazy => 1, builder => 'build_auth' ); |
40
|
|
|
|
|
|
|
has request_builder => (is => 'ro', isa => InstanceOf['Docker::Registry::RequestBuilder'], lazy => 1, default => sub { |
41
|
|
|
|
|
|
|
my $self = shift; |
42
|
|
|
|
|
|
|
require Docker::Registry::RequestBuilder; |
43
|
|
|
|
|
|
|
Docker::Registry::RequestBuilder->new(url => $self->url, api_base => $self->api_base); |
44
|
|
|
|
|
|
|
}); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub build_auth { |
47
|
1
|
|
|
1
|
0
|
463
|
require Docker::Registry::Auth::None; |
48
|
1
|
|
|
|
|
6
|
Docker::Registry::Auth::None->new; |
49
|
|
|
|
|
|
|
}; |
50
|
|
|
|
|
|
|
|
51
|
5
|
|
|
5
|
|
7197
|
use JSON::MaybeXS qw//; |
|
5
|
|
|
|
|
23477
|
|
|
5
|
|
|
|
|
953
|
|
52
|
|
|
|
|
|
|
has _json => (is => 'ro', default => sub { |
53
|
|
|
|
|
|
|
JSON::MaybeXS->new; |
54
|
|
|
|
|
|
|
}); |
55
|
|
|
|
|
|
|
sub process_json_response { |
56
|
14
|
|
|
14
|
0
|
45
|
my ($self, $response) = @_; |
57
|
14
|
100
|
|
|
|
85
|
if ($response->status == 200) { |
|
|
50
|
|
|
|
|
|
58
|
11
|
|
|
|
|
23
|
my $struct = eval { |
59
|
11
|
|
|
|
|
184
|
$self->_json->decode($response->content); |
60
|
|
|
|
|
|
|
}; |
61
|
11
|
50
|
|
|
|
40
|
if ($@) { |
62
|
0
|
|
|
|
|
0
|
Docker::Registry::Exception->throw({ message => $@ }); |
63
|
|
|
|
|
|
|
} |
64
|
11
|
|
|
|
|
44
|
my $pagination = $self->_parse_pagination_header($response); |
65
|
11
|
|
|
|
|
285
|
return { %$struct, %$pagination }; |
66
|
|
|
|
|
|
|
} elsif ($response->status == 401) { |
67
|
0
|
|
|
|
|
0
|
Docker::Registry::Exception::Unauthorized->throw({ |
68
|
|
|
|
|
|
|
message => $response->content, |
69
|
|
|
|
|
|
|
status => $response->status, |
70
|
|
|
|
|
|
|
}); |
71
|
|
|
|
|
|
|
} else { |
72
|
3
|
|
|
|
|
69
|
Docker::Registry::Exception::HTTP->throw({ |
73
|
|
|
|
|
|
|
message => $response->content, |
74
|
|
|
|
|
|
|
status => $response->status |
75
|
|
|
|
|
|
|
}); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
5
|
|
|
5
|
|
39
|
use URI; |
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
2152
|
|
80
|
|
|
|
|
|
|
sub _parse_pagination_header { |
81
|
11
|
|
|
11
|
|
30
|
my ($self, $response) = @_; |
82
|
|
|
|
|
|
|
|
83
|
11
|
100
|
|
|
|
240
|
return {} unless($response->headers->{link}); |
84
|
|
|
|
|
|
|
|
85
|
1
|
|
|
|
|
25
|
my ($link) = ($response->headers->{link} =~ /<([^>]*)>/); |
86
|
1
|
|
|
|
|
15
|
my $url = URI->new($link); |
87
|
1
|
|
|
|
|
75
|
my %url_params = $url->query_form; |
88
|
|
|
|
|
|
|
|
89
|
1
|
|
|
|
|
75
|
return {last => $url_params{last}}; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub repositories { |
93
|
6
|
|
|
6
|
0
|
14857
|
my $self = shift; |
94
|
|
|
|
|
|
|
# Inputs n, last |
95
|
|
|
|
|
|
|
# |
96
|
|
|
|
|
|
|
# GET /v2/_catalog |
97
|
|
|
|
|
|
|
# |
98
|
|
|
|
|
|
|
# Header next |
99
|
|
|
|
|
|
|
# { |
100
|
|
|
|
|
|
|
# "repositories": [ |
101
|
|
|
|
|
|
|
# , |
102
|
|
|
|
|
|
|
# ... |
103
|
|
|
|
|
|
|
# ] |
104
|
|
|
|
|
|
|
# } |
105
|
6
|
|
|
|
|
14
|
my $call_class = 'Docker::Registry::Call::Repositories'; |
106
|
6
|
|
|
|
|
98
|
my $call = $call_class->new({ @_ }); |
107
|
|
|
|
|
|
|
|
108
|
6
|
|
|
|
|
6028
|
my $request = $self->request_builder->build_request($call); |
109
|
|
|
|
|
|
|
|
110
|
6
|
|
|
|
|
16
|
my $scope = 'registry:catalog:*'; |
111
|
6
|
|
|
|
|
129
|
$request = $self->auth->authorize($request, $scope); |
112
|
6
|
|
|
|
|
53
|
my $response = $self->caller->send_request($request); |
113
|
6
|
|
|
|
|
8587
|
my $result_class = 'Docker::Registry::Result::Repositories'; |
114
|
6
|
|
|
|
|
32
|
my $result = $result_class->new($self->process_json_response($response)); |
115
|
3
|
|
|
|
|
6237
|
return $result; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub repository_tags { |
119
|
8
|
|
|
8
|
0
|
11416
|
my $self = shift; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# n, last |
122
|
|
|
|
|
|
|
#GET /v2/$repository/tags/list |
123
|
|
|
|
|
|
|
# |
124
|
|
|
|
|
|
|
#{"name":"$repository","tags":["2017.09","latest"]} |
125
|
8
|
|
|
|
|
21
|
my $call_class = 'Docker::Registry::Call::RepositoryTags'; |
126
|
8
|
|
|
|
|
140
|
my $call = $call_class->new({ @_ }); |
127
|
|
|
|
|
|
|
|
128
|
8
|
|
|
|
|
13194
|
my $request = $self->request_builder->build_request($call); |
129
|
|
|
|
|
|
|
|
130
|
8
|
|
|
|
|
77
|
my $scope = sprintf 'repository:%s:%s', $call->repository, 'pull'; |
131
|
8
|
|
|
|
|
175
|
$request = $self->auth->authorize($request, $scope); |
132
|
8
|
|
|
|
|
56
|
my $response = $self->caller->send_request($request); |
133
|
8
|
|
|
|
|
3342
|
my $result_class = 'Docker::Registry::Result::RepositoryTags'; |
134
|
8
|
|
|
|
|
38
|
my $result = $result_class->new($self->process_json_response($response)); |
135
|
8
|
|
|
|
|
14115
|
return $result; |
136
|
|
|
|
|
|
|
} |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub is_registry { |
139
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
140
|
|
|
|
|
|
|
# GET /v2 |
141
|
|
|
|
|
|
|
# if (200 or 401) and (header.Docker-Distribution-API-Version eq 'registry/2.0') |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# Actionable failure conditions, covered in detail in their relevant sections, |
145
|
|
|
|
|
|
|
# are reported as part of 4xx responses, in a json response body. One or more |
146
|
|
|
|
|
|
|
# errors will be returned in the following format: |
147
|
|
|
|
|
|
|
# { |
148
|
|
|
|
|
|
|
# "errors:" [{ |
149
|
|
|
|
|
|
|
# "code": , |
150
|
|
|
|
|
|
|
# "message": , |
151
|
|
|
|
|
|
|
# "detail": |
152
|
|
|
|
|
|
|
# }, |
153
|
|
|
|
|
|
|
# ... |
154
|
|
|
|
|
|
|
# ] |
155
|
|
|
|
|
|
|
# } |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# ECR: returns 401 error body as "Not Authorized" |
158
|
|
|
|
0
|
0
|
|
sub process_error { |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
0
|
0
|
|
sub request { |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
1; |