Add batch_get_image support for ECR (#1406)
* Add batch_get_image for ECR * Add tests for batch_get_image * Add tests for batch_get_image * Undo local commits * Undo local commits * Adding object representation for batch_get_image * Update responses. Add a couple more tests.
This commit is contained in:
		
							parent
							
								
									84f2ec5e04
								
							
						
					
					
						commit
						b855fee2e4
					
				| @ -1,14 +1,14 @@ | |||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
| # from datetime import datetime | 
 | ||||||
|  | import hashlib | ||||||
|  | from copy import copy | ||||||
| from random import random | from random import random | ||||||
| 
 | 
 | ||||||
| from moto.core import BaseBackend, BaseModel | from moto.core import BaseBackend, BaseModel | ||||||
| from moto.ec2 import ec2_backends | from moto.ec2 import ec2_backends | ||||||
| from copy import copy |  | ||||||
| import hashlib |  | ||||||
| 
 |  | ||||||
| from moto.ecr.exceptions import ImageNotFoundException, RepositoryNotFoundException | from moto.ecr.exceptions import ImageNotFoundException, RepositoryNotFoundException | ||||||
| 
 | 
 | ||||||
|  | from botocore.exceptions import ParamValidationError | ||||||
| 
 | 
 | ||||||
| DEFAULT_REGISTRY_ID = '012345678910' | DEFAULT_REGISTRY_ID = '012345678910' | ||||||
| 
 | 
 | ||||||
| @ -145,6 +145,17 @@ class Image(BaseObject): | |||||||
|         response_object['imagePushedAt'] = '2017-05-09' |         response_object['imagePushedAt'] = '2017-05-09' | ||||||
|         return response_object |         return response_object | ||||||
| 
 | 
 | ||||||
|  |     @property | ||||||
|  |     def response_batch_get_image(self): | ||||||
|  |         response_object = {} | ||||||
|  |         response_object['imageId'] = {} | ||||||
|  |         response_object['imageId']['imageTag'] = self.image_tag | ||||||
|  |         response_object['imageId']['imageDigest'] = self.get_image_digest() | ||||||
|  |         response_object['imageManifest'] = self.image_manifest | ||||||
|  |         response_object['repositoryName'] = self.repository | ||||||
|  |         response_object['registryId'] = self.registry_id | ||||||
|  |         return response_object | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class ECRBackend(BaseBackend): | class ECRBackend(BaseBackend): | ||||||
| 
 | 
 | ||||||
| @ -245,6 +256,39 @@ class ECRBackend(BaseBackend): | |||||||
|         repository.images.append(image) |         repository.images.append(image) | ||||||
|         return image |         return image | ||||||
| 
 | 
 | ||||||
|  |     def batch_get_image(self, repository_name, registry_id=None, image_ids=None, accepted_media_types=None): | ||||||
|  |         if repository_name in self.repositories: | ||||||
|  |             repository = self.repositories[repository_name] | ||||||
|  |         else: | ||||||
|  |             raise RepositoryNotFoundException(repository_name, registry_id or DEFAULT_REGISTRY_ID) | ||||||
|  | 
 | ||||||
|  |         if not image_ids: | ||||||
|  |             raise ParamValidationError(msg='Missing required parameter in input: "imageIds"') | ||||||
|  | 
 | ||||||
|  |         response = { | ||||||
|  |             'images': [], | ||||||
|  |             'failures': [], | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for image_id in image_ids: | ||||||
|  |             found = False | ||||||
|  |             for image in repository.images: | ||||||
|  |                 if (('imageDigest' in image_id and image.get_image_digest() == image_id['imageDigest']) or | ||||||
|  |                         ('imageTag' in image_id and image.image_tag == image_id['imageTag'])): | ||||||
|  |                     found = True | ||||||
|  |                     response['images'].append(image.response_batch_get_image) | ||||||
|  | 
 | ||||||
|  |         if not found: | ||||||
|  |             response['failures'].append({ | ||||||
|  |                 'imageId': { | ||||||
|  |                     'imageTag': image_id.get('imageTag', 'null') | ||||||
|  |                 }, | ||||||
|  |                 'failureCode': 'ImageNotFound', | ||||||
|  |                 'failureReason': 'Requested image not found' | ||||||
|  |             }) | ||||||
|  | 
 | ||||||
|  |         return response | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| ecr_backends = {} | ecr_backends = {} | ||||||
| for region, ec2_backend in ec2_backends.items(): | for region, ec2_backend in ec2_backends.items(): | ||||||
|  | |||||||
| @ -89,9 +89,13 @@ class ECRResponse(BaseResponse): | |||||||
|                 'ECR.batch_delete_image is not yet implemented') |                 'ECR.batch_delete_image is not yet implemented') | ||||||
| 
 | 
 | ||||||
|     def batch_get_image(self): |     def batch_get_image(self): | ||||||
|         if self.is_not_dryrun('BatchGetImage'): |         repository_str = self._get_param('repositoryName') | ||||||
|             raise NotImplementedError( |         registry_id = self._get_param('registryId') | ||||||
|                 'ECR.batch_get_image is not yet implemented') |         image_ids = self._get_param('imageIds') | ||||||
|  |         accepted_media_types = self._get_param('acceptedMediaTypes') | ||||||
|  | 
 | ||||||
