File Coverage

blib/lib/API/Docker/API/Images.pm
Criterion Covered Total %
statement 99 108 91.6
branch 46 100 46.0
condition 2 4 50.0
subroutine 16 17 94.1
pod 10 10 100.0
total 173 239 72.3


line stmt bran cond sub pod time code
1             package API::Docker::API::Images;
2             # ABSTRACT: Docker Engine Images API
3             our $VERSION = '0.002';
4 8     8   98127 use Moo;
  8         6395  
  8         53  
5 8     8   9560 use API::Docker::Image;
  8         25  
  8         383  
6 8     8   55 use Carp qw( croak );
  8         13  
  8         528  
7 8     8   40 use namespace::clean;
  8         16  
  8         36  
8              
9              
10             has client => (
11             is => 'ro',
12             required => 1,
13             weak_ref => 1,
14             );
15              
16              
17             sub _wrap {
18 3     3   7 my ($self, $data) = @_;
19 3         86 return API::Docker::Image->new(
20             client => $self->client,
21             %$data,
22             );
23             }
24              
25             sub _wrap_list {
26 1     1   4 my ($self, $list) = @_;
27 1         4 return [ map { $self->_wrap($_) } @$list ];
  2         3047  
28             }
29              
30             sub list {
31 1     1 1 1499 my ($self, %opts) = @_;
32 1         3 my %params;
33 1 0       5 $params{all} = $opts{all} ? 1 : 0 if defined $opts{all};
    50          
34 1 0       5 $params{digests} = $opts{digests} ? 1 : 0 if defined $opts{digests};
    50          
35 1 50       4 $params{filters} = $opts{filters} if defined $opts{filters};
36 1         14 my $result = $self->client->get('/images/json', params => \%params);
37 1   50     26 return $self->_wrap_list($result // []);
38             }
39              
40              
41             sub build {
42 2     2 1 64 my ($self, %opts) = @_;
43 2         7 my $context = delete $opts{context};
44 2 100       273 croak "Build context required (tar archive as scalar ref or raw bytes)" unless defined $context;
45              
46 1         3 my %params;
47 1 50       7 $params{dockerfile} = $opts{dockerfile} if defined $opts{dockerfile};
48 1 50       5 $params{t} = $opts{t} if defined $opts{t};
49 1 0       7 $params{q} = $opts{q} ? 1 : 0 if defined $opts{q};
    50          
50 1 0       4 $params{nocache} = $opts{nocache} ? 1 : 0 if defined $opts{nocache};
    50          
51 1 50       4 $params{pull} = $opts{pull} if defined $opts{pull};
52 1 0       4 $params{rm} = defined $opts{rm} ? ($opts{rm} ? 1 : 0) : 1;
    50          
53 1 0       4 $params{forcerm} = $opts{forcerm} ? 1 : 0 if defined $opts{forcerm};
    50          
54 1 50       4 $params{memory} = $opts{memory} if defined $opts{memory};
55 1 50       4 $params{memswap} = $opts{memswap} if defined $opts{memswap};
56 1 50       3 $params{cpushares} = $opts{cpushares} if defined $opts{cpushares};
57 1 50       4 $params{cpusetcpus} = $opts{cpusetcpus} if defined $opts{cpusetcpus};
58 1 50       20 $params{cpuperiod} = $opts{cpuperiod} if defined $opts{cpuperiod};
59 1 50       7 $params{cpuquota} = $opts{cpuquota} if defined $opts{cpuquota};
60 1 50       5 $params{shmsize} = $opts{shmsize} if defined $opts{shmsize};
61 1 50       4 $params{networkmode} = $opts{networkmode} if defined $opts{networkmode};
62 1 50       3 $params{platform} = $opts{platform} if defined $opts{platform};
63 1 50       4 $params{target} = $opts{target} if defined $opts{target};
64              
65 1 50       3 if ($opts{buildargs}) {
66 0         0 require JSON::MaybeXS;
67 0         0 $params{buildargs} = JSON::MaybeXS::encode_json($opts{buildargs});
68             }
69 1 50       5 if ($opts{labels}) {
70 0         0 require JSON::MaybeXS;
71 0         0 $params{labels} = JSON::MaybeXS::encode_json($opts{labels});
72             }
73              
74 1 50       4 my $raw = ref $context eq 'SCALAR' ? $$context : $context;
75              
76 1         8 return $self->client->_request('POST', '/build',
77             raw_body => $raw,
78             content_type => 'application/x-tar',
79             params => \%params,
80             );
81             }
82              
83              
84             sub pull {
85 1     1 1 3170 my ($self, %opts) = @_;
86 1 50       7 croak "fromImage required" unless $opts{fromImage};
87 1         2 my %params;
88 1         3 $params{fromImage} = $opts{fromImage};
89 1   50     5 $params{tag} = $opts{tag} // 'latest';
90 1         14 return $self->client->post('/images/create', undef, params => \%params);
91             }
92              
93              
94             sub inspect {
95 2     2 1 91 my ($self, $name) = @_;
96 2 100       182 croak "Image name required" unless $name;
97 1         13 my $result = $self->client->get("/images/$name/json");
98 1         23 return $self->_wrap($result);
99             }
100              
101              
102             sub history {
103 1     1 1 32 my ($self, $name) = @_;
104 1 50       6 croak "Image name required" unless $name;
105 1         13 return $self->client->get("/images/$name/history");
106             }
107              
108              
109             sub push {
110 1     1 1 1033 my ($self, $name, %opts) = @_;
111 1 50       4 croak "Image name required" unless $name;
112 1         1 my %params;
113 1 50       5 $params{tag} = $opts{tag} if defined $opts{tag};
114              
115 1         4 my $auth_header = _build_registry_auth_header($opts{auth});
116              
117 1         9 return $self->client->post(
118             "/images/$name/push",
119             undef,
120             params => \%params,
121             headers => { 'X-Registry-Auth' => $auth_header },
122             );
123             }
124              
125             sub _build_registry_auth_header {
126 5     5   249845 my ($auth) = @_;
127              
128             # The Docker Engine requires an X-Registry-Auth header on every push,
129             # even for anonymous attempts. Encoding is base64url of a JSON object.
130 5         29 require JSON::MaybeXS;
131 5         12 require MIME::Base64;
132              
133 5         12 my $payload;
134 5 100       39 if (!defined $auth) {
    100          
135 1         2 $payload = '{}';
136             }
137             elsif (ref $auth eq 'HASH') {
138 3         21 $payload = JSON::MaybeXS::encode_json($auth);
139             }
140             else {
141             # Already pre-built JSON or pre-encoded string. If it looks base64-like
142             # (no braces), pass through; otherwise encode as-is.
143 1 50       9 return $auth if $auth =~ /^[A-Za-z0-9+\/=_\-]+$/;
144 0         0 $payload = $auth;
145             }
146              
147 4         17 my $b64 = MIME::Base64::encode_base64($payload, '');
148 4         7 $b64 =~ tr{+/}{-_};
149 4         23 $b64 =~ s/=+$//;
150 4         14 return $b64;
151             }
152              
153              
154             sub tag {
155 1     1 1 588 my ($self, $name, %opts) = @_;
156 1 50       6 croak "Image name required" unless $name;
157 1         2 my %params;
158 1 50       6 $params{repo} = $opts{repo} if defined $opts{repo};
159 1 50       5 $params{tag} = $opts{tag} if defined $opts{tag};
160 1         9 return $self->client->post("/images/$name/tag", undef, params => \%params);
161             }
162              
163              
164             sub remove {
165 2     2 1 1524 my ($self, $name, %opts) = @_;
166 2 100       145 croak "Image name required" unless $name;
167 1         2 my %params;
168 1 0       4 $params{force} = $opts{force} ? 1 : 0 if defined $opts{force};
    50          
169 1 0       3 $params{noprune} = $opts{noprune} ? 1 : 0 if defined $opts{noprune};
    50          
170 1         14 return $self->client->delete_request("/images/$name", params => \%params);
171             }
172              
173              
174             sub search {
175 1     1 1 32 my ($self, $term, %opts) = @_;
176 1 50       5 croak "Search term required" unless $term;
177 1         2 my %params;
178 1         4 $params{term} = $term;
179 1 50       5 $params{limit} = $opts{limit} if defined $opts{limit};
180 1 50       4 $params{filters} = $opts{filters} if defined $opts{filters};
181 1         12 return $self->client->get('/images/search', params => \%params);
182             }
183              
184              
185             sub prune {
186 0     0 1   my ($self, %opts) = @_;
187 0           my %params;
188 0 0         $params{filters} = $opts{filters} if defined $opts{filters};
189 0           return $self->client->post('/images/prune', undef, params => \%params);
190             }
191              
192              
193              
194             1;
195              
196             __END__