diff --git a/moto/ecr/models.py b/moto/ecr/models.py index 51056842d..097c6d627 100644 --- a/moto/ecr/models.py +++ b/moto/ecr/models.py @@ -490,6 +490,11 @@ class ECRBackend(BaseBackend): else: raise Exception("{0} is not a repository".format(repository_name)) + # Tags are unique, so delete any existing image with this tag first + self.batch_delete_image( + repository_name=repository_name, image_ids=[{"imageTag": image_tag}] + ) + existing_images = list( filter( lambda x: x.response_object["imageManifest"] == image_manifest, diff --git a/tests/test_ecr/test_ecr_boto3.py b/tests/test_ecr/test_ecr_boto3.py index e7c69b8de..7573fd713 100644 --- a/tests/test_ecr/test_ecr_boto3.py +++ b/tests/test_ecr/test_ecr_boto3.py @@ -396,7 +396,7 @@ def test_put_image_with_push_date(): _ = client.put_image( repositoryName="test_repository", imageManifest=json.dumps(_create_image_manifest()), - imageTag="latest", + imageTag="first", ) with freeze_time("2019-05-31 00:00:00"): @@ -404,7 +404,7 @@ def test_put_image_with_push_date(): _ = client.put_image( repositoryName="test_repository", imageManifest=json.dumps(_create_image_manifest()), - imageTag="latest", + imageTag="second", ) describe_response = client.describe_images(repositoryName="test_repository") @@ -461,6 +461,46 @@ def test_put_image_with_multiple_tags(): response2["imageDetails"][0]["imageTags"].should.be.equal(["v1", "latest"]) +@mock_ecr +def test_put_multiple_images_with_same_tag(): + repo_name = "testrepo" + image_tag = "my-tag" + + client = boto3.client("ecr", "us-east-1") + client.create_repository(repositoryName=repo_name) + + image_1 = client.put_image( + repositoryName=repo_name, + imageTag=image_tag, + imageManifest=json.dumps(_create_image_manifest()), + )["image"]["imageId"]["imageDigest"] + + # We should overwrite the first image + image_2 = client.put_image( + repositoryName=repo_name, + imageTag=image_tag, + imageManifest=json.dumps(_create_image_manifest()), + )["image"]["imageId"]["imageDigest"] + + assert image_1 != image_2 + + images = client.describe_images(repositoryName=repo_name)["imageDetails"] + + images.should.have.length_of(1) + images[0]["imageDigest"].should.equal(image_2) + + # Image with different tags are allowed + image_3 = client.put_image( + repositoryName=repo_name, + imageTag="different-tag", + imageManifest=json.dumps(_create_image_manifest()), + )["image"]["imageId"]["imageDigest"] + + images = client.describe_images(repositoryName=repo_name)["imageDetails"] + images.should.have.length_of(2) + set([img["imageDigest"] for img in images]).should.equal({image_2, image_3}) + + @mock_ecr def test_list_images(): client = boto3.client("ecr", region_name="us-east-1")