|  |         response = self.ecr_backend.batch_get_image(repository_str, registry_id, image_ids, accepted_media_types) | ||||||
|  |         return json.dumps(response) | ||||||
| 
 | 
 | ||||||
|     def can_paginate(self): |     def can_paginate(self): | ||||||
|         if self.is_not_dryrun('CanPaginate'): |         if self.is_not_dryrun('CanPaginate'): | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ import re | |||||||
| import sure  # noqa | import sure  # noqa | ||||||
| 
 | 
 | ||||||
| import boto3 | import boto3 | ||||||
| from botocore.exceptions import ClientError | from botocore.exceptions import ClientError, ParamValidationError | ||||||
| from dateutil.tz import tzlocal | from dateutil.tz import tzlocal | ||||||
| 
 | 
 | ||||||
| from moto import mock_ecr | from moto import mock_ecr | ||||||
| @ -445,3 +445,117 @@ def test_get_authorization_token_explicit_regions(): | |||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
|     ]) |     ]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @mock_ecr | ||||||
|  | def test_batch_get_image(): | ||||||
|  |     client = boto3.client('ecr', region_name='us-east-1') | ||||||
|  |     _ = client.create_repository( | ||||||
|  |         repositoryName='test_repository' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     _ = client.put_image( | ||||||
|  |         repositoryName='test_repository', | ||||||
|  |         imageManifest=json.dumps(_create_image_manifest()), | ||||||
|  |         imageTag='latest' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     _ = client.put_image( | ||||||
|  |         repositoryName='test_repository', | ||||||
|  |         imageManifest=json.dumps(_create_image_manifest()), | ||||||
|  |         imageTag='v1' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     _ = client.put_image( | ||||||
|  |         repositoryName='test_repository', | ||||||
|  |         imageManifest=json.dumps(_create_image_manifest()), | ||||||
|  |         imageTag='v2' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     response = client.batch_get_image( | ||||||
|  |         repositoryName='test_repository', | ||||||
|  |         imageIds=[ | ||||||
|  |             { | ||||||
|  |                 'imageTag': 'v2' | ||||||
|  |             }, | ||||||
|  |         ], | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     type(response['images']).should.be(list) | ||||||
|  |     len(response['images']).should.be(1) | ||||||
|  | 
 | ||||||
|  |     response['images'][0]['imageManifest'].should.contain("vnd.docker.distribution.manifest.v2+json") | ||||||
|  |     response['images'][0]['registryId'].should.equal("012345678910") | ||||||
|  |     response['images'][0]['repositoryName'].should.equal("test_repository") | ||||||
|  | 
 | ||||||
|  |     response['images'][0]['imageId']['imageTag'].should.equal("v2") | ||||||
|  |     response['images'][0]['imageId']['imageDigest'].should.contain("sha") | ||||||
|  | 
 | ||||||
|  |     type(response['failures']).should.be(list) | ||||||
|  |     len(response['failures']).should.be(0) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @mock_ecr | ||||||
|  | def test_batch_get_image_that_doesnt_exist(): | ||||||
|  |     client = boto3.client('ecr', region_name='us-east-1') | ||||||
|  |     _ = client.create_repository( | ||||||
|  |         repositoryName='test_repository' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     _ = client.put_image( | ||||||
|  |         repositoryName='test_repository', | ||||||
|  |         imageManifest=json.dumps(_create_image_manifest()), | ||||||
|  |         imageTag='latest' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     _ = client.put_image( | ||||||
|  |         repositoryName='test_repository', | ||||||
|  |         imageManifest=json.dumps(_create_image_manifest()), | ||||||
|  |         imageTag='v1' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     _ = client.put_image( | ||||||
|  |         repositoryName='test_repository', | ||||||
|  |         imageManifest=json.dumps(_create_image_manifest()), | ||||||
|  |         imageTag='v2' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     response = client.batch_get_image( | ||||||
|  |         repositoryName='test_repository', | ||||||
|  |         imageIds=[ | ||||||
|  |             { | ||||||
|  |                 'imageTag': 'v5' | ||||||
|  |             }, | ||||||
|  |         ], | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     type(response['images']).should.be(list) | ||||||
|  |     len(response['images']).should.be(0) | ||||||
|  | 
 | ||||||
|  |     type(response['failures']).should.be(list) | ||||||
|  |     len(response['failures']).should.be(1) | ||||||
|  |     response['failures'][0]['failureReason'].should.equal("Requested image not found") | ||||||
|  |     response['failures'][0]['failureCode'].should.equal("ImageNotFound") | ||||||
|  |     response['failures'][0]['imageId']['imageTag'].should.equal("v5") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @mock_ecr | ||||||
|  | def test_batch_get_image_no_tags(): | ||||||
|  |     client = boto3.client('ecr', region_name='us-east-1') | ||||||
|  |     _ = client.create_repository( | ||||||
|  |         repositoryName='test_repository' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     _ = client.put_image( | ||||||
|  |         repositoryName='test_repository', | ||||||
|  |         imageManifest=json.dumps(_create_image_manifest()), | ||||||
|  |         imageTag='latest' | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     error_msg = re.compile( | ||||||
|  |         r".*Missing required parameter in input: \"imageIds\".*", | ||||||
|  |         re.MULTILINE) | ||||||
|  | 
 | ||||||
|  |     client.batch_get_image.when.called_with( | ||||||
|  |         repositoryName='test_repository').should.throw( | ||||||
|  |             ParamValidationError, error_msg) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user