From 3a355f126cea926fd03815ad240e9e29839a5228 Mon Sep 17 00:00:00 2001 From: Stephan Huber Date: Wed, 13 Jun 2018 16:14:18 +0200 Subject: [PATCH] first steps undertaken to fix spulec/moto#1684 and spulec/moto#1685 --- moto/ecr/models.py | 29 +++++++++----- tests/test_ecr/test_ecr_boto3.py | 68 +++++++++++++++++++++++++++++--- 2 files changed, 82 insertions(+), 15 deletions(-) diff --git a/moto/ecr/models.py b/moto/ecr/models.py index e20c550c..87d02c3b 100644 --- a/moto/ecr/models.py +++ b/moto/ecr/models.py @@ -4,12 +4,12 @@ import hashlib from copy import copy from random import random +from botocore.exceptions import ParamValidationError + from moto.core import BaseBackend, BaseModel from moto.ec2 import ec2_backends from moto.ecr.exceptions import ImageNotFoundException, RepositoryNotFoundException -from botocore.exceptions import ParamValidationError - DEFAULT_REGISTRY_ID = '012345678910' @@ -97,13 +97,13 @@ class Repository(BaseObject): class Image(BaseObject): - def __init__(self, tag, manifest, repository, registry_id=DEFAULT_REGISTRY_ID): + def __init__(self, tag, manifest, repository, digest=None, registry_id=DEFAULT_REGISTRY_ID): self.image_tag = tag self.image_manifest = manifest self.image_size_in_bytes = 50 * 1024 * 1024 self.repository = repository self.registry_id = registry_id - self.image_digest = None + self.image_digest = digest self.image_pushed_at = None def _create_digest(self): @@ -115,6 +115,9 @@ class Image(BaseObject): self._create_digest() return self.image_digest + def get_image_manifest(self): + return self.image_manifest + @property def response_object(self): response_object = self.gen_response_object() @@ -124,14 +127,14 @@ class Image(BaseObject): response_object['imageManifest'] = self.image_manifest response_object['repositoryName'] = self.repository response_object['registryId'] = self.registry_id - return response_object + return {k: v for k, v in response_object.items() if v is not None and v != [None]} @property def response_list_object(self): response_object = self.gen_response_object() response_object['imageTag'] = self.image_tag response_object['imageDigest'] = "i don't know" - return response_object + return {k: v for k, v in response_object.items() if v is not None and v != [None]} @property def response_describe_object(self): @@ -143,7 +146,7 @@ class Image(BaseObject): response_object['registryId'] = self.registry_id response_object['imageSizeInBytes'] = self.image_size_in_bytes response_object['imagePushedAt'] = '2017-05-09' - return response_object + return {k: v for k, v in response_object.items() if v is not None and v != [None]} @property def response_batch_get_image(self): @@ -154,7 +157,7 @@ class Image(BaseObject): response_object['imageManifest'] = self.image_manifest response_object['repositoryName'] = self.repository response_object['registryId'] = self.registry_id - return response_object + return {k: v for k, v in response_object.items() if v is not None and v != [None]} class ECRBackend(BaseBackend): @@ -252,8 +255,14 @@ class ECRBackend(BaseBackend): else: raise Exception("{0} is not a repository".format(repository_name)) - image = Image(image_tag, image_manifest, repository_name) - repository.images.append(image) + existing_image = list(filter(lambda x: x.response_object['imageManifest'] == image_manifest, repository.images)) + if not existing_image: + image = Image(image_tag, image_manifest, repository_name) + repository.images.append(image) + else: + image = Image(image_tag, image_manifest, repository_name, existing_image[0].get_image_digest()) + repository.images.append(image) + return image def batch_get_image(self, repository_name, registry_id=None, image_ids=None, accepted_media_types=None): diff --git a/tests/test_ecr/test_ecr_boto3.py b/tests/test_ecr/test_ecr_boto3.py index 7651dc83..43a41a4d 100644 --- a/tests/test_ecr/test_ecr_boto3.py +++ b/tests/test_ecr/test_ecr_boto3.py @@ -197,6 +197,54 @@ def test_put_image(): response['image']['repositoryName'].should.equal('test_repository') response['image']['registryId'].should.equal('012345678910') +@mock_ecr +def test_put_image_with_multiple_tags(): + client = boto3.client('ecr', region_name='us-east-1') + _ = client.create_repository( + repositoryName='test_repository' + ) + manifest = _create_image_manifest() + response = client.put_image( + repositoryName='test_repository', + imageManifest=json.dumps(manifest), + imageTag='v1' + ) + + response['image']['imageId']['imageTag'].should.equal('v1') + response['image']['imageId']['imageDigest'].should.contain("sha") + response['image']['repositoryName'].should.equal('test_repository') + response['image']['registryId'].should.equal('012345678910') + + response1 = client.put_image( + repositoryName='test_repository', + imageManifest=json.dumps(manifest), + imageTag='latest' + ) + + response1['image']['imageId']['imageTag'].should.equal('latest') + response1['image']['imageId']['imageDigest'].should.contain("sha") + response1['image']['repositoryName'].should.equal('test_repository') + response1['image']['registryId'].should.equal('012345678910') + + response2 = client.describe_images(repositoryName='test_repository') + type(response2['imageDetails']).should.be(list) + len(response2['imageDetails']).should.be(1) + + response['imageDetails'][0]['imageDigest'].should.contain("sha") + + # response['imageDetails'][0]['registryId'].should.equal("012345678910") + # response['imageDetails'][1]['registryId'].should.equal("012345678910") + # response['imageDetails'][2]['registryId'].should.equal("012345678910") + # response['imageDetails'][3]['registryId'].should.equal("012345678910") + # + # response['imageDetails'][0]['repositoryName'].should.equal("test_repository") + # response['imageDetails'][1]['repositoryName'].should.equal("test_repository") + # response['imageDetails'][2]['repositoryName'].should.equal("test_repository") + # response['imageDetails'][3]['repositoryName'].should.equal("test_repository") + # + # response['imageDetails'][0].should_not.have.key('imageTags') + # len(response['imageDetails'][1]['imageTags']).should.be(1) + @mock_ecr def test_list_images(): @@ -259,6 +307,11 @@ def test_describe_images(): repositoryName='test_repository' ) + _ = client.put_image( + repositoryName='test_repository', + imageManifest=json.dumps(_create_image_manifest()) + ) + _ = client.put_image( repositoryName='test_repository', imageManifest=json.dumps(_create_image_manifest()), @@ -279,32 +332,37 @@ def test_describe_images(): response = client.describe_images(repositoryName='test_repository') type(response['imageDetails']).should.be(list) - len(response['imageDetails']).should.be(3) + len(response['imageDetails']).should.be(4) response['imageDetails'][0]['imageDigest'].should.contain("sha") response['imageDetails'][1]['imageDigest'].should.contain("sha") response['imageDetails'][2]['imageDigest'].should.contain("sha") + response['imageDetails'][3]['imageDigest'].should.contain("sha") response['imageDetails'][0]['registryId'].should.equal("012345678910") response['imageDetails'][1]['registryId'].should.equal("012345678910") response['imageDetails'][2]['registryId'].should.equal("012345678910") + response['imageDetails'][3]['registryId'].should.equal("012345678910") response['imageDetails'][0]['repositoryName'].should.equal("test_repository") response['imageDetails'][1]['repositoryName'].should.equal("test_repository") response['imageDetails'][2]['repositoryName'].should.equal("test_repository") + response['imageDetails'][3]['repositoryName'].should.equal("test_repository") - len(response['imageDetails'][0]['imageTags']).should.be(1) + response['imageDetails'][0].should_not.have.key('imageTags') len(response['imageDetails'][1]['imageTags']).should.be(1) len(response['imageDetails'][2]['imageTags']).should.be(1) + len(response['imageDetails'][3]['imageTags']).should.be(1) image_tags = ['latest', 'v1', 'v2'] - set([response['imageDetails'][0]['imageTags'][0], - response['imageDetails'][1]['imageTags'][0], - response['imageDetails'][2]['imageTags'][0]]).should.equal(set(image_tags)) + set([response['imageDetails'][1]['imageTags'][0], + response['imageDetails'][2]['imageTags'][0], + response['imageDetails'][3]['imageTags'][0]]).should.equal(set(image_tags)) response['imageDetails'][0]['imageSizeInBytes'].should.equal(52428800) response['imageDetails'][1]['imageSizeInBytes'].should.equal(52428800) response['imageDetails'][2]['imageSizeInBytes'].should.equal(52428800) + response['imageDetails'][3]['imageSizeInBytes'].should.equal(52428800) @mock_ecr