ECS: allow ecs service to transition to arbitrary running task count (#6289)

This commit is contained in:
rafcio19 2023-05-11 18:38:29 +01:00 committed by GitHub
parent 6b2ee153e6
commit 60065b3cf8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 116 additions and 21 deletions

View File

@ -2,6 +2,7 @@ import re
from copy import copy
from datetime import datetime, timezone
from typing import Any, Dict, Iterator, List, Optional, Tuple
from os import getenv
from moto import settings
from moto.core import BaseBackend, BackendDict, BaseModel, CloudFormationModel
@ -488,6 +489,14 @@ class CapacityProviderFailure(BaseObject):
class Service(BaseObject, CloudFormationModel):
"""Set the environment variable MOTO_ECS_SERVICE_RUNNING to a number of running tasks you want
the service to transition to, ie if set to 2:
MOTO_ECS_SERVICE_RUNNING=2
then describe_services call to return runningCount of the service AND deployment to 2
"""
def __init__(
self,
cluster: Cluster,
@ -507,7 +516,6 @@ class Service(BaseObject, CloudFormationModel):
self.cluster_arn = cluster.arn
self.name = service_name
self.status = "ACTIVE"
self.running_count = 0
self.task_definition = task_definition.arn if task_definition else None
self.desired_count = desired_count
self.task_sets: List[TaskSet] = []
@ -515,6 +523,25 @@ class Service(BaseObject, CloudFormationModel):
self.events: List[Dict[str, Any]] = []
self.launch_type = launch_type
self.service_registries = service_registries or []
self.load_balancers = load_balancers if load_balancers is not None else []
self.scheduling_strategy = (
scheduling_strategy if scheduling_strategy is not None else "REPLICA"
)
self.platform_version = platform_version
self.tags = tags if tags is not None else []
self.region_name = cluster.region_name
self._account_id = backend.account_id
self._backend = backend
try:
# negative running count not allowed, set to 0 if so
ecs_running_count = max(int(getenv("MOTO_ECS_SERVICE_RUNNING", 0)), 0)
except ValueError:
# Unable to parse value of MOTO_ECS_SERVICE_RUNNING as an integer, set to default 0
ecs_running_count = 0
self.running_count = ecs_running_count
self.pending_count = desired_count - ecs_running_count
if self.deployment_controller["type"] == "ECS":
self.deployments = [
{
@ -522,8 +549,8 @@ class Service(BaseObject, CloudFormationModel):
"desiredCount": self.desired_count,
"id": f"ecs-svc/{mock_random.randint(0, 32**12)}",
"launchType": self.launch_type,
"pendingCount": self.desired_count,
"runningCount": 0,
"pendingCount": self.pending_count,
"runningCount": ecs_running_count,
"status": "PRIMARY",
"taskDefinition": self.task_definition,
"updatedAt": datetime.now(timezone.utc),
@ -531,16 +558,6 @@ class Service(BaseObject, CloudFormationModel):
]
else:
self.deployments = []
self.load_balancers = load_balancers if load_balancers is not None else []
self.scheduling_strategy = (
scheduling_strategy if scheduling_strategy is not None else "REPLICA"
)
self.platform_version = platform_version
self.tags = tags if tags is not None else []
self.pending_count = 0
self.region_name = cluster.region_name
self._account_id = backend.account_id
self._backend = backend
@property
def arn(self) -> str:
@ -1641,6 +1658,7 @@ class EC2ContainerServiceBackend(BaseBackend):
# It is only deleted later on
# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ecs.html#ECS.Client.delete_service
service.status = "INACTIVE"
service.pending_count = 0
return service
def register_container_instance(

View File

@ -2102,10 +2102,10 @@ class S3Response(BaseResponse):
ps = minidom.parseString(body).getElementsByTagName("Part")
prev = 0
for p in ps:
pn = int(p.getElementsByTagName("PartNumber")[0].firstChild.wholeText)
pn = int(p.getElementsByTagName("PartNumber")[0].firstChild.wholeText) # type: ignore[union-attr]
if pn <= prev:
raise InvalidPartOrder()
yield (pn, p.getElementsByTagName("ETag")[0].firstChild.wholeText)
yield (pn, p.getElementsByTagName("ETag")[0].firstChild.wholeText) # type: ignore[union-attr]
def _key_response_post(
self,

View File

@ -589,7 +589,7 @@ def test_create_service():
response["service"]["desiredCount"].should.equal(2)
len(response["service"]["events"]).should.equal(0)
len(response["service"]["loadBalancers"]).should.equal(0)
response["service"]["pendingCount"].should.equal(0)
response["service"]["pendingCount"].should.equal(2)
response["service"]["runningCount"].should.equal(0)
response["service"]["serviceArn"].should.equal(
f"arn:aws:ecs:us-east-1:{ACCOUNT_ID}:service/test_ecs_cluster/test_ecs_service"
@ -604,6 +604,77 @@ def test_create_service():
response["service"]["platformVersion"].should.equal("2")
@mock_ecs
@mock_ec2
def test_create_running_service():
if settings.TEST_SERVER_MODE:
raise SkipTest(
"Can't set environment variables in server mode for a single test"
)
running_service_count = 3
with mock.patch.dict(
os.environ, {"MOTO_ECS_SERVICE_RUNNING": str(running_service_count)}
):
client = boto3.client("ecs", region_name="us-east-1")
ec2 = boto3.resource("ec2", region_name="us-east-1")
setup_ecs(client, ec2)
response = client.create_service(
cluster="test_ecs_cluster",
serviceName="test_ecs_service",
taskDefinition="test_ecs_task",
desiredCount=4,
platformVersion="2",
)
assert response["service"]["runningCount"] == running_service_count
assert response["service"]["pendingCount"] == 1
@mock_ecs
@mock_ec2
def test_create_running_service_bad_env_var():
running_service_count = "ALSDHLHA;''"
with mock.patch.dict(
os.environ, {"MOTO_ECS_SERVICE_RUNNING": str(running_service_count)}
):
client = boto3.client("ecs", region_name="us-east-1")
ec2 = boto3.resource("ec2", region_name="us-east-1")
setup_ecs(client, ec2)
response = client.create_service(
cluster="test_ecs_cluster",
serviceName="test_ecs_service",
taskDefinition="test_ecs_task",
desiredCount=2,
platformVersion="2",
)
assert response["service"]["runningCount"] == 0
@mock_ecs
@mock_ec2
def test_create_running_service_negative_env_var():
running_service_count = "-20"
with mock.patch.dict(
os.environ, {"MOTO_ECS_SERVICE_RUNNING": str(running_service_count)}
):
client = boto3.client("ecs", region_name="us-east-1")
ec2 = boto3.resource("ec2", region_name="us-east-1")
setup_ecs(client, ec2)
response = client.create_service(
cluster="test_ecs_cluster",
serviceName="test_ecs_service",
taskDefinition="test_ecs_task",
desiredCount=2,
platformVersion="2",
)
assert response["service"]["runningCount"] == 0
@mock_ecs
def test_create_service_errors():
# given
@ -680,7 +751,7 @@ def test_create_service_scheduling_strategy():
response["service"]["desiredCount"].should.equal(2)
len(response["service"]["events"]).should.equal(0)
len(response["service"]["loadBalancers"]).should.equal(0)
response["service"]["pendingCount"].should.equal(0)
response["service"]["pendingCount"].should.equal(2)
response["service"]["runningCount"].should.equal(0)
response["service"]["serviceArn"].should.equal(
f"arn:aws:ecs:us-east-1:{ACCOUNT_ID}:service/test_ecs_cluster/test_ecs_service"
@ -870,7 +941,9 @@ def test_describe_services():
@mock.patch.dict(os.environ, {"MOTO_ECS_NEW_ARN": "TrUe"})
def test_describe_services_new_arn():
if settings.TEST_SERVER_MODE:
raise SkipTest("Cant set environment variables in server mode")
raise SkipTest(
"Can't set environment variables in server mode for a single test"
)
client = boto3.client("ecs", region_name="us-east-1")
_ = client.create_cluster(clusterName="test_ecs_cluster")
_ = client.register_task_definition(
@ -1338,7 +1411,9 @@ def test_register_container_instance():
@mock.patch.dict(os.environ, {"MOTO_ECS_NEW_ARN": "TrUe"})
def test_register_container_instance_new_arn_format():
if settings.TEST_SERVER_MODE:
raise SkipTest("Cant set environment variables in server mode")
raise SkipTest(
"Can't set environment variables in server mode for a single test"
)
ecs_client = boto3.client("ecs", region_name="us-east-1")
ec2 = boto3.resource("ec2", region_name="us-east-1")
@ -1876,7 +1951,9 @@ def test_run_task_default_cluster():
@mock.patch.dict(os.environ, {"MOTO_ECS_NEW_ARN": "TrUe"})
def test_run_task_default_cluster_new_arn_format():
if settings.TEST_SERVER_MODE:
raise SkipTest("Cant set environment variables in server mode")
raise SkipTest(
"Can't set environment variables in server mode for a single test"
)
client = boto3.client("ecs", region_name="us-east-1")
ec2 = boto3.resource("ec2", region_name="us-east-1")
@ -2953,7 +3030,7 @@ def test_create_service_load_balancing():
"test_container_name"
)
response["service"]["loadBalancers"][0]["containerPort"].should.equal(123)
response["service"]["pendingCount"].should.equal(0)
response["service"]["pendingCount"].should.equal(2)
response["service"]["runningCount"].should.equal(0)
response["service"]["serviceArn"].should.equal(
f"arn:aws:ecs:us-east-1:{ACCOUNT_ID}:service/test_ecs_cluster/test_ecs_service"