ApplicationAutoscaling: support for all the current various forms of resource_id (#3305)

* Change to test_s3 method test_presigned_url_restrict_parameters to tolerate change in exception messages, spurred by boto3 1.14.59 release.

* ApplicationAutoscaling: support for all the current various forms of resource_id.

* Factored logic for extracting application autoscaling resource_type from resource_id to separate function, per PR3304 comment.

Co-authored-by: Joseph Weitekamp <jweite@amazon.com>
This commit is contained in:
jweite 2020-09-19 15:13:44 -04:00 committed by GitHub
parent 7ce1e87477
commit da4de072a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 101 additions and 1 deletions

View File

@ -135,7 +135,7 @@ def _target_params_are_valid(namespace, r_id, dimension):
try:
valid_dimensions = [d.value for d in ScalableDimensionValueSet]
d_namespace, d_resource_type, scaling_property = dimension.split(":")
resource_type, cluster, service = r_id.split("/")
resource_type = _get_resource_type_from_resource_id(r_id)
if (
dimension not in valid_dimensions
or d_namespace != namespace
@ -151,6 +151,33 @@ def _target_params_are_valid(namespace, r_id, dimension):
return is_valid
def _get_resource_type_from_resource_id(resource_id):
# AWS Application Autoscaling resource_ids are multi-component (path-like) identifiers that vary in format,
# depending on the type of resource it identifies. resource_type is one of its components.
# resource_id format variations are described in
# https://docs.aws.amazon.com/autoscaling/application/APIReference/API_RegisterScalableTarget.html
# In a nutshell:
# - Most use slash separators, but some use colon separators.
# - The resource type is usually the first component of the resource_id...
# - ...except for sagemaker endpoints, dynamodb GSIs and keyspaces tables, where it's the third.
# - Comprehend uses an arn, with the resource type being the last element.
if resource_id.startswith("arn:aws:comprehend"):
resource_id = resource_id.split(":")[-1]
resource_split = (
resource_id.split("/") if "/" in resource_id else resource_id.split(":")
)
if (
resource_split[0] == "endpoint"
or (resource_split[0] == "table" and len(resource_split) > 2)
or (resource_split[0] == "keyspace")
):
resource_type = resource_split[2]
else:
resource_type = resource_split[0]
return resource_type
class FakeScalableTarget(BaseModel):
def __init__(
self, backend, service_namespace, resource_id, scalable_dimension, **kwargs

View File

@ -187,3 +187,76 @@ def register_scalable_target(client, **kwargs):
RoleARN=kwargs.get("RoleARN", DEFAULT_ROLE_ARN),
SuspendedState=kwargs.get("SuspendedState", DEFAULT_SUSPENDED_STATE),
)
@mock_ecs
@mock_applicationautoscaling
def test_register_scalable_target_resource_id_variations():
# Required to register an ECS target in moto
ecs = boto3.client("ecs", region_name=DEFAULT_REGION)
_create_ecs_defaults(ecs)
# See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-applicationautoscaling-scalabletarget.html
resource_id_variations = [
(
DEFAULT_SERVICE_NAMESPACE,
DEFAULT_RESOURCE_ID,
DEFAULT_SCALABLE_DIMENSION,
), # ECS
(
"ec2",
"spot-fleet-request/sfr-73fbd2ce-aa30-494c-8788-1cee4EXAMPLE",
"ec2:spot-fleet-request:TargetCapacity",
),
(
"elasticmapreduce",
"instancegroup/j-2EEZNYKUA1NTV/ig-1791Y4E1L8YI0",
"elasticmapreduce:instancegroup:InstanceCount",
),
("appstream", "fleet/sample-fleet", "appstream:fleet:DesiredCapacity"),
("dynamodb", "table/my-table", "dynamodb:table:ReadCapacityUnits"),
(
"dynamodb",
"table/my-table/index/my-table-index",
"dynamodb:index:ReadCapacityUnits",
),
("rds", "cluster:my-db-cluster", "rds:cluster:ReadReplicaCount"),
(
"sagemaker",
"endpoint/MyEndPoint/variant/MyVariant",
"sagemaker:variant:DesiredInstanceCount",
),
(
"comprehend",
"arn:aws:comprehend:us-west-2:123456789012:document-classifier-endpoint/EXAMPLE",
"comprehend:document-classifier-endpoint:DesiredInferenceUnits",
),
(
"lambda",
"function:my-function:prod",
"lambda:function:ProvisionedConcurrency",
),
(
"cassandra",
"keyspace/mykeyspace/table/mytable",
"cassandra:table:ReadCapacityUnits",
),
]
client = boto3.client("application-autoscaling", region_name=DEFAULT_REGION)
for namespace, resource_id, scalable_dimension in resource_id_variations:
client.register_scalable_target(
ServiceNamespace=namespace,
ResourceId=resource_id,
ScalableDimension=scalable_dimension,
)
response = client.describe_scalable_targets(ServiceNamespace=namespace)
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(200)
num_targets = 2 if namespace == "dynamodb" and "index" in resource_id else 1
len(response["ScalableTargets"]).should.equal(num_targets)
t = response["ScalableTargets"][-1]
t.should.have.key("ServiceNamespace").which.should.equal(namespace)
t.should.have.key("ResourceId").which.should.equal(resource_id)
t.should.have.key("ScalableDimension").which.should.equal(scalable_dimension)
t.should.have.key("CreationTime").which.should.be.a("datetime.datetime")