Этот код действует как система раннего предупреждения о сбоях ADFS, которая отлично работает при локальном запуске. Проблема в том, что когда я запускаю его в Lambda, он зацикливается без остановки.
Коротко:
Я #commentedOut часть SNS, которая обычно отправляет информацию по электронной почте, и я использую print (), чтобы вместо этого отправлять информацию в журналы CloudWatch, пока я не смогу отсортировать цикл. Журналы:
Если я запустил это локально, он выдаст чистый одиночный вывод. В Lambda функция будет повторяться, пока не истечет время ожидания. Это почти как каждый раз, когда списки обновляются, они передаются модулю notification (), и он запускается. Я могу ограничить время работы функции, но лучше исправлю код!
Ваше здоровье, такс
# This python/boto3/lambda script sends a request to an Office 365 landing page, parses return details to confirm a successful redirect to /
# the organisation ADFS homepage, authenticates homepge is correct, raises any errors, and sends a consolodated report to /
# an AWS SNS topic.
# Run once to produce pageserver and htmlchar values for global variables.
# Import required modules
import boto3
import urllib.request
from urllib.request import Request, urlopen
from datetime import datetime
import time
import re
import sys
# Global variables to be set
url = "https://outlook.com/CONTOSSO.com"
adfslink = "https://sts.CONTOSSO.com/adfs/ls/?client-request-id = "
# Input after first run
pageserver = "Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0"
htmlchar = 18600
# Input AWS SNS ARN
snsarn = 'arn:aws:sns:ap-southeast-2:XXXXXXXXXXXXX:Daily_Check_Notifications_CONTOSSO'
sns = boto3.client('sns')
def pagecheck():
# Present the request to the webpage as if coming from a user in a browser
user_agent = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64)'
values = {'name' : 'user'}
headers = { 'User-Agent' : user_agent }
data = urllib.parse.urlencode(values)
data = data.encode('ascii')
# "Null" the Message Detail and Error lists
msgdet_list = []
error_list = []
request = Request(url)
req = urllib.request.Request(url, data, headers)
response = urlopen(request)
with urllib.request.urlopen(request) as response:
# Get the URL. This gets the real URL.
acturl = response.geturl()
msgdet_list.append("\nThe Actual URL is:")
msgdet_list.append(str(acturl))
if adfslink not in acturl:
error_list.append(str("Redirect Fail"))
# Get the HTTP resonse code
httpcode = response.code
msgdet_list.append("\nThe HTTP code is: ")
msgdet_list.append(str(httpcode))
if httpcode//200 != 1:
error_list.append(str("No HTTP 2XX Code"))
# Get the Headers as a dictionary-like object
headers = response.info()
msgdet_list.append("\nThe Headers are:")
msgdet_list.append(str(headers))
if response.info() == "":
error_list.append(str("Header Error"))
# Get the date of request and compare to UTC (DD MMM YYYY HH MM)
date = response.info()['date']
msgdet_list.append("The Date is: ")
msgdet_list.append(str(date))
returndate = str(date.split( )[1:5])
returndate = re.sub(r'[^\w\s]','',returndate)
returndate = returndate[:-2]
currentdate = datetime.utcnow()
currentdate = currentdate.strftime("%d %b %Y %H%M")
if returndate != currentdate:
date_error = ("Date Error. Returned Date: ", returndate, "Expected Date: ", currentdate, "Times in UTC (DD MMM YYYY HH MM)")
date_error = str(date_error)
date_error = re.sub(r'[^\w\s]','',date_error)
error_list.append(str(date_error))
# Get the server
headerserver = response.info()['server']
msgdet_list.append("\nThe Server is: ")
msgdet_list.append(str(headerserver))
if pageserver not in headerserver:
error_list.append(str("Server Error"))
# Get all HTML data and confirm no major change to content size by character lenth (global var: htmlchar).
html = response.read()
htmllength = len(html)
msgdet_list.append("\nHTML Length is: ")
msgdet_list.append(str(htmllength))
msgdet_list.append("\nThe Full HTML is: ")
msgdet_list.append(str(html))
msgdet_list.append("\n")
if htmllength // htmlchar != 1:
error_list.append(str("Page HTML Error - incorrect # of characters"))
if adfslink not in str(acturl):
error_list.append(str("ADFS Link Error"))
error_list.append("\n")
error_count = len(error_list)
if error_count == 1:
error_list.insert(0, 'No Errors Found.')
elif error_count == 2:
error_list.insert(0, 'Error Found:')
else:
error_list.insert(0, 'Multiple Errors Found:')
# Pass completed results and data to the notification() module
notification(msgdet_list, error_list, error_count)
# Use AWS SNS to create a notification email with the additional data generated
def notification(msgdet_list, error_list, errors):
datacheck = str("\n".join(msgdet_list))
errorcheck = str("\n".join(error_list))
notificationbody = str(errorcheck + datacheck)
if errors >1:
result = 'FAILED!'
else:
result = 'passed.'
notificationheader = ('The daily ADFS check has been marked as ' + result + ' ' + str(errors) + ' ' + str(error_list))
if result != 'passed.':
# message = sns.publish(
# TopicArn = snsarn,
# Subject = notificationheader,
# Message = notificationbody
# )
# Output result to CloudWatch logstream
print('Response: ' + notificationheader)
else:
print('passed')
sys.exit()
# Trigger the Lambda handler
def lambda_handler(event, context):
aws_account_ids = [context.invoked_function_arn.split(":")[4]]
pagecheck()
return "Successful"
sys.exit()
Вот что меня беспокоит, циклов нет, но Lambda запускает это снова и снова. Настройки 128 МБ + в любых секундах, до тайм-аута все равно. Ссылка журнала выше показывает ошибки, я попытался удалить все вызовы sys.exit () и попытался добавить их везде, чтобы сломать его, без изменений. Как я уже сказал, отлично работает при локальном запуске, только Lambda зацикливает его.
Тайм-аут устанавливается независимо от памяти.
Другая идея заключается в том, что AWS Lambda автоматически повторяет попытку вашей функции после сбоя. docs.aws.amazon.com/lambda/latest/dg/retries-on-errors.html. Установите максимальное время ожидания лямбда для тестирования (300 секунд).
Спасибо, Джон, мне казалось, что Lambda воспринимает sys.exits как неудачные завершения и снова запускает функцию. Для тестирования у меня установлен тайм-аут 15 секунд, чего достаточно, чтобы он зацикливался 3 раза и срабатывал каждую минуту.
Хорошо - я думаю, что проблема в sys.exit (). Я не использую sys.exit () в своем лямбда-коде, но я использую при тестировании на своем рабочем столе, поэтому я упустил это из виду. Вы должны выполнить нормальный возврат и регистрировать ошибки только в том случае, если они произошли. Примечание: вам не нужно устанавливать такие жесткие тайм-ауты. Я всегда устанавливаю максимальный тайм-аут в разработке. Поскольку вы выполняете вызовы request (), вы не можете так точно контролировать время отклика сети. Просто используйте таймауты, чтобы остановить цикл while, который будет запускать вечные ошибки.
Джон, вы должны поставить ^ в качестве ответа :)





