Merge pull request #1046 from spulec/feature/add-proper-errors-to-ecr-calls
Add proper errors to ECR calls
This commit is contained in:
commit
9ebcaf561e
22
moto/ecr/exceptions.py
Normal file
22
moto/ecr/exceptions.py
Normal file
@ -0,0 +1,22 @@
|
||||
from __future__ import unicode_literals
|
||||
from moto.core.exceptions import RESTError
|
||||
|
||||
|
||||
class RepositoryNotFoundException(RESTError):
|
||||
code = 400
|
||||
|
||||
def __init__(self, repository_name, registry_id):
|
||||
super(RepositoryNotFoundException, self).__init__(
|
||||
error_type="RepositoryNotFoundException",
|
||||
message="The repository with name '{0}' does not exist in the registry "
|
||||
"with id '{1}'".format(repository_name, registry_id))
|
||||
|
||||
|
||||
class ImageNotFoundException(RESTError):
|
||||
code = 400
|
||||
|
||||
def __init__(self, image_id, repository_name, registry_id):
|
||||
super(ImageNotFoundException, self).__init__(
|
||||
error_type="ImageNotFoundException",
|
||||
message="The image with imageId {0} does not exist within the repository with name '{1}' "
|
||||
"in the registry with id '{2}'".format(image_id, repository_name, registry_id))
|
@ -7,6 +7,11 @@ from moto.ec2 import ec2_backends
|
||||
from copy import copy
|
||||
import hashlib
|
||||
|
||||
from moto.ecr.exceptions import ImageNotFoundException, RepositoryNotFoundException
|
||||
|
||||
|
||||
DEFAULT_REGISTRY_ID = '012345678910'
|
||||
|
||||
|
||||
class BaseObject(BaseModel):
|
||||
|
||||
@ -35,14 +40,13 @@ class BaseObject(BaseModel):
|
||||
class Repository(BaseObject):
|
||||
|
||||
def __init__(self, repository_name):
|
||||
self.arn = 'arn:aws:ecr:us-east-1:012345678910:repository/{0}'.format(
|
||||
repository_name)
|
||||
self.registry_id = DEFAULT_REGISTRY_ID
|
||||
self.arn = 'arn:aws:ecr:us-east-1:{0}:repository/{1}'.format(
|
||||
self.registry_id, repository_name)
|
||||
self.name = repository_name
|
||||
# self.created = datetime.utcnow()
|
||||
self.uri = '012345678910.dkr.ecr.us-east-1.amazonaws.com/{0}'.format(
|
||||
repository_name
|
||||
)
|
||||
self.registry_id = '012345678910'
|
||||
self.uri = '{0}.dkr.ecr.us-east-1.amazonaws.com/{1}'.format(
|
||||
self.registry_id, repository_name)
|
||||
self.images = []
|
||||
|
||||
@property
|
||||
@ -93,7 +97,7 @@ class Repository(BaseObject):
|
||||
|
||||
class Image(BaseObject):
|
||||
|
||||
def __init__(self, tag, manifest, repository, registry_id="012345678910"):
|
||||
def __init__(self, tag, manifest, repository, registry_id=DEFAULT_REGISTRY_ID):
|
||||
self.image_tag = tag
|
||||
self.image_manifest = manifest
|
||||
self.image_size_in_bytes = 50 * 1024 * 1024
|
||||
@ -151,6 +155,11 @@ class ECRBackend(BaseBackend):
|
||||
"""
|
||||
maxResults and nextToken not implemented
|
||||
"""
|
||||
if repository_names:
|
||||
for repository_name in repository_names:
|
||||
if repository_name not in self.repositories:
|
||||
raise RepositoryNotFoundException(repository_name, registry_id or DEFAULT_REGISTRY_ID)
|
||||
|
||||
repositories = []
|
||||
for repository in self.repositories.values():
|
||||
# If a registry_id was supplied, ensure this repository matches
|
||||
@ -170,11 +179,11 @@ class ECRBackend(BaseBackend):
|
||||
self.repositories[repository_name] = repository
|
||||
return repository
|
||||
|
||||
def delete_repository(self, respository_name, registry_id=None):
|
||||
if respository_name in self.repositories:
|
||||
return self.repositories.pop(respository_name)
|
||||
def delete_repository(self, repository_name, registry_id=None):
|
||||
if repository_name in self.repositories:
|
||||
return self.repositories.pop(repository_name)
|
||||
else:
|
||||
raise Exception("{0} is not a repository".format(respository_name))
|
||||
raise RepositoryNotFoundException(repository_name, registry_id or DEFAULT_REGISTRY_ID)
|
||||
|
||||
def list_images(self, repository_name, registry_id=None):
|
||||
"""
|
||||
@ -198,17 +207,27 @@ class ECRBackend(BaseBackend):
|
||||
if repository_name in self.repositories:
|
||||
repository = self.repositories[repository_name]
|
||||
else:
|
||||
raise Exception("{0} is not a repository".format(repository_name))
|
||||
raise RepositoryNotFoundException(repository_name, registry_id or DEFAULT_REGISTRY_ID)
|
||||
|
||||
if image_ids:
|
||||
response = set()
|
||||
for image_id in image_ids:
|
||||
if 'imageDigest' in image_id:
|
||||
desired_digest = image_id['imageDigest']
|
||||
response.update([i for i in repository.images if i.get_image_digest() == desired_digest])
|
||||
if 'imageTag' in image_id:
|
||||
desired_tag = image_id['imageTag']
|
||||
response.update([i for i in repository.images if i.image_tag == desired_tag])
|
||||
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.add(image)
|
||||
if not found:
|
||||
image_id_representation = "{imageDigest:'%s', imageTag:'%s'}" % (
|
||||
image_id.get('imageDigest', 'null'),
|
||||
image_id.get('imageTag', 'null'),
|
||||
)
|
||||
raise ImageNotFoundException(
|
||||
image_id=image_id_representation,
|
||||
repository_name=repository_name,
|
||||
registry_id=registry_id or DEFAULT_REGISTRY_ID)
|
||||
|
||||
else:
|
||||
response = []
|
||||
for image in repository.images:
|
||||
|
@ -45,7 +45,8 @@ class ECRResponse(BaseResponse):
|
||||
|
||||
def delete_repository(self):
|
||||
repository_str = self._get_param('repositoryName')
|
||||
repository = self.ecr_backend.delete_repository(repository_str)
|
||||
registry_id = self._get_param('registryId')
|
||||
repository = self.ecr_backend.delete_repository(repository_str, registry_id)
|
||||
return json.dumps({
|
||||
'repository': repository.response_object
|
||||
})
|
||||
|
@ -59,6 +59,7 @@ class Message(BaseModel):
|
||||
return str.encode('utf-8')
|
||||
return str
|
||||
md5 = hashlib.md5()
|
||||
struct_format = "!I".encode('ascii') # ensure it's a bytestring
|
||||
for name in sorted(self.message_attributes.keys()):
|
||||
attr = self.message_attributes[name]
|
||||
data_type = attr['data_type']
|
||||
@ -67,10 +68,10 @@ class Message(BaseModel):
|
||||
# Each part of each attribute is encoded right after it's
|
||||
# own length is packed into a 4-byte integer
|
||||
# 'timestamp' -> b'\x00\x00\x00\t'
|
||||
encoded += struct.pack("!I", len(utf8(name))) + utf8(name)
|
||||
encoded += struct.pack(struct_format, len(utf8(name))) + utf8(name)
|
||||
# The datatype is additionally given a final byte
|
||||
# representing which type it is
|
||||
encoded += struct.pack("!I", len(data_type)) + utf8(data_type)
|
||||
encoded += struct.pack(struct_format, len(data_type)) + utf8(data_type)
|
||||
encoded += TRANSPORT_TYPE_ENCODINGS[data_type]
|
||||
|
||||
if data_type == 'String' or data_type == 'Number':
|
||||
@ -86,7 +87,7 @@ class Message(BaseModel):
|
||||
# MD5 so as not to break client softwre
|
||||
return('deadbeefdeadbeefdeadbeefdeadbeef')
|
||||
|
||||
encoded += struct.pack("!I", len(utf8(value))) + utf8(value)
|
||||
encoded += struct.pack(struct_format, len(utf8(value))) + utf8(value)
|
||||
|
||||
md5.update(encoded)
|
||||
return md5.hexdigest()
|
||||
|
@ -7,5 +7,5 @@ flake8
|
||||
freezegun
|
||||
flask
|
||||
boto3>=1.4.4
|
||||
botocore>=1.4.28
|
||||
botocore>=1.5.77
|
||||
six
|
||||
|
@ -5,9 +5,11 @@ import json
|
||||
from datetime import datetime
|
||||
from random import random
|
||||
|
||||
import re
|
||||
import sure # noqa
|
||||
|
||||
import boto3
|
||||
from botocore.exceptions import ClientError
|
||||
from dateutil.tz import tzlocal
|
||||
|
||||
from moto import mock_ecr
|
||||
@ -141,19 +143,6 @@ def test_describe_repositories_3():
|
||||
response['repositories'][0]['repositoryUri'].should.equal(respository_uri)
|
||||
|
||||
|
||||
@mock_ecr
|
||||
def test_describe_repositories_4():
|
||||
client = boto3.client('ecr', region_name='us-east-1')
|
||||
_ = client.create_repository(
|
||||
repositoryName='test_repository1'
|
||||
)
|
||||
_ = client.create_repository(
|
||||
repositoryName='test_repository0'
|
||||
)
|
||||
response = client.describe_repositories(repositoryNames=['not_a_valid_name'])
|
||||
len(response['repositories']).should.equal(0)
|
||||
|
||||
|
||||
@mock_ecr
|
||||
def test_describe_repositories_with_image():
|
||||
client = boto3.client('ecr', region_name='us-east-1')
|
||||
@ -344,6 +333,54 @@ def test_describe_images_by_tag():
|
||||
image_detail['imageDigest'].should.equal(put_response['imageId']['imageDigest'])
|
||||
|
||||
|
||||
@mock_ecr
|
||||
def test_describe_repository_that_doesnt_exist():
|
||||
client = boto3.client('ecr', region_name='us-east-1')
|
||||
|
||||
error_msg = re.compile(
|
||||
r".*The repository with name 'repo-that-doesnt-exist' does not exist in the registry with id '123'.*",
|
||||
re.MULTILINE)
|
||||
client.describe_repositories.when.called_with(
|
||||
repositoryNames=['repo-that-doesnt-exist'],
|
||||
registryId='123',
|
||||
).should.throw(ClientError, error_msg)
|
||||
|
||||
@mock_ecr
|
||||
def test_describe_image_that_doesnt_exist():
|
||||
client = boto3.client('ecr', region_name='us-east-1')
|
||||
client.create_repository(repositoryName='test_repository')
|
||||
|
||||
error_msg1 = re.compile(
|
||||
r".*The image with imageId {imageDigest:'null', imageTag:'testtag'} does not exist within "
|
||||
r"the repository with name 'test_repository' in the registry with id '123'.*",
|
||||
re.MULTILINE)
|
||||
|
||||
client.describe_images.when.called_with(
|
||||
repositoryName='test_repository', imageIds=[{'imageTag': 'testtag'}], registryId='123',
|
||||
).should.throw(ClientError, error_msg1)
|
||||
|
||||
error_msg2 = re.compile(
|
||||
r".*The repository with name 'repo-that-doesnt-exist' does not exist in the registry with id '123'.*",
|
||||
re.MULTILINE)
|
||||
client.describe_images.when.called_with(
|
||||
repositoryName='repo-that-doesnt-exist', imageIds=[{'imageTag': 'testtag'}], registryId='123',
|
||||
).should.throw(ClientError, error_msg2)
|
||||
|
||||
|
||||
@mock_ecr
|
||||
def test_delete_repository_that_doesnt_exist():
|
||||
client = boto3.client('ecr', region_name='us-east-1')
|
||||
|
||||
error_msg = re.compile(
|
||||
r".*The repository with name 'repo-that-doesnt-exist' does not exist in the registry with id '123'.*",
|
||||
re.MULTILINE)
|
||||
|
||||
client.delete_repository.when.called_with(
|
||||
repositoryName='repo-that-doesnt-exist',
|
||||
registryId='123').should.throw(
|
||||
ClientError, error_msg)
|
||||
|
||||
|
||||
@mock_ecr
|
||||
def test_describe_images_by_digest():
|
||||
client = boto3.client('ecr', region_name='us-east-1')
|
||||
|
Loading…
Reference in New Issue
Block a user