Skip to content

Commit a8f88d6

Browse files
authored
✨Maintenance CLI: introduce option to terminate dynamic instances (#7630)
1 parent 885d028 commit a8f88d6

File tree

4 files changed

+211
-6
lines changed

4 files changed

+211
-6
lines changed

scripts/maintenance/computational-clusters/autoscaled_monitor/cli.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,5 +209,18 @@ def check_database_connection() -> None:
209209
asyncio.run(api.check_database_connection(state))
210210

211211

212+
@app.command()
213+
def terminate_dynamic_instances(
214+
user_id: Annotated[int | None, typer.Option(help="the user ID")] = None,
215+
instance_id: Annotated[str | None, typer.Option(help="the instance ID")] = None,
216+
*,
217+
force: Annotated[bool, typer.Option(help="will not ask for confirmation")] = False,
218+
) -> None:
219+
"""this will terminate the instance(s) used for the given user or instance ID."""
220+
asyncio.run(
221+
api.terminate_dynamic_instances(state, user_id, instance_id, force=force)
222+
)
223+
224+
212225
if __name__ == "__main__":
213226
app()

scripts/maintenance/computational-clusters/autoscaled_monitor/core.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,10 @@ async def summary(
491491
# get all the running instances
492492
assert state.ec2_resource_autoscaling
493493
dynamic_instances = await ec2.list_dynamic_instances_from_ec2(
494-
state, user_id, wallet_id
494+
state,
495+
filter_by_user_id=user_id,
496+
filter_by_wallet_id=wallet_id,
497+
filter_by_instance_id=None,
495498
)
496499
dynamic_autoscaled_instances = await _parse_dynamic_instances(
497500
state, dynamic_instances, state.ssh_key_path, user_id, wallet_id
@@ -776,3 +779,48 @@ async def trigger_cluster_termination(
776779

777780
async def check_database_connection(state: AppState) -> None:
778781
await db.check_db_connection(state)
782+
783+
784+
async def terminate_dynamic_instances(
785+
state: AppState,
786+
user_id: int | None,
787+
instance_id: str | None,
788+
*,
789+
force: bool,
790+
) -> None:
791+
if not user_id and not instance_id:
792+
rich.print("either define user_id or instance_id!")
793+
raise typer.Exit(2)
794+
dynamic_instances = await ec2.list_dynamic_instances_from_ec2(
795+
state,
796+
filter_by_user_id=None,
797+
filter_by_wallet_id=None,
798+
filter_by_instance_id=instance_id,
799+
)
800+
801+
dynamic_autoscaled_instances = await _parse_dynamic_instances(
802+
state, dynamic_instances, state.ssh_key_path, user_id, None
803+
)
804+
805+
if not dynamic_autoscaled_instances:
806+
rich.print("no instances found")
807+
raise typer.Exit(1)
808+
809+
_print_dynamic_instances(
810+
dynamic_autoscaled_instances,
811+
state.environment,
812+
state.ec2_resource_autoscaling.meta.client.meta.region_name,
813+
output=None,
814+
)
815+
816+
for instance in dynamic_autoscaled_instances:
817+
rich.print(
818+
f"terminating instance {instance.ec2_instance.instance_id} with name {utils.get_instance_name(instance.ec2_instance)}"
819+
)
820+
if force is True or typer.confirm(
821+
f"Are you sure you want to terminate instance {instance.ec2_instance.instance_id}?"
822+
):
823+
instance.ec2_instance.terminate()
824+
rich.print(f"terminated instance {instance.ec2_instance.instance_id}")
825+
else:
826+
rich.print("not terminating anything")

scripts/maintenance/computational-clusters/autoscaled_monitor/ec2.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def _list_running_ec2_instances(
1818
custom_tags: dict[str, str],
1919
user_id: int | None,
2020
wallet_id: int | None,
21+
instance_id: str | None,
2122
) -> ServiceResourceInstancesCollection:
2223
# get all the running instances
2324

@@ -37,7 +38,8 @@ def _list_running_ec2_instances(
3738
ec2_filters.append({"Name": "tag:user_id", "Values": [f"{user_id}"]})
3839
if wallet_id:
3940
ec2_filters.append({"Name": "tag:wallet_id", "Values": [f"{wallet_id}"]})
40-
41+
if instance_id:
42+
ec2_filters.append({"Name": "instance-id", "Values": [f"{instance_id}"]})
4143
return ec2_resource.instances.filter(Filters=ec2_filters)
4244

4345

@@ -66,13 +68,16 @@ async def list_computational_instances_from_ec2(
6668
custom_tags,
6769
user_id,
6870
wallet_id,
71+
None,
6972
)
7073

7174

7275
async def list_dynamic_instances_from_ec2(
7376
state: AppState,
74-
user_id: int | None,
75-
wallet_id: int | None,
77+
*,
78+
filter_by_user_id: int | None,
79+
filter_by_wallet_id: int | None,
80+
filter_by_instance_id: str | None,
7681
) -> ServiceResourceInstancesCollection:
7782
assert state.environment["EC2_INSTANCES_KEY_NAME"]
7883
custom_tags = {}
@@ -83,8 +88,9 @@ async def list_dynamic_instances_from_ec2(
8388
state.ec2_resource_autoscaling,
8489
state.environment["EC2_INSTANCES_KEY_NAME"],
8590
custom_tags,
86-
user_id,
87-
wallet_id,
91+
filter_by_user_id,
92+
filter_by_wallet_id,
93+
filter_by_instance_id,
8894
)
8995

9096

@@ -101,6 +107,7 @@ async def get_computational_bastion_instance(state: AppState) -> Instance:
101107
{},
102108
None,
103109
None,
110+
None,
104111
)
105112

106113
possible_bastions = list(
@@ -120,6 +127,7 @@ async def get_dynamic_bastion_instance(state: AppState) -> Instance:
120127
{},
121128
None,
122129
None,
130+
None,
123131
)
124132

125133
possible_bastions = list(

0 commit comments

Comments
 (0)