Журналы CloudWatch содержат следующее сообщение об ошибке:
Process exited before completing request
Это вызвано вызовом sys.exit() в вашем коде. Локально ваш интерпретатор Python просто завершит работу при обнаружении такого sys.exit().
AWS Lambda, с другой стороны, ожидает, что функция Python будет работать только с return, и обрабатывает sys.exit() как ошибку. Поскольку ваша функция наверняка получила вызывается асинхронно AWS Lambda пытается выполнить его дважды.
Чтобы решить вашу проблему, вы можете заменить вхождения sys.exit() на return или даже лучше, просто удалить вызовы sys.exit(), поскольку там уже будут неявные возвраты в тех местах, где вы используете sys.exit().
Эта проблема частично решена: согласно моим подозрениям + комментариям Джона выше, использование sys.exit () заставляло Lambda видеть в функции ошибку, и поэтому она пыталась повторить попытку. Удаление sys.exits сокращает вывод CloudWatch до 1 набора журналов для каждого вызова, однако при введении обмена сообщениями он по-прежнему дважды нажимает на SNS (2 сообщения электронной почты на функцию, если SNS вызывается). Re: время функции, оно в среднем составляет около 6-8 секунд, поэтому время функции 15 секунд не слишком строго контролируется, его достаточно, чтобы просмотреть журналы, созданные 3+ раза, что доказывает ошибку.
Учитывая, что обработчик был запущен выражением cron CloudWatch, я думаю, что повторная попытка была результатом непотоковых / синхронных вызовов? Честно говоря, учитывая, что я # прокомментировал статью в соцсети, я поднял это как второй вопрос: stackoverflow.com/questions/51705061/lambda-boto3-python-iss ue
В вашем коде нет циклического раздела. Какую ошибку вы получаете и каковы настройки лямбда-выражения?