1919from  ._errors  import  (
2020    EC2InstanceNotFoundError ,
2121    EC2InsufficientCapacityError ,
22-     EC2TooManyInstancesError ,
2322)
2423from  ._models  import  (
2524    AWSTagKey ,
2928    EC2Tags ,
3029    Resources ,
3130)
32- from  ._utils  import  compose_user_data , ec2_instance_data_from_aws_instance 
31+ from  ._utils  import  (
32+     check_max_number_of_instances_not_exceeded ,
33+     compose_user_data ,
34+     ec2_instance_data_from_aws_instance ,
35+ )
3336
3437_logger  =  logging .getLogger (__name__ )
3538
@@ -127,34 +130,29 @@ async def launch_instances(
127130
128131        Arguments: 
129132            instance_config -- The EC2 instance configuration 
130-             min_number_of_instances -- the minimal number of instances needed  (fails if this amount cannot be reached) 
133+             min_number_of_instances -- the minimal number of instances required  (fails if this amount cannot be reached) 
131134            number_of_instances -- the ideal number of instances needed (it it cannot be reached AWS will return a number >=min_number_of_instances) 
132- 
133-         Keyword Arguments: 
134-             max_total_number_of_instances -- The total maximum allowed number of instances for this given instance_config (default: {10}) 
135+             max_total_number_of_instances -- The total maximum allowed number of instances for this given instance_config 
135136
136137        Raises: 
137138            EC2TooManyInstancesError: 
138139
139140        Returns: 
140141            The created instance data infos 
141142        """ 
143+ 
142144        with  log_context (
143145            _logger ,
144146            logging .INFO ,
145147            msg = f"launch { number_of_instances }   AWS instance(s) { instance_config .type .name }   with { instance_config .tags = }  " ,
146148        ):
147149            # first check the max amount is not already reached 
148-             current_instances  =  await  self .get_instances (
149-                 key_names = [instance_config .key_name ], tags = instance_config .tags 
150+             await  check_max_number_of_instances_not_exceeded (
151+                 self ,
152+                 instance_config ,
153+                 required_number_instances = number_of_instances ,
154+                 max_total_number_of_instances = max_total_number_of_instances ,
150155            )
151-             if  (
152-                 len (current_instances ) +  number_of_instances 
153-                 >  max_total_number_of_instances 
154-             ):
155-                 raise  EC2TooManyInstancesError (
156-                     num_instances = max_total_number_of_instances 
157-                 )
158156
159157            resource_tags : list [TagTypeDef ] =  [
160158                {"Key" : tag_key , "Value" : tag_value }
@@ -215,9 +213,7 @@ async def launch_instances(
215213                        continue 
216214                    # For any other ClientError, re-raise to let the decorator handle it 
217215                    raise 
218-                 except  Exception :
219-                     # For any other error (not AWS-related), fail immediately 
220-                     raise 
216+ 
221217            else :
222218                # All subnets failed with capacity errors 
223219                _logger .error (
@@ -231,32 +227,25 @@ async def launch_instances(
231227                    instance_type = instance_config .type .name ,
232228                )
233229            instance_ids  =  [i ["InstanceId" ] for  i  in  instances ["Instances" ]]
234-             _logger .info (
235-                 "%s New instances launched: %s, waiting for them to start now..." ,
236-                 len (instance_ids ),
237-                 instance_ids ,
238-             )
239- 
240-             # wait for the instance to be in a pending state 
241-             # NOTE: reference to EC2 states https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-lifecycle.html 
242-             waiter  =  self .client .get_waiter ("instance_exists" )
243-             await  waiter .wait (InstanceIds = instance_ids )
244-             _logger .debug ("instances %s exists now." , instance_ids )
230+             with  log_context (
231+                 _logger ,
232+                 logging .INFO ,
233+                 msg = f"{ len (instance_ids )}   instances: { instance_ids = }   launched. Wait to reach pending state" ,
234+             ):
235+                 # wait for the instance to be in a pending state 
236+                 # NOTE: reference to EC2 states https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-lifecycle.html 
237+                 waiter  =  self .client .get_waiter ("instance_exists" )
238+                 await  waiter .wait (InstanceIds = instance_ids )
245239
246-             # NOTE: waiting for pending ensure  we get all the IPs back 
240+             # NOTE: waiting for pending ensures  we get all the IPs back 
247241            described_instances  =  await  self .client .describe_instances (
248242                InstanceIds = instance_ids 
249243            )
250244            assert  "Instances"  in  described_instances ["Reservations" ][0 ]  # nosec 
251-             instance_datas   =  [
245+             return  [
252246                await  ec2_instance_data_from_aws_instance (self , i )
253247                for  i  in  described_instances ["Reservations" ][0 ]["Instances" ]
254248            ]
255-             _logger .info (
256-                 "%s are pending now" ,
257-                 f"{ instance_ids = }  " ,
258-             )
259-             return  instance_datas 
260249
261250    @ec2_exception_handler (_logger ) 
262251    async  def  get_instances (
0 commit comments