Decentralize cloudformation naming responsibilities (#3201)
* #3127 - Decentralize CF naming responsibilities * Decentralize CloudFormation naming responsibilities * Update URLs in cloudformation_resource_type functions * Fix flake8 errors * Black formatting * Add a bunch of imports to populate CloudFormationModel.__subclasses__ * Add noqa to s3 models import statement in cloudformation/parsing.py * Black formatting * Remove debugging print statement Co-authored-by: Bert Blommers <info@bertblommers.nl>
This commit is contained in:
parent
88a1134657
commit
9a9a1d8413
@ -6,7 +6,7 @@ from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping
|
||||
from moto.ec2.exceptions import InvalidInstanceIdError
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.ec2 import ec2_backends
|
||||
from moto.elb import elb_backends
|
||||
from moto.elbv2 import elbv2_backends
|
||||
@ -74,7 +74,7 @@ class FakeScalingPolicy(BaseModel):
|
||||
)
|
||||
|
||||
|
||||
class FakeLaunchConfiguration(BaseModel):
|
||||
class FakeLaunchConfiguration(CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
@ -127,6 +127,15 @@ class FakeLaunchConfiguration(BaseModel):
|
||||
)
|
||||
return config
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "LaunchConfigurationName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-autoscaling-launchconfiguration.html
|
||||
return "AWS::AutoScaling::LaunchConfiguration"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -215,7 +224,7 @@ class FakeLaunchConfiguration(BaseModel):
|
||||
return block_device_map
|
||||
|
||||
|
||||
class FakeAutoScalingGroup(BaseModel):
|
||||
class FakeAutoScalingGroup(CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
@ -309,6 +318,15 @@ class FakeAutoScalingGroup(BaseModel):
|
||||
tag["PropagateAtLaunch"] = bool_to_string[tag["PropagateAtLaunch"]]
|
||||
return tags
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "AutoScalingGroupName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-autoscaling-autoscalinggroup.html
|
||||
return "AWS::AutoScaling::AutoScalingGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -28,7 +28,7 @@ import requests.adapters
|
||||
from boto3 import Session
|
||||
|
||||
from moto.awslambda.policy import Policy
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, CloudFormationModel
|
||||
from moto.core.exceptions import RESTError
|
||||
from moto.iam.models import iam_backend
|
||||
from moto.iam.exceptions import IAMNotFoundException
|
||||
@ -151,7 +151,7 @@ class _DockerDataVolumeContext:
|
||||
raise # multiple processes trying to use same volume?
|
||||
|
||||
|
||||
class LambdaFunction(BaseModel):
|
||||
class LambdaFunction(CloudFormationModel):
|
||||
def __init__(self, spec, region, validate_s3=True, version=1):
|
||||
# required
|
||||
self.region = region
|
||||
@ -492,6 +492,15 @@ class LambdaFunction(BaseModel):
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "FunctionName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-function.html
|
||||
return "AWS::Lambda::Function"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -556,7 +565,7 @@ class LambdaFunction(BaseModel):
|
||||
lambda_backends[region].delete_function(self.function_name)
|
||||
|
||||
|
||||
class EventSourceMapping(BaseModel):
|
||||
class EventSourceMapping(CloudFormationModel):
|
||||
def __init__(self, spec):
|
||||
# required
|
||||
self.function_name = spec["FunctionName"]
|
||||
@ -633,6 +642,15 @@ class EventSourceMapping(BaseModel):
|
||||
lambda_backend = lambda_backends[region_name]
|
||||
lambda_backend.delete_event_source_mapping(self.uuid)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-eventsourcemapping.html
|
||||
return "AWS::Lambda::EventSourceMapping"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -667,13 +685,22 @@ class EventSourceMapping(BaseModel):
|
||||
esm.delete(region_name)
|
||||
|
||||
|
||||
class LambdaVersion(BaseModel):
|
||||
class LambdaVersion(CloudFormationModel):
|
||||
def __init__(self, spec):
|
||||
self.version = spec["Version"]
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.logical_resource_id)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-lambda-version.html
|
||||
return "AWS::Lambda::Version"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -13,7 +13,7 @@ import threading
|
||||
import dateutil.parser
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.iam import iam_backends
|
||||
from moto.ec2 import ec2_backends
|
||||
from moto.ecs import ecs_backends
|
||||
@ -42,7 +42,7 @@ def datetime2int(date):
|
||||
return int(time.mktime(date.timetuple()))
|
||||
|
||||
|
||||
class ComputeEnvironment(BaseModel):
|
||||
class ComputeEnvironment(CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
compute_environment_name,
|
||||
@ -76,6 +76,15 @@ class ComputeEnvironment(BaseModel):
|
||||
def physical_resource_id(self):
|
||||
return self.arn
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "ComputeEnvironmentName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-batch-computeenvironment.html
|
||||
return "AWS::Batch::ComputeEnvironment"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -95,7 +104,7 @@ class ComputeEnvironment(BaseModel):
|
||||
return backend.get_compute_environment_by_arn(arn)
|
||||
|
||||
|
||||
class JobQueue(BaseModel):
|
||||
class JobQueue(CloudFormationModel):
|
||||
def __init__(
|
||||
self, name, priority, state, environments, env_order_json, region_name
|
||||
):
|
||||
@ -139,6 +148,15 @@ class JobQueue(BaseModel):
|
||||
def physical_resource_id(self):
|
||||
return self.arn
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "JobQueueName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-batch-jobqueue.html
|
||||
return "AWS::Batch::JobQueue"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -164,7 +182,7 @@ class JobQueue(BaseModel):
|
||||
return backend.get_job_queue_by_arn(arn)
|
||||
|
||||
|
||||
class JobDefinition(BaseModel):
|
||||
class JobDefinition(CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
@ -264,6 +282,15 @@ class JobDefinition(BaseModel):
|
||||
def physical_resource_id(self):
|
||||
return self.arn
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "JobDefinitionName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-batch-jobdefinition.html
|
||||
return "AWS::Batch::JobDefinition"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -6,31 +6,43 @@ import copy
|
||||
import warnings
|
||||
import re
|
||||
|
||||
from moto.autoscaling import models as autoscaling_models
|
||||
from moto.awslambda import models as lambda_models
|
||||
from moto.batch import models as batch_models
|
||||
from moto.cloudwatch import models as cloudwatch_models
|
||||
from moto.cognitoidentity import models as cognitoidentity_models
|
||||
from moto.compat import collections_abc
|
||||
from moto.datapipeline import models as datapipeline_models
|
||||
from moto.dynamodb2 import models as dynamodb2_models
|
||||
|
||||
# This ugly section of imports is necessary because we
|
||||
# build the list of CloudFormationModel subclasses using
|
||||
# CloudFormationModel.__subclasses__(). However, if the class
|
||||
# definition of a subclass hasn't been executed yet - for example, if
|
||||
# the subclass's module hasn't been imported yet - then that subclass
|
||||
# doesn't exist yet, and __subclasses__ won't find it.
|
||||
# So we import here to populate the list of subclasses.
|
||||
from moto.autoscaling import models as autoscaling_models # noqa
|
||||
from moto.awslambda import models as awslambda_models # noqa
|
||||
from moto.batch import models as batch_models # noqa
|
||||
from moto.cloudwatch import models as cloudwatch_models # noqa
|
||||
from moto.datapipeline import models as datapipeline_models # noqa
|
||||
from moto.dynamodb2 import models as dynamodb2_models # noqa
|
||||
from moto.ecr import models as ecr_models # noqa
|
||||
from moto.ecs import models as ecs_models # noqa
|
||||
from moto.elb import models as elb_models # noqa
|
||||
from moto.elbv2 import models as elbv2_models # noqa
|
||||
from moto.events import models as events_models # noqa
|
||||
from moto.iam import models as iam_models # noqa
|
||||
from moto.kinesis import models as kinesis_models # noqa
|
||||
from moto.kms import models as kms_models # noqa
|
||||
from moto.rds import models as rds_models # noqa
|
||||
from moto.rds2 import models as rds2_models # noqa
|
||||
from moto.redshift import models as redshift_models # noqa
|
||||
from moto.route53 import models as route53_models # noqa
|
||||
from moto.s3 import models as s3_models # noqa
|
||||
from moto.sns import models as sns_models # noqa
|
||||
from moto.sqs import models as sqs_models # noqa
|
||||
|
||||
# End ugly list of imports
|
||||
|
||||
from moto.ec2 import models as ec2_models
|
||||
from moto.ecs import models as ecs_models
|
||||
from moto.elb import models as elb_models
|
||||
from moto.elbv2 import models as elbv2_models
|
||||
from moto.events import models as events_models
|
||||
from moto.iam import models as iam_models
|
||||
from moto.kinesis import models as kinesis_models
|
||||
from moto.kms import models as kms_models
|
||||
from moto.rds import models as rds_models
|
||||
from moto.rds2 import models as rds2_models
|
||||
from moto.redshift import models as redshift_models
|
||||
from moto.route53 import models as route53_models
|
||||
from moto.s3 import models as s3_models, s3_backend
|
||||
from moto.s3 import models as _, s3_backend # noqa
|
||||
from moto.s3.utils import bucket_and_name_from_url
|
||||
from moto.sns import models as sns_models
|
||||
from moto.sqs import models as sqs_models
|
||||
from moto.core import ACCOUNT_ID
|
||||
from moto.core import ACCOUNT_ID, CloudFormationModel
|
||||
from .utils import random_suffix
|
||||
from .exceptions import (
|
||||
ExportNotFound,
|
||||
@ -40,105 +52,13 @@ from .exceptions import (
|
||||
)
|
||||
from boto.cloudformation.stack import Output
|
||||
|
||||
MODEL_MAP = {
|
||||
"AWS::AutoScaling::AutoScalingGroup": autoscaling_models.FakeAutoScalingGroup,
|
||||
"AWS::AutoScaling::LaunchConfiguration": autoscaling_models.FakeLaunchConfiguration,
|
||||
"AWS::Batch::JobDefinition": batch_models.JobDefinition,
|
||||
"AWS::Batch::JobQueue": batch_models.JobQueue,
|
||||
"AWS::Batch::ComputeEnvironment": batch_models.ComputeEnvironment,
|
||||
"AWS::DynamoDB::Table": dynamodb2_models.Table,
|
||||
"AWS::Kinesis::Stream": kinesis_models.Stream,
|
||||
"AWS::Lambda::EventSourceMapping": lambda_models.EventSourceMapping,
|
||||
"AWS::Lambda::Function": lambda_models.LambdaFunction,
|
||||
"AWS::Lambda::Version": lambda_models.LambdaVersion,
|
||||
"AWS::EC2::EIP": ec2_models.ElasticAddress,
|
||||
"AWS::EC2::Instance": ec2_models.Instance,
|
||||
"AWS::EC2::InternetGateway": ec2_models.InternetGateway,
|
||||
"AWS::EC2::NatGateway": ec2_models.NatGateway,
|
||||
"AWS::EC2::NetworkInterface": ec2_models.NetworkInterface,
|
||||
"AWS::EC2::Route": ec2_models.Route,
|
||||
"AWS::EC2::RouteTable": ec2_models.RouteTable,
|
||||
"AWS::EC2::SecurityGroup": ec2_models.SecurityGroup,
|
||||
"AWS::EC2::SecurityGroupIngress": ec2_models.SecurityGroupIngress,
|
||||
"AWS::EC2::SpotFleet": ec2_models.SpotFleetRequest,
|
||||
"AWS::EC2::Subnet": ec2_models.Subnet,
|
||||
"AWS::EC2::SubnetRouteTableAssociation": ec2_models.SubnetRouteTableAssociation,
|
||||
"AWS::EC2::Volume": ec2_models.Volume,
|
||||
"AWS::EC2::VolumeAttachment": ec2_models.VolumeAttachment,
|
||||
"AWS::EC2::VPC": ec2_models.VPC,
|
||||
"AWS::EC2::VPCGatewayAttachment": ec2_models.VPCGatewayAttachment,
|
||||
"AWS::EC2::VPCPeeringConnection": ec2_models.VPCPeeringConnection,
|
||||
"AWS::ECS::Cluster": ecs_models.Cluster,
|
||||
"AWS::ECS::TaskDefinition": ecs_models.TaskDefinition,
|
||||
"AWS::ECS::Service": ecs_models.Service,
|
||||
"AWS::ElasticLoadBalancing::LoadBalancer": elb_models.FakeLoadBalancer,
|
||||
"AWS::ElasticLoadBalancingV2::LoadBalancer": elbv2_models.FakeLoadBalancer,
|
||||
"AWS::ElasticLoadBalancingV2::TargetGroup": elbv2_models.FakeTargetGroup,
|
||||
"AWS::ElasticLoadBalancingV2::Listener": elbv2_models.FakeListener,
|
||||
"AWS::Cognito::IdentityPool": cognitoidentity_models.CognitoIdentity,
|
||||
"AWS::DataPipeline::Pipeline": datapipeline_models.Pipeline,
|
||||
"AWS::IAM::InstanceProfile": iam_models.InstanceProfile,
|
||||
"AWS::IAM::Role": iam_models.Role,
|
||||
"AWS::KMS::Key": kms_models.Key,
|
||||
"AWS::Logs::LogGroup": cloudwatch_models.LogGroup,
|
||||
"AWS::RDS::DBInstance": rds_models.Database,
|
||||
"AWS::RDS::DBSecurityGroup": rds_models.SecurityGroup,
|
||||
"AWS::RDS::DBSubnetGroup": rds_models.SubnetGroup,
|
||||
"AWS::RDS::DBParameterGroup": rds2_models.DBParameterGroup,
|
||||
"AWS::Redshift::Cluster": redshift_models.Cluster,
|
||||
"AWS::Redshift::ClusterParameterGroup": redshift_models.ParameterGroup,
|
||||
"AWS::Redshift::ClusterSubnetGroup": redshift_models.SubnetGroup,
|
||||
"AWS::Route53::HealthCheck": route53_models.HealthCheck,
|
||||
"AWS::Route53::HostedZone": route53_models.FakeZone,
|
||||
"AWS::Route53::RecordSet": route53_models.RecordSet,
|
||||
"AWS::Route53::RecordSetGroup": route53_models.RecordSetGroup,
|
||||
"AWS::SNS::Topic": sns_models.Topic,
|
||||
"AWS::S3::Bucket": s3_models.FakeBucket,
|
||||
"AWS::SQS::Queue": sqs_models.Queue,
|
||||
"AWS::Events::Rule": events_models.Rule,
|
||||
"AWS::Events::EventBus": events_models.EventBus,
|
||||
}
|
||||
|
||||
UNDOCUMENTED_NAME_TYPE_MAP = {
|
||||
"AWS::AutoScaling::AutoScalingGroup": "AutoScalingGroupName",
|
||||
"AWS::AutoScaling::LaunchConfiguration": "LaunchConfigurationName",
|
||||
"AWS::IAM::InstanceProfile": "InstanceProfileName",
|
||||
}
|
||||
|
||||
# http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-name.html
|
||||
# List of supported CloudFormation models
|
||||
MODEL_LIST = CloudFormationModel.__subclasses__()
|
||||
MODEL_MAP = {model.cloudformation_type(): model for model in MODEL_LIST}
|
||||
NAME_TYPE_MAP = {
|
||||
"AWS::ApiGateway::ApiKey": "Name",
|
||||
"AWS::ApiGateway::Model": "Name",
|
||||
"AWS::CloudWatch::Alarm": "AlarmName",
|
||||
"AWS::DynamoDB::Table": "TableName",
|
||||
"AWS::ElasticBeanstalk::Application": "ApplicationName",
|
||||
"AWS::ElasticBeanstalk::Environment": "EnvironmentName",
|
||||
"AWS::CodeDeploy::Application": "ApplicationName",
|
||||
"AWS::CodeDeploy::DeploymentConfig": "DeploymentConfigName",
|
||||
"AWS::CodeDeploy::DeploymentGroup": "DeploymentGroupName",
|
||||
"AWS::Config::ConfigRule": "ConfigRuleName",
|
||||
"AWS::Config::DeliveryChannel": "Name",
|
||||
"AWS::Config::ConfigurationRecorder": "Name",
|
||||
"AWS::ElasticLoadBalancing::LoadBalancer": "LoadBalancerName",
|
||||
"AWS::ElasticLoadBalancingV2::LoadBalancer": "Name",
|
||||
"AWS::ElasticLoadBalancingV2::TargetGroup": "Name",
|
||||
"AWS::EC2::SecurityGroup": "GroupName",
|
||||
"AWS::ElastiCache::CacheCluster": "ClusterName",
|
||||
"AWS::ECR::Repository": "RepositoryName",
|
||||
"AWS::ECS::Cluster": "ClusterName",
|
||||
"AWS::Elasticsearch::Domain": "DomainName",
|
||||
"AWS::Events::Rule": "Name",
|
||||
"AWS::IAM::Group": "GroupName",
|
||||
"AWS::IAM::ManagedPolicy": "ManagedPolicyName",
|
||||
"AWS::IAM::Role": "RoleName",
|
||||
"AWS::IAM::User": "UserName",
|
||||
"AWS::Lambda::Function": "FunctionName",
|
||||
"AWS::RDS::DBInstance": "DBInstanceIdentifier",
|
||||
"AWS::S3::Bucket": "BucketName",
|
||||
"AWS::SNS::Topic": "TopicName",
|
||||
"AWS::SQS::Queue": "QueueName",
|
||||
model.cloudformation_type(): model.cloudformation_name_type()
|
||||
for model in MODEL_LIST
|
||||
}
|
||||
NAME_TYPE_MAP.update(UNDOCUMENTED_NAME_TYPE_MAP)
|
||||
|
||||
# Just ignore these models types for now
|
||||
NULL_MODELS = [
|
||||
@ -292,9 +212,11 @@ def clean_json(resource_json, resources_map):
|
||||
def resource_class_from_type(resource_type):
|
||||
if resource_type in NULL_MODELS:
|
||||
return None
|
||||
|
||||
if resource_type not in MODEL_MAP:
|
||||
logger.warning("No Moto CloudFormation support for %s", resource_type)
|
||||
return None
|
||||
|
||||
return MODEL_MAP.get(resource_type)
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@ import json
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core.utils import iso_8601_datetime_without_milliseconds
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.exceptions import RESTError
|
||||
from moto.logs import logs_backends
|
||||
from datetime import datetime, timedelta
|
||||
@ -490,13 +490,22 @@ class CloudWatchBackend(BaseBackend):
|
||||
return None, metrics
|
||||
|
||||
|
||||
class LogGroup(BaseModel):
|
||||
class LogGroup(CloudFormationModel):
|
||||
def __init__(self, spec):
|
||||
# required
|
||||
self.name = spec["LogGroupName"]
|
||||
# optional
|
||||
self.tags = spec.get("Tags", [])
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "LogGroupName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-logs-loggroup.html
|
||||
return "AWS::Logs::LogGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -1,6 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .models import BaseModel, BaseBackend, moto_api_backend, ACCOUNT_ID # noqa
|
||||
from .models import CloudFormationModel # noqa
|
||||
from .responses import ActionAuthenticatorMixin
|
||||
|
||||
moto_api_backends = {"global": moto_api_backend}
|
||||
|
@ -8,6 +8,7 @@ import os
|
||||
import re
|
||||
import six
|
||||
import types
|
||||
from abc import abstractmethod
|
||||
from io import BytesIO
|
||||
from collections import defaultdict
|
||||
from botocore.config import Config
|
||||
@ -534,6 +535,47 @@ class BaseModel(object):
|
||||
return instance
|
||||
|
||||
|
||||
# Parent class for every Model that can be instantiated by CloudFormation
|
||||
# On subclasses, implement the two methods as @staticmethod to ensure correct behaviour of the CF parser
|
||||
class CloudFormationModel(BaseModel):
|
||||
@abstractmethod
|
||||
def cloudformation_name_type(self):
|
||||
# This must be implemented as a staticmethod with no parameters
|
||||
# Return None for resources that do not have a name property
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def cloudformation_type(self):
|
||||
# This must be implemented as a staticmethod with no parameters
|
||||
# See for example https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html
|
||||
return "AWS::SERVICE::RESOURCE"
|
||||
|
||||
@abstractmethod
|
||||
def create_from_cloudformation_json(self):
|
||||
# This must be implemented as a classmethod with parameters:
|
||||
# cls, resource_name, cloudformation_json, region_name
|
||||
# Extract the resource parameters from the cloudformation json
|
||||
# and return an instance of the resource class
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def update_from_cloudformation_json(self):
|
||||
# This must be implemented as a classmethod with parameters:
|
||||
# cls, original_resource, new_resource_name, cloudformation_json, region_name
|
||||
# Extract the resource parameters from the cloudformation json,
|
||||
# delete the old resource and return the new one. Optionally inspect
|
||||
# the change in parameters and no-op when nothing has changed.
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def delete_from_cloudformation_json(self):
|
||||
# This must be implemented as a classmethod with parameters:
|
||||
# cls, resource_name, cloudformation_json, region_name
|
||||
# Extract the resource parameters from the cloudformation json
|
||||
# and delete the resource. Do not include a return statement.
|
||||
pass
|
||||
|
||||
|
||||
class BaseBackend(object):
|
||||
def _reset_model_refs(self):
|
||||
# Remove all references to the models stored
|
||||
|
@ -4,7 +4,7 @@ import datetime
|
||||
from boto3 import Session
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from .utils import get_random_pipeline_id, remove_capitalization_of_dict_keys
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ class PipelineObject(BaseModel):
|
||||
return {"fields": self.fields, "id": self.object_id, "name": self.name}
|
||||
|
||||
|
||||
class Pipeline(BaseModel):
|
||||
class Pipeline(CloudFormationModel):
|
||||
def __init__(self, name, unique_id, **kwargs):
|
||||
self.name = name
|
||||
self.unique_id = unique_id
|
||||
@ -74,6 +74,15 @@ class Pipeline(BaseModel):
|
||||
def activate(self):
|
||||
self.status = "SCHEDULED"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "Name"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-datapipeline-pipeline.html
|
||||
return "AWS::DataPipeline::Pipeline"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -4,7 +4,7 @@ import datetime
|
||||
import json
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import unix_time
|
||||
from moto.core import ACCOUNT_ID
|
||||
from .comparisons import get_comparison_func
|
||||
@ -82,7 +82,7 @@ class Item(BaseModel):
|
||||
return {"Item": included}
|
||||
|
||||
|
||||
class Table(BaseModel):
|
||||
class Table(CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
@ -135,6 +135,15 @@ class Table(BaseModel):
|
||||
}
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "TableName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html
|
||||
return "AWS::DynamoDB::Table"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -9,7 +9,7 @@ import uuid
|
||||
|
||||
from boto3 import Session
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import unix_time
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
from moto.dynamodb2.comparisons import get_filter_expression
|
||||
@ -359,7 +359,7 @@ class GlobalSecondaryIndex(SecondaryIndex):
|
||||
self.throughput = u.get("ProvisionedThroughput", self.throughput)
|
||||
|
||||
|
||||
class Table(BaseModel):
|
||||
class Table(CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
table_name,
|
||||
@ -431,6 +431,15 @@ class Table(BaseModel):
|
||||
def physical_resource_id(self):
|
||||
return self.name
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "TableName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-dynamodb-table.html
|
||||
return "AWS::DynamoDB::Table"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -22,7 +22,7 @@ from boto.ec2.launchspecification import LaunchSpecification
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend
|
||||
from moto.core.models import Model, BaseModel
|
||||
from moto.core.models import Model, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import (
|
||||
iso_8601_datetime_with_milliseconds,
|
||||
camelcase_to_underscores,
|
||||
@ -219,7 +219,7 @@ class TaggedEC2Resource(BaseModel):
|
||||
raise FilterNotImplementedError(filter_name, method_name)
|
||||
|
||||
|
||||
class NetworkInterface(TaggedEC2Resource):
|
||||
class NetworkInterface(TaggedEC2Resource, CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
ec2_backend,
|
||||
@ -268,6 +268,15 @@ class NetworkInterface(TaggedEC2Resource):
|
||||
if group:
|
||||
self._group_set.append(group)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-networkinterface.html
|
||||
return "AWS::EC2::NetworkInterface"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -454,7 +463,7 @@ class NetworkInterfaceBackend(object):
|
||||
return generic_filter(filters, enis)
|
||||
|
||||
|
||||
class Instance(TaggedEC2Resource, BotoInstance):
|
||||
class Instance(TaggedEC2Resource, BotoInstance, CloudFormationModel):
|
||||
VALID_ATTRIBUTES = {
|
||||
"instanceType",
|
||||
"kernel",
|
||||
@ -621,6 +630,15 @@ class Instance(TaggedEC2Resource, BotoInstance):
|
||||
formatted_ip, self.region_name
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-instance.html
|
||||
return "AWS::EC2::Instance"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -1843,7 +1861,7 @@ class SecurityRule(object):
|
||||
return True
|
||||
|
||||
|
||||
class SecurityGroup(TaggedEC2Resource):
|
||||
class SecurityGroup(TaggedEC2Resource, CloudFormationModel):
|
||||
def __init__(self, ec2_backend, group_id, name, description, vpc_id=None):
|
||||
self.ec2_backend = ec2_backend
|
||||
self.id = group_id
|
||||
@ -1861,6 +1879,15 @@ class SecurityGroup(TaggedEC2Resource):
|
||||
if vpc and len(vpc.get_cidr_block_association_set(ipv6=True)) > 0:
|
||||
self.egress_rules.append(SecurityRule("-1", None, None, [], []))
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "GroupName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-securitygroup.html
|
||||
return "AWS::EC2::SecurityGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -2260,11 +2287,20 @@ class SecurityGroupBackend(object):
|
||||
raise RulesPerSecurityGroupLimitExceededError
|
||||
|
||||
|
||||
class SecurityGroupIngress(object):
|
||||
class SecurityGroupIngress(CloudFormationModel):
|
||||
def __init__(self, security_group, properties):
|
||||
self.security_group = security_group
|
||||
self.properties = properties
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-securitygroupingress.html
|
||||
return "AWS::EC2::SecurityGroupIngress"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -2328,7 +2364,7 @@ class SecurityGroupIngress(object):
|
||||
return cls(security_group, properties)
|
||||
|
||||
|
||||
class VolumeAttachment(object):
|
||||
class VolumeAttachment(CloudFormationModel):
|
||||
def __init__(self, volume, instance, device, status):
|
||||
self.volume = volume
|
||||
self.attach_time = utc_date_and_time()
|
||||
@ -2336,6 +2372,15 @@ class VolumeAttachment(object):
|
||||
self.device = device
|
||||
self.status = status
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-volumeattachment.html
|
||||
return "AWS::EC2::VolumeAttachment"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -2354,7 +2399,7 @@ class VolumeAttachment(object):
|
||||
return attachment
|
||||
|
||||
|
||||
class Volume(TaggedEC2Resource):
|
||||
class Volume(TaggedEC2Resource, CloudFormationModel):
|
||||
def __init__(
|
||||
self, ec2_backend, volume_id, size, zone, snapshot_id=None, encrypted=False
|
||||
):
|
||||
@ -2367,6 +2412,15 @@ class Volume(TaggedEC2Resource):
|
||||
self.ec2_backend = ec2_backend
|
||||
self.encrypted = encrypted
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-volume.html
|
||||
return "AWS::EC2::Volume"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -2623,7 +2677,7 @@ class EBSBackend(object):
|
||||
return True
|
||||
|
||||
|
||||
class VPC(TaggedEC2Resource):
|
||||
class VPC(TaggedEC2Resource, CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
ec2_backend,
|
||||
@ -2656,6 +2710,15 @@ class VPC(TaggedEC2Resource):
|
||||
amazon_provided_ipv6_cidr_block=amazon_provided_ipv6_cidr_block,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpc.html
|
||||
return "AWS::EC2::VPC"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -3022,13 +3085,22 @@ class VPCPeeringConnectionStatus(object):
|
||||
self.message = "Inactive"
|
||||
|
||||
|
||||
class VPCPeeringConnection(TaggedEC2Resource):
|
||||
class VPCPeeringConnection(TaggedEC2Resource, CloudFormationModel):
|
||||
def __init__(self, vpc_pcx_id, vpc, peer_vpc):
|
||||
self.id = vpc_pcx_id
|
||||
self.vpc = vpc
|
||||
self.peer_vpc = peer_vpc
|
||||
self._status = VPCPeeringConnectionStatus()
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpcpeeringconnection.html
|
||||
return "AWS::EC2::VPCPeeringConnection"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -3114,7 +3186,7 @@ class VPCPeeringConnectionBackend(object):
|
||||
return vpc_pcx
|
||||
|
||||
|
||||
class Subnet(TaggedEC2Resource):
|
||||
class Subnet(TaggedEC2Resource, CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
ec2_backend,
|
||||
@ -3150,6 +3222,15 @@ class Subnet(TaggedEC2Resource):
|
||||
self._unused_ips = set() # if instance is destroyed hold IP here for reuse
|
||||
self._subnet_ips = {} # has IP: instance
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet.html
|
||||
return "AWS::EC2::Subnet"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -3377,11 +3458,20 @@ class SubnetBackend(object):
|
||||
raise InvalidParameterValueError(attr_name)
|
||||
|
||||
|
||||
class SubnetRouteTableAssociation(object):
|
||||
class SubnetRouteTableAssociation(CloudFormationModel):
|
||||
def __init__(self, route_table_id, subnet_id):
|
||||
self.route_table_id = route_table_id
|
||||
self.subnet_id = subnet_id
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnetroutetableassociation.html
|
||||
return "AWS::EC2::SubnetRouteTableAssociation"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -3411,7 +3501,7 @@ class SubnetRouteTableAssociationBackend(object):
|
||||
return subnet_association
|
||||
|
||||
|
||||
class RouteTable(TaggedEC2Resource):
|
||||
class RouteTable(TaggedEC2Resource, CloudFormationModel):
|
||||
def __init__(self, ec2_backend, route_table_id, vpc_id, main=False):
|
||||
self.ec2_backend = ec2_backend
|
||||
self.id = route_table_id
|
||||
@ -3420,6 +3510,15 @@ class RouteTable(TaggedEC2Resource):
|
||||
self.associations = {}
|
||||
self.routes = {}
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-routetable.html
|
||||
return "AWS::EC2::RouteTable"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -3555,7 +3654,7 @@ class RouteTableBackend(object):
|
||||
return self.associate_route_table(route_table_id, subnet_id)
|
||||
|
||||
|
||||
class Route(object):
|
||||
class Route(CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
route_table,
|
||||
@ -3581,6 +3680,15 @@ class Route(object):
|
||||
self.interface = interface
|
||||
self.vpc_pcx = vpc_pcx
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-route.html
|
||||
return "AWS::EC2::Route"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -3748,12 +3856,21 @@ class RouteBackend(object):
|
||||
return deleted
|
||||
|
||||
|
||||
class InternetGateway(TaggedEC2Resource):
|
||||
class InternetGateway(TaggedEC2Resource, CloudFormationModel):
|
||||
def __init__(self, ec2_backend):
|
||||
self.ec2_backend = ec2_backend
|
||||
self.id = random_internet_gateway_id()
|
||||
self.vpc = None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-internetgateway.html
|
||||
return "AWS::EC2::InternetGateway"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -3826,11 +3943,20 @@ class InternetGatewayBackend(object):
|
||||
return self.describe_internet_gateways(internet_gateway_ids=igw_ids)[0]
|
||||
|
||||
|
||||
class VPCGatewayAttachment(BaseModel):
|
||||
class VPCGatewayAttachment(CloudFormationModel):
|
||||
def __init__(self, gateway_id, vpc_id):
|
||||
self.gateway_id = gateway_id
|
||||
self.vpc_id = vpc_id
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-vpcgatewayattachment.html
|
||||
return "AWS::EC2::VPCGatewayAttachment"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -4051,7 +4177,7 @@ class SpotFleetLaunchSpec(object):
|
||||
self.weighted_capacity = float(weighted_capacity)
|
||||
|
||||
|
||||
class SpotFleetRequest(TaggedEC2Resource):
|
||||
class SpotFleetRequest(TaggedEC2Resource, CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
ec2_backend,
|
||||
@ -4100,6 +4226,15 @@ class SpotFleetRequest(TaggedEC2Resource):
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-spotfleet.html
|
||||
return "AWS::EC2::SpotFleet"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -4323,7 +4458,7 @@ class SpotFleetBackend(object):
|
||||
return True
|
||||
|
||||
|
||||
class ElasticAddress(object):
|
||||
class ElasticAddress(CloudFormationModel):
|
||||
def __init__(self, domain, address=None):
|
||||
if address:
|
||||
self.public_ip = address
|
||||
@ -4335,6 +4470,15 @@ class ElasticAddress(object):
|
||||
self.eni = None
|
||||
self.association_id = None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-eip.html
|
||||
return "AWS::EC2::EIP"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -5095,7 +5239,7 @@ class CustomerGatewayBackend(object):
|
||||
return deleted
|
||||
|
||||
|
||||
class NatGateway(object):
|
||||
class NatGateway(CloudFormationModel):
|
||||
def __init__(self, backend, subnet_id, allocation_id):
|
||||
# public properties
|
||||
self.id = random_nat_gateway_id()
|
||||
@ -5133,6 +5277,15 @@ class NatGateway(object):
|
||||
eips = self._backend.address_by_allocation([self.allocation_id])
|
||||
return eips[0].public_ip
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-natgateway.html
|
||||
return "AWS::EC2::NatGateway"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -7,7 +7,7 @@ from random import random
|
||||
|
||||
from botocore.exceptions import ParamValidationError
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.ec2 import ec2_backends
|
||||
from moto.ecr.exceptions import ImageNotFoundException, RepositoryNotFoundException
|
||||
|
||||
@ -38,7 +38,7 @@ class BaseObject(BaseModel):
|
||||
return self.gen_response_object()
|
||||
|
||||
|
||||
class Repository(BaseObject):
|
||||
class Repository(BaseObject, CloudFormationModel):
|
||||
def __init__(self, repository_name):
|
||||
self.registry_id = DEFAULT_REGISTRY_ID
|
||||
self.arn = "arn:aws:ecr:us-east-1:{0}:repository/{1}".format(
|
||||
@ -67,6 +67,15 @@ class Repository(BaseObject):
|
||||
del response_object["arn"], response_object["name"], response_object["images"]
|
||||
return response_object
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "RepositoryName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecr-repository.html
|
||||
return "AWS::ECR::Repository"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -8,7 +8,7 @@ import pytz
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import unix_time
|
||||
from moto.ec2 import ec2_backends
|
||||
from copy import copy
|
||||
@ -44,7 +44,7 @@ class BaseObject(BaseModel):
|
||||
return self.gen_response_object()
|
||||
|
||||
|
||||
class Cluster(BaseObject):
|
||||
class Cluster(BaseObject, CloudFormationModel):
|
||||
def __init__(self, cluster_name, region_name):
|
||||
self.active_services_count = 0
|
||||
self.arn = "arn:aws:ecs:{0}:012345678910:cluster/{1}".format(
|
||||
@ -69,6 +69,15 @@ class Cluster(BaseObject):
|
||||
del response_object["arn"], response_object["name"]
|
||||
return response_object
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "ClusterName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-cluster.html
|
||||
return "AWS::ECS::Cluster"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -116,7 +125,7 @@ class Cluster(BaseObject):
|
||||
raise UnformattedGetAttTemplateException()
|
||||
|
||||
|
||||
class TaskDefinition(BaseObject):
|
||||
class TaskDefinition(BaseObject, CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
family,
|
||||
@ -159,6 +168,15 @@ class TaskDefinition(BaseObject):
|
||||
def physical_resource_id(self):
|
||||
return self.arn
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html
|
||||
return "AWS::ECS::TaskDefinition"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -235,7 +253,7 @@ class Task(BaseObject):
|
||||
return response_object
|
||||
|
||||
|
||||
class Service(BaseObject):
|
||||
class Service(BaseObject, CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
cluster,
|
||||
@ -315,6 +333,15 @@ class Service(BaseObject):
|
||||
|
||||
return response_object
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "ServiceName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-service.html
|
||||
return "AWS::ECS::Service"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -13,7 +13,7 @@ from boto.ec2.elb.attributes import (
|
||||
)
|
||||
from boto.ec2.elb.policies import Policies, OtherPolicy
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.ec2.models import ec2_backends
|
||||
from .exceptions import (
|
||||
BadHealthCheckDefinition,
|
||||
@ -69,7 +69,7 @@ class FakeBackend(BaseModel):
|
||||
)
|
||||
|
||||
|
||||
class FakeLoadBalancer(BaseModel):
|
||||
class FakeLoadBalancer(CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
@ -119,6 +119,15 @@ class FakeLoadBalancer(BaseModel):
|
||||
)
|
||||
self.backends.append(backend)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "LoadBalancerName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancing-loadbalancer.html
|
||||
return "AWS::ElasticLoadBalancing::LoadBalancer"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -6,7 +6,7 @@ from jinja2 import Template
|
||||
from botocore.exceptions import ParamValidationError
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core.exceptions import RESTError
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import camelcase_to_underscores, underscores_to_camelcase
|
||||
from moto.ec2.models import ec2_backends
|
||||
from moto.acm.models import acm_backends
|
||||
@ -50,7 +50,7 @@ class FakeHealthStatus(BaseModel):
|
||||
self.description = description
|
||||
|
||||
|
||||
class FakeTargetGroup(BaseModel):
|
||||
class FakeTargetGroup(CloudFormationModel):
|
||||
HTTP_CODE_REGEX = re.compile(r"(?:(?:\d+-\d+|\d+),?)+")
|
||||
|
||||
def __init__(
|
||||
@ -143,6 +143,15 @@ class FakeTargetGroup(BaseModel):
|
||||
)
|
||||
return FakeHealthStatus(t["id"], t["port"], self.healthcheck_port, "healthy")
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "Name"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-targetgroup.html
|
||||
return "AWS::ElasticLoadBalancingV2::TargetGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -183,7 +192,7 @@ class FakeTargetGroup(BaseModel):
|
||||
return target_group
|
||||
|
||||
|
||||
class FakeListener(BaseModel):
|
||||
class FakeListener(CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
load_balancer_arn,
|
||||
@ -228,6 +237,15 @@ class FakeListener(BaseModel):
|
||||
self._non_default_rules, key=lambda x: x.priority
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-listener.html
|
||||
return "AWS::ElasticLoadBalancingV2::Listener"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -343,7 +361,7 @@ class FakeBackend(BaseModel):
|
||||
)
|
||||
|
||||
|
||||
class FakeLoadBalancer(BaseModel):
|
||||
class FakeLoadBalancer(CloudFormationModel):
|
||||
VALID_ATTRS = {
|
||||
"access_logs.s3.enabled",
|
||||
"access_logs.s3.bucket",
|
||||
@ -402,6 +420,15 @@ class FakeLoadBalancer(BaseModel):
|
||||
""" Not exposed as part of the ELB API - used for CloudFormation. """
|
||||
elbv2_backends[region].delete_load_balancer(self.arn)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "Name"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-loadbalancer.html
|
||||
return "AWS::ElasticLoadBalancingV2::LoadBalancer"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -4,14 +4,14 @@ import json
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, CloudFormationModel
|
||||
from moto.sts.models import ACCOUNT_ID
|
||||
from moto.utilities.tagging_service import TaggingService
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
class Rule(BaseModel):
|
||||
class Rule(CloudFormationModel):
|
||||
def _generate_arn(self, name):
|
||||
return "arn:aws:events:{region_name}:111111111111:rule/{name}".format(
|
||||
region_name=self.region_name, name=name
|
||||
@ -73,6 +73,15 @@ class Rule(BaseModel):
|
||||
|
||||
raise UnformattedGetAttTemplateException()
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "Name"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html
|
||||
return "AWS::Events::Rule"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -101,7 +110,7 @@ class Rule(BaseModel):
|
||||
event_backend.delete_rule(name=event_name)
|
||||
|
||||
|
||||
class EventBus(BaseModel):
|
||||
class EventBus(CloudFormationModel):
|
||||
def __init__(self, region_name, name):
|
||||
self.region = region_name
|
||||
self.name = name
|
||||
@ -152,6 +161,15 @@ class EventBus(BaseModel):
|
||||
|
||||
raise UnformattedGetAttTemplateException()
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "Name"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-eventbus.html
|
||||
return "AWS::Events::EventBus"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -15,7 +15,7 @@ from six.moves.urllib.parse import urlparse
|
||||
from uuid import uuid4
|
||||
|
||||
from moto.core.exceptions import RESTError
|
||||
from moto.core import BaseBackend, BaseModel, ACCOUNT_ID
|
||||
from moto.core import BaseBackend, BaseModel, ACCOUNT_ID, CloudFormationModel
|
||||
from moto.core.utils import (
|
||||
iso_8601_datetime_without_milliseconds,
|
||||
iso_8601_datetime_with_milliseconds,
|
||||
@ -299,7 +299,7 @@ class InlinePolicy(Policy):
|
||||
"""TODO: is this needed?"""
|
||||
|
||||
|
||||
class Role(BaseModel):
|
||||
class Role(CloudFormationModel):
|
||||
def __init__(
|
||||
self,
|
||||
role_id,
|
||||
@ -327,6 +327,15 @@ class Role(BaseModel):
|
||||
def created_iso_8601(self):
|
||||
return iso_8601_datetime_with_milliseconds(self.create_date)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "RoleName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html
|
||||
return "AWS::IAM::Role"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -384,7 +393,7 @@ class Role(BaseModel):
|
||||
return [self.tags[tag] for tag in self.tags]
|
||||
|
||||
|
||||
class InstanceProfile(BaseModel):
|
||||
class InstanceProfile(CloudFormationModel):
|
||||
def __init__(self, instance_profile_id, name, path, roles):
|
||||
self.id = instance_profile_id
|
||||
self.name = name
|
||||
@ -396,6 +405,15 @@ class InstanceProfile(BaseModel):
|
||||
def created_iso_8601(self):
|
||||
return iso_8601_datetime_with_milliseconds(self.create_date)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "InstanceProfileName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html
|
||||
return "AWS::IAM::InstanceProfile"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -12,7 +12,7 @@ from hashlib import md5
|
||||
from boto3 import Session
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import unix_time
|
||||
from moto.core import ACCOUNT_ID
|
||||
from .exceptions import (
|
||||
@ -129,7 +129,7 @@ class Shard(BaseModel):
|
||||
}
|
||||
|
||||
|
||||
class Stream(BaseModel):
|
||||
class Stream(CloudFormationModel):
|
||||
def __init__(self, stream_name, shard_count, region):
|
||||
self.stream_name = stream_name
|
||||
self.shard_count = shard_count
|
||||
@ -216,6 +216,15 @@ class Stream(BaseModel):
|
||||
}
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "Name"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kinesis-stream.html
|
||||
return "AWS::Kinesis::Stream"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -6,7 +6,7 @@ from datetime import datetime, timedelta
|
||||
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, CloudFormationModel
|
||||
from moto.core.utils import unix_time
|
||||
from moto.utilities.tagging_service import TaggingService
|
||||
from moto.core.exceptions import JsonRESTError
|
||||
@ -15,7 +15,7 @@ from moto.iam.models import ACCOUNT_ID
|
||||
from .utils import decrypt, encrypt, generate_key_id, generate_master_key
|
||||
|
||||
|
||||
class Key(BaseModel):
|
||||
class Key(CloudFormationModel):
|
||||
def __init__(
|
||||
self, policy, key_usage, customer_master_key_spec, description, region
|
||||
):
|
||||
@ -99,6 +99,15 @@ class Key(BaseModel):
|
||||
def delete(self, region_name):
|
||||
kms_backends[region_name].delete_key(self.id)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-kms-key.html
|
||||
return "AWS::KMS::Key"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
self, resource_name, cloudformation_json, region_name
|
||||
|
@ -3,14 +3,14 @@ from __future__ import unicode_literals
|
||||
import boto.rds
|
||||
from jinja2 import Template
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, CloudFormationModel
|
||||
from moto.core.utils import get_random_hex
|
||||
from moto.ec2.models import ec2_backends
|
||||
from moto.rds.exceptions import UnformattedGetAttTemplateException
|
||||
from moto.rds2.models import rds2_backends
|
||||
|
||||
|
||||
class Database(BaseModel):
|
||||
class Database(CloudFormationModel):
|
||||
def get_cfn_attribute(self, attribute_name):
|
||||
if attribute_name == "Endpoint.Address":
|
||||
return self.address
|
||||
@ -18,13 +18,22 @@ class Database(BaseModel):
|
||||
return self.port
|
||||
raise UnformattedGetAttTemplateException()
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "DBInstanceIdentifier"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbinstance.html
|
||||
return "AWS::RDS::DBInstance"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
):
|
||||
properties = cloudformation_json["Properties"]
|
||||
|
||||
db_instance_identifier = properties.get("DBInstanceIdentifier")
|
||||
db_instance_identifier = properties.get(cls.cloudformation_name_type())
|
||||
if not db_instance_identifier:
|
||||
db_instance_identifier = resource_name.lower() + get_random_hex(12)
|
||||
db_security_groups = properties.get("DBSecurityGroups")
|
||||
@ -163,7 +172,7 @@ class Database(BaseModel):
|
||||
backend.delete_database(self.db_instance_identifier)
|
||||
|
||||
|
||||
class SecurityGroup(BaseModel):
|
||||
class SecurityGroup(CloudFormationModel):
|
||||
def __init__(self, group_name, description):
|
||||
self.group_name = group_name
|
||||
self.description = description
|
||||
@ -206,6 +215,15 @@ class SecurityGroup(BaseModel):
|
||||
def authorize_security_group(self, security_group):
|
||||
self.ec2_security_groups.append(security_group)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbsecuritygroup.html
|
||||
return "AWS::RDS::DBSecurityGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -239,7 +257,7 @@ class SecurityGroup(BaseModel):
|
||||
backend.delete_security_group(self.group_name)
|
||||
|
||||
|
||||
class SubnetGroup(BaseModel):
|
||||
class SubnetGroup(CloudFormationModel):
|
||||
def __init__(self, subnet_name, description, subnets):
|
||||
self.subnet_name = subnet_name
|
||||
self.description = description
|
||||
@ -271,13 +289,23 @@ class SubnetGroup(BaseModel):
|
||||
)
|
||||
return template.render(subnet_group=self)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "DBSubnetGroupName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbsubnetgroup.html
|
||||
return "AWS::RDS::DBSubnetGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
):
|
||||
properties = cloudformation_json["Properties"]
|
||||
|
||||
subnet_name = resource_name.lower() + get_random_hex(12)
|
||||
subnet_name = properties.get(cls.cloudformation_name_type())
|
||||
if not subnet_name:
|
||||
subnet_name = resource_name.lower() + get_random_hex(12)
|
||||
description = properties["DBSubnetGroupDescription"]
|
||||
subnet_ids = properties["SubnetIds"]
|
||||
tags = properties.get("Tags")
|
||||
|
@ -9,7 +9,7 @@ from boto3 import Session
|
||||
from jinja2 import Template
|
||||
from re import compile as re_compile
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import get_random_hex
|
||||
from moto.core.utils import iso_8601_datetime_with_milliseconds
|
||||
from moto.ec2.models import ec2_backends
|
||||
@ -28,7 +28,7 @@ from .exceptions import (
|
||||
)
|
||||
|
||||
|
||||
class Database(BaseModel):
|
||||
class Database(CloudFormationModel):
|
||||
def __init__(self, **kwargs):
|
||||
self.status = "available"
|
||||
self.is_replica = False
|
||||
@ -356,13 +356,22 @@ class Database(BaseModel):
|
||||
"sqlserver-web": {"gp2": 20, "io1": 100, "standard": 20},
|
||||
}[engine][storage_type]
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "DBInstanceIdentifier"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbinstance.html
|
||||
return "AWS::RDS::DBInstance"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
):
|
||||
properties = cloudformation_json["Properties"]
|
||||
|
||||
db_instance_identifier = properties.get("DBInstanceIdentifier")
|
||||
db_instance_identifier = properties.get(cls.cloudformation_name_type())
|
||||
if not db_instance_identifier:
|
||||
db_instance_identifier = resource_name.lower() + get_random_hex(12)
|
||||
db_security_groups = properties.get("DBSecurityGroups")
|
||||
@ -564,7 +573,7 @@ class Snapshot(BaseModel):
|
||||
self.tags = [tag_set for tag_set in self.tags if tag_set["Key"] not in tag_keys]
|
||||
|
||||
|
||||
class SecurityGroup(BaseModel):
|
||||
class SecurityGroup(CloudFormationModel):
|
||||
def __init__(self, group_name, description, tags):
|
||||
self.group_name = group_name
|
||||
self.description = description
|
||||
@ -627,6 +636,15 @@ class SecurityGroup(BaseModel):
|
||||
def authorize_security_group(self, security_group):
|
||||
self.ec2_security_groups.append(security_group)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbsecuritygroup.html
|
||||
return "AWS::RDS::DBSecurityGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -671,7 +689,7 @@ class SecurityGroup(BaseModel):
|
||||
backend.delete_security_group(self.group_name)
|
||||
|
||||
|
||||
class SubnetGroup(BaseModel):
|
||||
class SubnetGroup(CloudFormationModel):
|
||||
def __init__(self, subnet_name, description, subnets, tags):
|
||||
self.subnet_name = subnet_name
|
||||
self.description = description
|
||||
@ -726,13 +744,24 @@ class SubnetGroup(BaseModel):
|
||||
)
|
||||
return template.render(subnet_group=self)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "DBSubnetGroupName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbsubnetgroup.html
|
||||
return "AWS::RDS::DBSubnetGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
):
|
||||
properties = cloudformation_json["Properties"]
|
||||
|
||||
subnet_name = resource_name.lower() + get_random_hex(12)
|
||||
subnet_name = properties.get(cls.cloudformation_name_type())
|
||||
if not subnet_name:
|
||||
subnet_name = resource_name.lower() + get_random_hex(12)
|
||||
description = properties["DBSubnetGroupDescription"]
|
||||
subnet_ids = properties["SubnetIds"]
|
||||
tags = properties.get("Tags")
|
||||
@ -1441,7 +1470,7 @@ class OptionGroupOptionSetting(object):
|
||||
return template.render(option_group_option_setting=self)
|
||||
|
||||
|
||||
class DBParameterGroup(object):
|
||||
class DBParameterGroup(CloudFormationModel):
|
||||
def __init__(self, name, description, family, tags):
|
||||
self.name = name
|
||||
self.description = description
|
||||
@ -1480,6 +1509,15 @@ class DBParameterGroup(object):
|
||||
backend = rds2_backends[region_name]
|
||||
backend.delete_db_parameter_group(self.name)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbparametergroup.html
|
||||
return "AWS::RDS::DBParameterGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -6,7 +6,7 @@ import datetime
|
||||
from boto3 import Session
|
||||
from botocore.exceptions import ClientError
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import iso_8601_datetime_with_milliseconds
|
||||
from moto.ec2 import ec2_backends
|
||||
from .exceptions import (
|
||||
@ -63,7 +63,7 @@ class TaggableResourceMixin(object):
|
||||
return self.tags
|
||||
|
||||
|
||||
class Cluster(TaggableResourceMixin, BaseModel):
|
||||
class Cluster(TaggableResourceMixin, CloudFormationModel):
|
||||
|
||||
resource_type = "cluster"
|
||||
|
||||
@ -157,6 +157,15 @@ class Cluster(TaggableResourceMixin, BaseModel):
|
||||
self.iam_roles_arn = iam_roles_arn or []
|
||||
self.restored_from_snapshot = restored_from_snapshot
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshift-cluster.html
|
||||
return "AWS::Redshift::Cluster"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -170,6 +179,7 @@ class Cluster(TaggableResourceMixin, BaseModel):
|
||||
].cluster_subnet_group_name
|
||||
else:
|
||||
subnet_group_name = None
|
||||
|
||||
cluster = redshift_backend.create_cluster(
|
||||
cluster_identifier=resource_name,
|
||||
node_type=properties.get("NodeType"),
|
||||
@ -321,7 +331,7 @@ class SnapshotCopyGrant(TaggableResourceMixin, BaseModel):
|
||||
}
|
||||
|
||||
|
||||
class SubnetGroup(TaggableResourceMixin, BaseModel):
|
||||
class SubnetGroup(TaggableResourceMixin, CloudFormationModel):
|
||||
|
||||
resource_type = "subnetgroup"
|
||||
|
||||
@ -342,6 +352,15 @@ class SubnetGroup(TaggableResourceMixin, BaseModel):
|
||||
if not self.subnets:
|
||||
raise InvalidSubnetError(subnet_ids)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshift-clustersubnetgroup.html
|
||||
return "AWS::Redshift::ClusterSubnetGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -412,7 +431,7 @@ class SecurityGroup(TaggableResourceMixin, BaseModel):
|
||||
}
|
||||
|
||||
|
||||
class ParameterGroup(TaggableResourceMixin, BaseModel):
|
||||
class ParameterGroup(TaggableResourceMixin, CloudFormationModel):
|
||||
|
||||
resource_type = "parametergroup"
|
||||
|
||||
@ -429,6 +448,15 @@ class ParameterGroup(TaggableResourceMixin, BaseModel):
|
||||
self.group_family = group_family
|
||||
self.description = description
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-redshift-clusterparametergroup.html
|
||||
return "AWS::Redshift::ClusterParameterGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -7,7 +7,7 @@ import random
|
||||
import uuid
|
||||
from jinja2 import Template
|
||||
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, CloudFormationModel
|
||||
|
||||
|
||||
ROUTE53_ID_CHOICE = string.ascii_uppercase + string.digits
|
||||
@ -18,7 +18,7 @@ def create_route53_zone_id():
|
||||
return "".join([random.choice(ROUTE53_ID_CHOICE) for _ in range(0, 15)])
|
||||
|
||||
|
||||
class HealthCheck(BaseModel):
|
||||
class HealthCheck(CloudFormationModel):
|
||||
def __init__(self, health_check_id, health_check_args):
|
||||
self.id = health_check_id
|
||||
self.ip_address = health_check_args.get("ip_address")
|
||||
@ -34,6 +34,15 @@ class HealthCheck(BaseModel):
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-route53-healthcheck.html
|
||||
return "AWS::Route53::HealthCheck"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -75,7 +84,7 @@ class HealthCheck(BaseModel):
|
||||
return template.render(health_check=self)
|
||||
|
||||
|
||||
class RecordSet(BaseModel):
|
||||
class RecordSet(CloudFormationModel):
|
||||
def __init__(self, kwargs):
|
||||
self.name = kwargs.get("Name")
|
||||
self.type_ = kwargs.get("Type")
|
||||
@ -91,6 +100,15 @@ class RecordSet(BaseModel):
|
||||
self.failover = kwargs.get("Failover")
|
||||
self.geo_location = kwargs.get("GeoLocation")
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "Name"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-route53-recordset.html
|
||||
return "AWS::Route53::RecordSet"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -202,7 +220,7 @@ def reverse_domain_name(domain_name):
|
||||
return ".".join(reversed(domain_name.split(".")))
|
||||
|
||||
|
||||
class FakeZone(BaseModel):
|
||||
class FakeZone(CloudFormationModel):
|
||||
def __init__(self, name, id_, private_zone, comment=None):
|
||||
self.name = name
|
||||
self.id = id_
|
||||
@ -267,6 +285,15 @@ class FakeZone(BaseModel):
|
||||
def physical_resource_id(self):
|
||||
return self.id
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "Name"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-route53-hostedzone.html
|
||||
return "AWS::Route53::HostedZone"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -278,7 +305,7 @@ class FakeZone(BaseModel):
|
||||
return hosted_zone
|
||||
|
||||
|
||||
class RecordSetGroup(BaseModel):
|
||||
class RecordSetGroup(CloudFormationModel):
|
||||
def __init__(self, hosted_zone_id, record_sets):
|
||||
self.hosted_zone_id = hosted_zone_id
|
||||
self.record_sets = record_sets
|
||||
@ -287,6 +314,15 @@ class RecordSetGroup(BaseModel):
|
||||
def physical_resource_id(self):
|
||||
return "arn:aws:route53:::hostedzone/{0}".format(self.hosted_zone_id)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-route53-recordsetgroup.html
|
||||
return "AWS::Route53::RecordSetGroup"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -21,7 +21,7 @@ import uuid
|
||||
import six
|
||||
|
||||
from bisect import insort
|
||||
from moto.core import ACCOUNT_ID, BaseBackend, BaseModel
|
||||
from moto.core import ACCOUNT_ID, BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import iso_8601_datetime_without_milliseconds_s3, rfc_1123_datetime
|
||||
from moto.cloudwatch.models import MetricDatum
|
||||
from moto.utilities.tagging_service import TaggingService
|
||||
@ -763,7 +763,7 @@ class PublicAccessBlock(BaseModel):
|
||||
}
|
||||
|
||||
|
||||
class FakeBucket(BaseModel):
|
||||
class FakeBucket(CloudFormationModel):
|
||||
def __init__(self, name, region_name):
|
||||
self.name = name
|
||||
self.region_name = region_name
|
||||
@ -1070,6 +1070,15 @@ class FakeBucket(BaseModel):
|
||||
def physical_resource_id(self):
|
||||
return self.name
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "BucketName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-s3-bucket.html
|
||||
return "AWS::S3::Bucket"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
@ -11,7 +11,7 @@ import re
|
||||
from boto3 import Session
|
||||
|
||||
from moto.compat import OrderedDict
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import (
|
||||
iso_8601_datetime_with_milliseconds,
|
||||
camelcase_to_underscores,
|
||||
@ -37,7 +37,7 @@ DEFAULT_PAGE_SIZE = 100
|
||||
MAXIMUM_MESSAGE_LENGTH = 262144 # 256 KiB
|
||||
|
||||
|
||||
class Topic(BaseModel):
|
||||
class Topic(CloudFormationModel):
|
||||
def __init__(self, name, sns_backend):
|
||||
self.name = name
|
||||
self.sns_backend = sns_backend
|
||||
@ -87,6 +87,15 @@ class Topic(BaseModel):
|
||||
def policy(self, policy):
|
||||
self._policy_json = json.loads(policy)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "TopicName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sns-topic.html
|
||||
return "AWS::SNS::Topic"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
@ -94,7 +103,7 @@ class Topic(BaseModel):
|
||||
sns_backend = sns_backends[region_name]
|
||||
properties = cloudformation_json["Properties"]
|
||||
|
||||
topic = sns_backend.create_topic(properties.get("TopicName"))
|
||||
topic = sns_backend.create_topic(properties.get(cls.cloudformation_name_type()))
|
||||
for subscription in properties.get("Subscription", []):
|
||||
sns_backend.subscribe(
|
||||
topic.arn, subscription["Endpoint"], subscription["Protocol"]
|
||||
|
@ -12,7 +12,7 @@ from xml.sax.saxutils import escape
|
||||
from boto3 import Session
|
||||
|
||||
from moto.core.exceptions import RESTError
|
||||
from moto.core import BaseBackend, BaseModel
|
||||
from moto.core import BaseBackend, BaseModel, CloudFormationModel
|
||||
from moto.core.utils import (
|
||||
camelcase_to_underscores,
|
||||
get_random_message_id,
|
||||
@ -188,7 +188,7 @@ class Message(BaseModel):
|
||||
return False
|
||||
|
||||
|
||||
class Queue(BaseModel):
|
||||
class Queue(CloudFormationModel):
|
||||
BASE_ATTRIBUTES = [
|
||||
"ApproximateNumberOfMessages",
|
||||
"ApproximateNumberOfMessagesDelayed",
|
||||
@ -354,6 +354,15 @@ class Queue(BaseModel):
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_name_type():
|
||||
return "QueueName"
|
||||
|
||||
@staticmethod
|
||||
def cloudformation_type():
|
||||
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-sqs-queue.html
|
||||
return "AWS::SQS::Queue"
|
||||
|
||||
@classmethod
|
||||
def create_from_cloudformation_json(
|
||||
cls, resource_name, cloudformation_json, region_name
|
||||
|
Loading…
Reference in New Issue
Block a user