99
1010from typing import List
1111import json
12+ from datetime import datetime
13+ import click
14+
15+
1216
1317def parse_arn (arn ):
1418 elements = arn .split (':' )
@@ -87,3 +91,80 @@ def list_ecs_service_endpoints(self):
8791
8892 return self ._ecs_service_endpoints
8993
94+ class EcsScaler :
95+ def __init__ (self , environment ) -> None :
96+ self .environment = environment
97+
98+ @property
99+ def ecs_client (self ):
100+ if not hasattr (self , "_ecs_client" ):
101+ self ._ecs_client = boto3 .client ("ecs" )
102+ return self ._ecs_client
103+
104+ def get_ecs_clusters (self ):
105+ clusters = self .ecs_client .describe_clusters (
106+ clusters = self .ecs_client .list_clusters ()["clusterArns" ],
107+ include = ["TAGS" ]
108+ )
109+
110+ return [cluster for cluster in clusters ["clusters" ] if {"key" : "Environment" , "value" : self .environment } in cluster ["tags" ]]
111+
112+ def get_services (self ):
113+ services = {}
114+ for cluster in self .get_ecs_clusters ():
115+ if cluster ["clusterArn" ] not in services :
116+ services [cluster ["clusterArn" ]] = {}
117+ paginator = self .ecs_client .get_paginator ('list_services' )
118+ response_iterator = paginator .paginate (
119+ cluster = cluster ["clusterArn" ],
120+ )
121+ for page in response_iterator :
122+ for service in page ["serviceArns" ]:
123+ service_data = self .ecs_client .describe_services (
124+ services = page ["serviceArns" ],
125+ cluster = cluster ["clusterArn" ],
126+ include = ["TAGS" ]
127+ )
128+ for service in service_data ["services" ]:
129+ services [cluster ["clusterArn" ]][service ["serviceArn" ]] = service
130+
131+ return services
132+
133+ def scale_down (self ):
134+ services = self .get_services ()
135+ for cluster_arn , services in services .items ():
136+ for service_arn , service in services .items ():
137+ desired_count = str (service ["desiredCount" ])
138+ if int (desired_count ) > 0 :
139+ self .ecs_client .tag_resource (resourceArn = service_arn , tags = [
140+ {
141+ "key" : "downscaler_desired_count" ,
142+ "value" : desired_count
143+ },
144+ {
145+ "key" : "downscaler_scaled_down_at" ,
146+ "value" : datetime .now ().strftime ("%Y-%m-%d %H:%M:%S" )
147+ }
148+ ])
149+ self .ecs_client .update_service (service = service_arn , cluster = cluster_arn , desiredCount = 0 )
150+ click .echo (f"Scaled down { service_arn } from { desired_count } " )
151+
152+ def scale_up (self ):
153+ services = self .get_services ()
154+ for cluster_arn , services in services .items ():
155+ for service_arn , service in services .items ():
156+ try :
157+ desired_count = self .serviceTagsDict (service )["downscaler_desired_count" ]
158+ self .ecs_client .tag_resource (resourceArn = service_arn , tags = [
159+ {
160+ "key" : "downscaler_scaled_up_at" ,
161+ "value" : datetime .now ().strftime ("%Y-%m-%d %H:%M:%S" )
162+ }
163+ ])
164+ self .ecs_client .update_service (service = service_arn , cluster = cluster_arn , desiredCount = int (desired_count ))
165+ click .echo (f"Scaled up { service_arn } to { desired_count } " )
166+ except KeyError :
167+ pass
168+
169+ def serviceTagsDict (self , service ):
170+ return {tag ["key" ]: tag ["value" ] for tag in service ["tags" ]}
0 commit comments