tweak Fn::GetAtt to return resource_json if resource is not implemented. DRY
This is better than failing out with a misleading Boto 400 error which should only happen when get_cfn_attribute is called but fails.
This commit is contained in:
parent
83f187fa7e
commit
20a69255c3
6
moto/cloudformation/exceptions.py
Normal file
6
moto/cloudformation/exceptions.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|
||||||
|
class UnformattedGetAttTemplateException(Exception):
|
||||||
|
description = 'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt'
|
||||||
|
status_code = 400
|
@ -8,6 +8,7 @@ from moto.elb import models as elb_models
|
|||||||
from moto.iam import models as iam_models
|
from moto.iam import models as iam_models
|
||||||
from moto.sqs import models as sqs_models
|
from moto.sqs import models as sqs_models
|
||||||
from .utils import random_suffix
|
from .utils import random_suffix
|
||||||
|
from .exceptions import UnformattedGetAttTemplateException
|
||||||
from boto.cloudformation.stack import Output
|
from boto.cloudformation.stack import Output
|
||||||
from boto.exception import BotoServerError
|
from boto.exception import BotoServerError
|
||||||
|
|
||||||
@ -72,17 +73,22 @@ def clean_json(resource_json, resources_map):
|
|||||||
return resource
|
return resource
|
||||||
|
|
||||||
if 'Fn::GetAtt' in resource_json:
|
if 'Fn::GetAtt' in resource_json:
|
||||||
|
|
||||||
resource = resources_map[resource_json['Fn::GetAtt'][0]]
|
resource = resources_map[resource_json['Fn::GetAtt'][0]]
|
||||||
|
if resource is None:
|
||||||
|
return resource_json
|
||||||
try:
|
try:
|
||||||
return resource.get_cfn_attribute(resource_json['Fn::GetAtt'][1])
|
return resource.get_cfn_attribute(resource_json['Fn::GetAtt'][1])
|
||||||
except NotImplementedError as n:
|
except NotImplementedError as n:
|
||||||
raise NotImplementedError(n.message.format(resource_json['Fn::GetAtt'][0]))
|
logger.warning(n.message.format(resource_json['Fn::GetAtt'][0]))
|
||||||
except AttributeError:
|
except UnformattedGetAttTemplateException:
|
||||||
raise BotoServerError(
|
raise BotoServerError(
|
||||||
400,
|
UnformattedGetAttTemplateException.status_code,
|
||||||
'Bad Request',
|
'Bad Request',
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt'.format(
|
UnformattedGetAttTemplateException.description.format(
|
||||||
resource_json['Fn::GetAtt'][0], resource_json['Fn::GetAtt'][1]))
|
resource_json['Fn::GetAtt'][0], resource_json['Fn::GetAtt'][1]))
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
cleaned_json = {}
|
cleaned_json = {}
|
||||||
for key, value in resource_json.items():
|
for key, value in resource_json.items():
|
||||||
|
@ -9,7 +9,6 @@ from boto.ec2.instance import Instance as BotoInstance, Reservation
|
|||||||
from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType
|
from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType
|
||||||
from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest
|
from boto.ec2.spotinstancerequest import SpotInstanceRequest as BotoSpotRequest
|
||||||
from boto.ec2.launchspecification import LaunchSpecification
|
from boto.ec2.launchspecification import LaunchSpecification
|
||||||
from boto.exception import BotoServerError
|
|
||||||
|
|
||||||
from moto.core import BaseBackend
|
from moto.core import BaseBackend
|
||||||
from moto.core.models import Model
|
from moto.core.models import Model
|
||||||
@ -187,13 +186,12 @@ class NetworkInterface(object):
|
|||||||
return self._group_set
|
return self._group_set
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'PrimaryPrivateIpAddress':
|
if attribute_name == 'PrimaryPrivateIpAddress':
|
||||||
return self.private_ip_address
|
return self.private_ip_address
|
||||||
elif attribute_name == 'SecondaryPrivateIpAddresses':
|
elif attribute_name == 'SecondaryPrivateIpAddresses':
|
||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "SecondaryPrivateIpAddresses" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "SecondaryPrivateIpAddresses" ]"')
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkInterfaceBackend(object):
|
class NetworkInterfaceBackend(object):
|
||||||
@ -423,6 +421,7 @@ class Instance(BotoInstance, TaggedEC2Resource):
|
|||||||
eni.device_index = None
|
eni.device_index = None
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'AvailabilityZone':
|
if attribute_name == 'AvailabilityZone':
|
||||||
return self.placement
|
return self.placement
|
||||||
elif attribute_name == 'PrivateDnsName':
|
elif attribute_name == 'PrivateDnsName':
|
||||||
@ -433,9 +432,7 @@ class Instance(BotoInstance, TaggedEC2Resource):
|
|||||||
return self.private_ip_address
|
return self.private_ip_address
|
||||||
elif attribute_name == 'PublicIp':
|
elif attribute_name == 'PublicIp':
|
||||||
return self.ip_address
|
return self.ip_address
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class InstanceBackend(object):
|
class InstanceBackend(object):
|
||||||
@ -997,11 +994,10 @@ class SecurityGroup(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'GroupId':
|
if attribute_name == 'GroupId':
|
||||||
return self.id
|
return self.id
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupBackend(object):
|
class SecurityGroupBackend(object):
|
||||||
@ -1527,11 +1523,10 @@ class Subnet(TaggedEC2Resource):
|
|||||||
return filter_value
|
return filter_value
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'AvailabilityZone':
|
if attribute_name == 'AvailabilityZone':
|
||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "AvailabilityZone" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "AvailabilityZone" ]"')
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class SubnetBackend(object):
|
class SubnetBackend(object):
|
||||||
@ -1969,11 +1964,10 @@ class ElasticAddress(object):
|
|||||||
return self.allocation_id
|
return self.allocation_id
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'AllocationId':
|
if attribute_name == 'AllocationId':
|
||||||
return self.allocation_id
|
return self.allocation_id
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class ElasticAddressBackend(object):
|
class ElasticAddressBackend(object):
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from moto.core import BaseBackend
|
from moto.core import BaseBackend
|
||||||
from boto.exception import BotoServerError
|
|
||||||
|
|
||||||
|
|
||||||
class FakeHealthCheck(object):
|
class FakeHealthCheck(object):
|
||||||
@ -58,6 +57,7 @@ class FakeLoadBalancer(object):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'CanonicalHostedZoneName':
|
if attribute_name == 'CanonicalHostedZoneName':
|
||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "CanonicalHostedZoneName" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "CanonicalHostedZoneName" ]"')
|
||||||
elif attribute_name == 'CanonicalHostedZoneNameID':
|
elif attribute_name == 'CanonicalHostedZoneNameID':
|
||||||
@ -68,9 +68,7 @@ class FakeLoadBalancer(object):
|
|||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "SourceSecurityGroup.GroupName" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "SourceSecurityGroup.GroupName" ]"')
|
||||||
elif attribute_name == 'SourceSecurityGroup.OwnerAlias':
|
elif attribute_name == 'SourceSecurityGroup.OwnerAlias':
|
||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "SourceSecurityGroup.OwnerAlias" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "SourceSecurityGroup.OwnerAlias" ]"')
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class ELBBackend(BaseBackend):
|
class ELBBackend(BaseBackend):
|
||||||
|
@ -31,11 +31,10 @@ class Role(object):
|
|||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'Arn':
|
if attribute_name == 'Arn':
|
||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "Arn" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "Arn" ]"')
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class InstanceProfile(object):
|
class InstanceProfile(object):
|
||||||
@ -61,11 +60,10 @@ class InstanceProfile(object):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'Arn':
|
if attribute_name == 'Arn':
|
||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "Arn" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "Arn" ]"')
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class Certificate(object):
|
class Certificate(object):
|
||||||
@ -93,11 +91,10 @@ class AccessKey(object):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'SecretAccessKey':
|
if attribute_name == 'SecretAccessKey':
|
||||||
return self.secret_access_key
|
return self.secret_access_key
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class Group(object):
|
class Group(object):
|
||||||
@ -113,11 +110,10 @@ class Group(object):
|
|||||||
self.users = []
|
self.users = []
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'Arn':
|
if attribute_name == 'Arn':
|
||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "Arn" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "Arn" ]"')
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
@ -172,11 +168,10 @@ class User(object):
|
|||||||
raise BotoServerError(404, 'Not Found')
|
raise BotoServerError(404, 'Not Found')
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'Arn':
|
if attribute_name == 'Arn':
|
||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "Arn" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "Arn" ]"')
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class IAMBackend(BaseBackend):
|
class IAMBackend(BaseBackend):
|
||||||
|
@ -12,7 +12,6 @@ from moto.core import BaseBackend
|
|||||||
from moto.core.utils import iso_8601_datetime, rfc_1123_datetime
|
from moto.core.utils import iso_8601_datetime, rfc_1123_datetime
|
||||||
from .exceptions import BucketAlreadyExists, MissingBucket
|
from .exceptions import BucketAlreadyExists, MissingBucket
|
||||||
from .utils import clean_key_name, _VersionedKeyStore
|
from .utils import clean_key_name, _VersionedKeyStore
|
||||||
from boto.exception import BotoServerError
|
|
||||||
|
|
||||||
UPLOAD_ID_BYTES = 43
|
UPLOAD_ID_BYTES = 43
|
||||||
UPLOAD_PART_MIN_SIZE = 5242880
|
UPLOAD_PART_MIN_SIZE = 5242880
|
||||||
@ -172,13 +171,12 @@ class FakeBucket(object):
|
|||||||
return self.versioning_status == 'Enabled'
|
return self.versioning_status == 'Enabled'
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'DomainName':
|
if attribute_name == 'DomainName':
|
||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "DomainName" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "DomainName" ]"')
|
||||||
elif attribute_name == 'WebsiteURL':
|
elif attribute_name == 'WebsiteURL':
|
||||||
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "WebsiteURL" ]"')
|
raise NotImplementedError('"Fn::GetAtt" : [ "{0}" , "WebsiteURL" ]"')
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class S3Backend(BaseBackend):
|
class S3Backend(BaseBackend):
|
||||||
|
@ -8,7 +8,6 @@ from moto.core import BaseBackend
|
|||||||
from moto.core.utils import iso_8601_datetime
|
from moto.core.utils import iso_8601_datetime
|
||||||
from moto.sqs.models import sqs_backend
|
from moto.sqs.models import sqs_backend
|
||||||
from .utils import make_arn_for_topic, make_arn_for_subscription
|
from .utils import make_arn_for_topic, make_arn_for_subscription
|
||||||
from boto.exception import BotoServerError
|
|
||||||
|
|
||||||
DEFAULT_ACCOUNT_ID = 123456789012
|
DEFAULT_ACCOUNT_ID = 123456789012
|
||||||
|
|
||||||
@ -35,11 +34,10 @@ class Topic(object):
|
|||||||
return message_id
|
return message_id
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'TopicName':
|
if attribute_name == 'TopicName':
|
||||||
return self.name
|
return self.name
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class Subscription(object):
|
class Subscription(object):
|
||||||
|
@ -4,7 +4,7 @@ import hashlib
|
|||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from boto.exception import BotoServerError
|
|
||||||
from moto.core import BaseBackend
|
from moto.core import BaseBackend
|
||||||
from moto.core.utils import camelcase_to_underscores, get_random_message_id
|
from moto.core.utils import camelcase_to_underscores, get_random_message_id
|
||||||
from .utils import generate_receipt_handle, unix_time_millis
|
from .utils import generate_receipt_handle, unix_time_millis
|
||||||
@ -154,13 +154,12 @@ class Queue(object):
|
|||||||
self._messages.append(message)
|
self._messages.append(message)
|
||||||
|
|
||||||
def get_cfn_attribute(self, attribute_name):
|
def get_cfn_attribute(self, attribute_name):
|
||||||
|
from moto.cloudformation.exceptions import UnformattedGetAttTemplateException
|
||||||
if attribute_name == 'Arn':
|
if attribute_name == 'Arn':
|
||||||
return self.queue_arn
|
return self.queue_arn
|
||||||
elif attribute_name == 'QueueName':
|
elif attribute_name == 'QueueName':
|
||||||
return self.name
|
return self.name
|
||||||
raise BotoServerError(400,
|
raise UnformattedGetAttTemplateException()
|
||||||
'Bad Request',
|
|
||||||
'Template error: resource {0} does not support attribute type {1} in Fn::GetAtt')
|
|
||||||
|
|
||||||
|
|
||||||
class SQSBackend(BaseBackend):
|
class SQSBackend(BaseBackend):
|
||||||
|
Loading…
Reference in New Issue
Block a user