Skip to content

Commit 2f90263

Browse files
authored
Merge pull request #8 from aws-samples/v-updates
Deprecated Runtime Auto checks
2 parents b73266c + 6c2db7c commit 2f90263

File tree

8 files changed

+1141
-181
lines changed

8 files changed

+1141
-181
lines changed

README.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,24 @@ This tool has been created to help evaluate **AWS Serverless services** in bulk
77
## Currently supported features
88

99
- **AWS Lambda**
10-
- Risk Warnings:
11-
- Multi-AZ warnings evaluate if a function is VPC enabled and if the linked subnets follow multi-AZ approach.
12-
- Runtime warnings evaluate if a function runtime is older then latest or two latest runtime versions (only Zip type supported)
10+
- <span style="background-color:red;"><b>RED Danger Zone - Risks</b></span>:
11+
- Multi-AZ checks evaluate if a function is VPC enabled and if the linked subnets follow multi-AZ approach.
12+
- Runtime checks evaluate if a function runtime has been deprecated (ZIP type only). This check separates functions based on runtime and provides details on all affected function versions.
1313
- **AWS Trusted Advisor checks** (the following checks are included as part of the report if the AWS Account has [AWS Trusted Advisor](https://docs.aws.amazon.com/awssupport/latest/user/trusted-advisor.html) enabled):
1414
- Functions with high error rates
1515
- Functions with excessive timeouts
16+
17+
- <span style="background-color:yellow;"><b>YELLOW Warnings Zone</b></span>:
18+
- Runtime checks that evaluate if a function runtime versions is scheduled to be deprecated in the near future.
1619

17-
- Optimization Recommendations:
20+
- <span style="background-color:navy;color:white"><b>BLUE Optimization Zone - Recommendations</b></span>:
1821
- Provides optimization recommendations focused on function memory and benefits of optimizing (duration and cost).
1922
- This information is pulled from AWS Compute Optimizer so there has to be historical usage available.
2023
- Lists functions using x86 architecture type and recommends converting to arm64 (Graviton2).
2124

22-
- List of reviewed functions:
23-
- Provides the list of reviewed functions with their configuration as well as Event Source Mapping configuration (if present).
25+
- <span style="background-color:grey"><b>Reviewed Functions - Configurations</b></span>:
26+
- Provides the list of reviewed functions with their configurations.
27+
- Provides a list of function event source mappings configurations.
2428

2529
## Architecture
2630

VERSIONS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Serverless Operational Review Tool Versions
22

3+
- **Version 1.3.0**
4+
- Added evaluation of deprecated runtimes and runtimes that are scheduled to deprecate, including blocked update and creation dates.
5+
- Functions with deprecated runtime versions allocated to RED Danger Zone.
6+
- Functions with runtime versions scheduled to deprecate allocated to YELLOW Warning Zone.
7+
38
- **Version 1.2.1**
49
- Added runtimes : Python 12, Node.js 20, Java 21, OS-only Runtime Amazon Linux 2023
510
- Removed runtimes: Java 11, Node.js 16, .NET 6

src/lambda/function-generate-selected/app.py

Lines changed: 209 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
# SPDX-License-Identifier: MIT-0
33

44
import json, os, copy
5-
import boto3
5+
import boto3, requests
66
from jinja2 import Template
7+
from bs4 import BeautifulSoup
78

89
s3 = boto3.client('s3')
910
s3_r = boto3.resource('s3')
@@ -18,29 +19,52 @@
1819
ACT_RUNTIMES = os.environ['ACT_RUNTIMES']
1920
STACK_NAME = os.environ['STACK_NAME']
2021
TA_ENABLED = os.environ['TA_ENABLED']
22+
DOC_URL = "https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html"
2123

2224

2325
###################################################################################################################################
24-
#Deprecated runtime evaluation code functionality
26+
# Runtime evaluation code functionality
2527
###################################################################################################################################
26-
def check_deprecated_runtime(fns):
27-
run_check = fns
28-
act_runtimes = str.split(ACT_RUNTIMES, ',')
29-
30-
for fn in run_check:
31-
match = False
32-
if fn['PackageType'] == 'Image':
33-
fn['Runtime'] = 'Container'
34-
for run in act_runtimes:
35-
if run == fn['Runtime']:
36-
match = True
28+
def check_dep_runtime(fns,dep_runs):
29+
# Create a dictionary to map Identifiers to runtime details
30+
runtime_map = {item['Identifier']: {
31+
'Deprecation date': item['Deprecation date'],
32+
'Block function create': item['Block function create'],
33+
'Block function update': item['Block function update']
34+
} for item in dep_runs['Deprecated Runtimes']}
35+
36+
# Iterate through the function data and append the runtime details
37+
for function in fns:
38+
runtime = function['Runtime']
39+
for identifier, details in runtime_map.items():
40+
if runtime == identifier:
41+
function['Deprecation date'] = details['Deprecation date']
42+
function['Block function create'] = details['Block function create']
43+
function['Block function update'] = details['Block function update']
44+
break
45+
46+
return fns
47+
48+
def check_sup_runtime(fns,sup_runs):
49+
# Create a dictionary to map Identifiers to runtime details
50+
runtime_map = {item['Identifier']: {
51+
'Deprecation date': item['Deprecation date'],
52+
'Block function create': item['Block function create'],
53+
'Block function update': item['Block function update']
54+
} for item in sup_runs['Supported Runtimes']}
55+
56+
# Iterate through the function data and append the runtime details
57+
for function in fns:
58+
runtime = function['Runtime']
59+
for identifier, details in runtime_map.items():
60+
if runtime == identifier:
61+
function['Deprecation date'] = details['Deprecation date']
62+
function['Block function create'] = details['Block function create']
63+
function['Block function update'] = details['Block function update']
3764
break
38-
if match == False:
39-
fn['Message'] = 'Function is using older or deprecated runtime, consider updating.'
40-
else:
41-
fn['Message'] = 'Function runtime is one of latest.'
4265

43-
return run_check
66+
return fns
67+
4468

4569
###################################################################################################################################
4670

@@ -164,6 +188,68 @@ def check_multi_az(fns):
164188

165189
###################################################################################################################################
166190

191+
###################################################################################################################################
192+
#Deprecated runtime fetch:
193+
###################################################################################################################################
194+
def fetch_deprecated_runtimes(dep_run_soup):
195+
"""
196+
Fetch the list of deprecated runtimes from the AWS Lambda docs
197+
"""
198+
# Find the Deprecated Runtime table
199+
element = dep_run_soup.find(id="runtimes-deprecated")
200+
table = element.find_next("table")
201+
202+
# Extract header row
203+
headers = [header.text for header in table.find('thead').find_all('tr')]
204+
headers = headers[1]
205+
headers = headers.split('\n')
206+
headers = headers[1:-1]
207+
208+
rows = []
209+
210+
for row in table.find_all('tr')[1:]:
211+
cells = row.find_all('td')
212+
data = [cell.text for cell in cells]
213+
data = [line.strip() for line in data]
214+
rows.append(dict(zip(headers, data)))
215+
rows = rows[1:]
216+
217+
218+
return rows
219+
###################################################################################################################################
220+
221+
###################################################################################################################################
222+
#Supported runtime fetch:
223+
###################################################################################################################################
224+
225+
def fetch_supported_runtimes(sup_run_soup):
226+
"""
227+
Fetch the list of supported runtimes from the AWS Lambda docs
228+
"""
229+
# Find the Supported Runtime table
230+
element = sup_run_soup.find(id="runtimes-supported")
231+
table = element.find_next("table")
232+
233+
# Extract header row
234+
headers = [header.text for header in table.find('thead').find_all('tr')]
235+
headers = headers[1]
236+
headers = headers.split('\n')
237+
headers = headers[1:-1]
238+
239+
rows = []
240+
241+
for row in table.find_all('tr')[1:]:
242+
cells = row.find_all('td')
243+
data = [cell.text for cell in cells]
244+
data = [line.strip() for line in data]
245+
rows.append(dict(zip(headers, data)))
246+
rows = rows[1:]
247+
248+
249+
return rows
250+
251+
###################################################################################################################################
252+
167253
def handler(event, context):
168254
#List function config objects from S3:
169255
s3_prefix = event['Prefix']
@@ -195,33 +281,110 @@ def handler(event, context):
195281

196282

197283
#################################################################################
198-
#Deprecated runtime evaluation method pointer:
199-
#################################################################################
200-
run_fns = copy.deepcopy(functions)
201-
warnings_dep_runtime = check_deprecated_runtime(run_fns)
284+
#Runtime evaluation method pointer:
285+
#################################################################################
286+
287+
html = requests.get(DOC_URL)
288+
html = html.text
289+
290+
soup = BeautifulSoup(html, 'html.parser')
291+
292+
supported_runtimes = {'Supported Runtimes': fetch_supported_runtimes(soup)}
293+
294+
deprecated_runtimes = {'Deprecated Runtimes': fetch_deprecated_runtimes(soup)}
295+
296+
dep_fns = copy.deepcopy(functions)
297+
dep_runtime_fns = check_dep_runtime(dep_fns,deprecated_runtimes)
298+
sup_fns = copy.deepcopy(functions)
299+
sup_runtime_fns = check_sup_runtime(sup_fns,supported_runtimes)
300+
301+
# Separate deprecated functions by runtime:
202302
py_dep_run = []
303+
for function in dep_runtime_fns:
304+
runtime = function['Runtime']
305+
if runtime.startswith('python') and 'Deprecation date' in function and function['Deprecation date'] != '':
306+
py_dep_run.append(function)
307+
203308
no_dep_run = []
309+
for function in dep_runtime_fns:
310+
runtime = function['Runtime']
311+
if runtime.startswith('nodejs') and 'Deprecation date' in function and function['Deprecation date'] != '':
312+
no_dep_run.append(function)
313+
204314
ja_dep_run = []
315+
for function in dep_runtime_fns:
316+
runtime = function['Runtime']
317+
if runtime.startswith('java') and 'Deprecation date' in function and function['Deprecation date'] != '':
318+
ja_dep_run.append(function)
319+
205320
do_dep_run = []
321+
for function in dep_runtime_fns:
322+
runtime = function['Runtime']
323+
if runtime.startswith('dotnet') and 'Deprecation date' in function and function['Deprecation date'] != '':
324+
do_dep_run.append(function)
325+
206326
ru_dep_run = []
327+
for function in dep_runtime_fns:
328+
runtime = function['Runtime']
329+
if runtime.startswith('ruby') and 'Deprecation date' in function and function['Deprecation date'] != '':
330+
ru_dep_run.append(function)
331+
207332
go_dep_run = []
333+
for function in dep_runtime_fns:
334+
runtime = function['Runtime']
335+
if runtime.startswith('golang') and 'Deprecation date' in function and function['Deprecation date'] != '':
336+
go_dep_run.append(function)
337+
208338
cu_dep_run = []
209-
for run_warning in warnings_dep_runtime:
210-
if run_warning['Message'] != 'Function runtime is one of latest.':
211-
if run_warning['Runtime'].startswith('python'):
212-
py_dep_run.append(run_warning)
213-
elif run_warning['Runtime'].startswith('nodejs'):
214-
no_dep_run.append(run_warning)
215-
elif run_warning['Runtime'].startswith('java'):
216-
ja_dep_run.append(run_warning)
217-
elif run_warning['Runtime'].startswith('dotnet'):
218-
do_dep_run.append(run_warning)
219-
elif run_warning['Runtime'].startswith('ruby'):
220-
ru_dep_run.append(run_warning)
221-
elif run_warning['Runtime'].startswith('golang'):
222-
go_dep_run.append(run_warning)
223-
elif run_warning['Runtime'].startswith('provided'):
224-
cu_dep_run.append(run_warning)
339+
for function in dep_runtime_fns:
340+
runtime = function['Runtime']
341+
if runtime.startswith('provided') and 'Deprecation date' in function and function['Deprecation date'] != '':
342+
cu_dep_run.append(function)
343+
344+
345+
# Separate supported functions by runtime:
346+
py_sup_run = []
347+
for function in sup_runtime_fns:
348+
runtime = function['Runtime']
349+
if runtime.startswith('python') and 'Deprecation date' in function and function['Deprecation date'] != '':
350+
py_sup_run.append(function)
351+
352+
no_sup_run = []
353+
for function in sup_runtime_fns:
354+
runtime = function['Runtime']
355+
if runtime.startswith('nodejs') and 'Deprecation date' in function and function['Deprecation date'] != '':
356+
no_sup_run.append(function)
357+
358+
ja_sup_run = []
359+
for function in sup_runtime_fns:
360+
runtime = function['Runtime']
361+
if runtime.startswith('java') and 'Deprecation date' in function and function['Deprecation date'] != '':
362+
ja_sup_run.append(function)
363+
364+
do_sup_run = []
365+
for function in sup_runtime_fns:
366+
runtime = function['Runtime']
367+
if runtime.startswith('dotnet') and 'Deprecation date' in function and function['Deprecation date'] != '':
368+
do_sup_run.append(function)
369+
370+
ru_sup_run = []
371+
for function in sup_runtime_fns:
372+
runtime = function['Runtime']
373+
if runtime.startswith('ruby') and 'Deprecation date' in function and function['Deprecation date'] != '':
374+
ru_sup_run.append(function)
375+
376+
go_sup_run = []
377+
for function in sup_runtime_fns:
378+
runtime = function['Runtime']
379+
if runtime.startswith('golang') and 'Deprecation date' in function and function['Deprecation date'] != '':
380+
go_sup_run.append(function)
381+
382+
cu_sup_run = []
383+
for function in sup_runtime_fns:
384+
runtime = function['Runtime']
385+
if runtime.startswith('provided') and 'Deprecation date' in function and function['Deprecation date'] != '':
386+
cu_sup_run.append(function)
387+
225388

226389
#################################################################################
227390
#Trusted Advisor evaluations
@@ -292,16 +455,11 @@ def handler(event, context):
292455
if fn['FunctionArn'] == esm['FunctionArn']:
293456
filtered_esms.append(esm)
294457

295-
print(filtered_esms)
296-
297-
298-
299-
300458

301459
#################################################################################
302460

303461
#################################################################################
304-
#Create a dict wilh all data that will populate the template
462+
#Create a dict with all data that will populate the template
305463
#################################################################################
306464
data = {}
307465
data['account'] = AWS_ACCOUNT
@@ -314,6 +472,13 @@ def handler(event, context):
314472
data['dep_ruby_functions'] = ru_dep_run
315473
data['dep_golang_functions'] = go_dep_run
316474
data['dep_custom_functions'] = cu_dep_run
475+
data['sup_python_functions'] = py_sup_run
476+
data['sup_nodejs_functions'] = no_sup_run
477+
data['sup_java_functions'] = ja_sup_run
478+
data['sup_dotnet_functions'] = do_sup_run
479+
data['sup_ruby_functions'] = ru_sup_run
480+
data['sup_golang_functions'] = go_sup_run
481+
data['sup_custom_functions'] = cu_sup_run
317482
if TA_ENABLED == 'true':
318483
data['warnings_ta_high_errors'] = warnings_ta_high_errors
319484
data['warnings_ta_excessive_timeouts'] = warnings_ta_excessive_timeout
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
jinja2
1+
jinja2
2+
requests
3+
bs4

0 commit comments

Comments
 (0)