ECS: allow ecs service to transition to arbitrary running task count (#6289)
This commit is contained in:
		
							parent
							
								
									6b2ee153e6
								
							
						
					
					
						commit
						60065b3cf8
					
				| @ -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( | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -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" | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user