Skip to content

Commit 244386c

Browse files
committed
Update docstrings, docs, and typing
1 parent fc49499 commit 244386c

File tree

6 files changed

+122
-36
lines changed

6 files changed

+122
-36
lines changed

.DS_Store

-8 KB
Binary file not shown.

cloudendure/api.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,12 @@ def login(self, username="", password=""):
9090
_xsrf_token (str): The XSRF token to be used for subsequent API requests.
9191
9292
"""
93-
endpoint: str = "login"
9493
_username: str = self.config.active_config["username"] or username
9594
_password: str = self.config.active_config["password"] or password
9695
_auth: Dict[str, str] = {"username": _username, "password": _password}
9796

9897
# Attempt to login to the CloudEndure API via a POST request.
99-
response: requests.Response = self.api_call(
100-
"login", "post", data=json.dumps(_auth)
101-
)
102-
# response: requests.Response = self.session.post(f'{self.api_endpoint}/{endpoint}', json=_auth)
98+
response: requests.Response = self.api_call("login", "post", data=json.dumps(_auth))
10399

104100
# Check whether or not the request was successful.
105101
if response.status_code not in [200, 307]:
@@ -117,8 +113,11 @@ def login(self, username="", password=""):
117113
)
118114
raise CloudEndureUnauthorized()
119115

120-
# print('response: ', response, response.cookies)
121-
_xsrf_token: str = str(response.cookies["XSRF-TOKEN"])
116+
# Grab the XSRF token received from the response, as stored in cookies.
117+
# _xsrf_token: str = str(response.cookies["XSRF-TOKEN"])
118+
_xsrf_token: str = str(response.cookies.get("XSRF-TOKEN", ""))
119+
if not _xsrf_token:
120+
raise CloudEndureException("Failed to fetch a token from CloudEndure!")
122121

123122
# Strip the XSRF token of wrapping double-quotes from the cookie.
124123
if _xsrf_token.startswith('"') and _xsrf_token.endswith('"'):

cloudendure/cloudendure.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
MIGRATION_WAVE: str = os.environ.get("CLOUDENDURE_MIGRATION_WAVE", "0")
3131
CLONE_STATUS: str = os.environ.get("CLOUDENDURE_CLONE_STATUS", "NOT_STARTED")
3232
MAX_LAG_TTL: int = int(os.environ.get("CLOUDENDURE_MAX_LAG_TTL", "90"))
33+
SHARE_IMAGE: str = os.environ.get("", "")
3334

3435

3536
class CloudEndure:
@@ -201,6 +202,7 @@ def update_blueprint(
201202
except Exception as e:
202203
print(f"Updating blueprint task failed! {e}")
203204
return False
205+
return True
204206

205207
def launch(self, project_id=global_project_id, launch_type="test", dry_run=False):
206208
"""Launch the test target instances."""

lambda/exceptions.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# -*- coding: utf-8 -*-
2+
"""Define the Lambda specific exceptions."""
3+
4+
5+
class LambdaException(Exception):
6+
"""Define the generic AWS Lambda exception."""
7+
8+
pass
9+
10+
11+
class InvalidPayload(LambdaException):
12+
"""Define the exception to be raised when an invalid payload is encountered."""
13+
14+
pass
15+
16+
17+
class ImproperlyConfigured(LambdaException):
18+
"""Define the exception to be raised if the environment is improperly configured or missing."""
19+
20+
pass

lambda/handler.py

Lines changed: 83 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,20 @@
44
Attributes:
55
LOG_LEVEL (str):
66
REGION_OVERRIDE (str): If provided, this value will override the default AWS region.
7-
logger (logging.Logger):
7+
logger (logging.Logger): The logger to be used throughout execution of the AWS Lambda.
88
99
"""
1010
import datetime
1111
import json
1212
import logging
1313
import os
14+
from typing import Any, Dict, List
1415

1516
import boto3
1617
from botocore.exceptions import ClientError
1718

19+
from .exceptions import ImproperlyConfigured, InvalidPayload
20+
1821
LOG_LEVEL = getattr(logging, os.environ.get("CLOUDENDURE_LOGLEVEL", "INFO"))
1922
REGION_OVERRIDE = os.environ.get("CLOUDENDURE_REGION_OVERRIDE", "")
2023

@@ -23,23 +26,51 @@
2326
logger.setLevel(LOG_LEVEL)
2427

2528

26-
def send_sqs_message(imageinfo):
27-
"""Sends a SQS message with the AMI information
28-
that was created from the migrated instance
29-
that passed testing post migration in CloudEndure
29+
def send_sqs_message(image_info: Dict) -> bool:
30+
"""Send a SQS message.
31+
32+
The message includes the AMI information that was created from the migrated
33+
instance that passed testing post migration in CloudEndure.
34+
35+
Raises:
36+
ClientError: The exception is raised in the event of a boto3 client error.
37+
ImproperlyConfigured: The exception is raised in the event of missing or invalid
38+
environment configuration settings.
39+
40+
Returns:
41+
bool: Whether or not the message has been sent successfully.
42+
3043
"""
44+
queue_url = os.environ.get("QueueURL", "")
45+
if not queue_url:
46+
raise ImproperlyConfigured("Missing environment variable: QueueURL")
47+
3148
try:
32-
message = json.dumps(imageinfo)
49+
message = json.dumps(image_info)
3350
sqsclient = boto3.client("sqs")
34-
sqsclient.send_message(QueueUrl=os.environ["QueueURL"], MessageBody=message)
35-
36-
except ClientError as err:
37-
logger.error(err.response)
51+
sqsclient.send_message(QueueUrl=queue_url, MessageBody=message)
52+
except ClientError as e:
53+
logger.error(e.response)
54+
return False
55+
except ImproperlyConfigured as e:
56+
logger.error(e)
57+
return False
58+
return True
3859

3960

40-
def create_ami(project_id, instance_id):
61+
def create_ami(project_id: str, instance_id: str) -> bool:
4162
"""Create an AMI from the specified instance.
4263
64+
Args:
65+
project_id (str): The ID associated with the Project.
66+
instance_id (str): The ID associated with the AWS instance.
67+
68+
Raises:
69+
ClientError: The exception is raised in the event of a boto3 client error.
70+
71+
Returns:
72+
bool: Whether or not the AMI has been created successfully.
73+
4374
"""
4475
try:
4576
_ec2_client = boto3.client("ec2")
@@ -53,7 +84,10 @@ def create_ami(project_id, instance_id):
5384
NoReboot=True,
5485
)
5586
logger.info("AMI Id: %s", ec2_image)
56-
_filters = [{"Name": "resource-id", "Values": [instance_id]}]
87+
_filters: List[Dict] = [{
88+
"Name": "resource-id",
89+
"Values": [instance_id],
90+
}]
5791

5892
# Tag the newly created AMI by getting the tags of the migrated instance to copy to the AMI.
5993
ec2_tags = _ec2_client.describe_tags(Filters=_filters)
@@ -69,26 +103,46 @@ def create_ami(project_id, instance_id):
69103

70104
except ClientError as err:
71105
logger.error(err.response)
106+
return False
107+
return True
72108

73109

74-
def lambda_handler(event, context):
75-
"""Lambda entry point"""
76-
logger.info(event)
110+
def lambda_handler(event: Dict[str, Any], context: Dict[str, Any]) -> bool:
111+
"""Define the AWS Lambda entry point and handler.
77112
78-
try:
79-
json_sns_message = json.loads(event["Records"][0]["Sns"]["Message"])
113+
Args:
114+
event (str): The event performed against Lambda.
115+
context (dict): The context of the request performed against Lambda.
80116
81-
if json_sns_message["Pass"] != "True":
82-
logger.error(
83-
"%s did not pass post migration testing! Not creating an AMI."
84-
% (json_sns_message["instanceId"])
85-
)
117+
Raises:
118+
ClientError: The exception is raised in the event of a boto3 client error.
119+
InvalidPayload: The exception is raised in the event of an invalid payload.
120+
121+
Returns:
122+
bool: Whether or not the lambda function has executed successfully.
123+
124+
"""
125+
logger.debug(event)
126+
127+
event_records = event.get("Records", [])
128+
if not event_records:
129+
return False
86130

131+
try:
132+
event_message = event_records[0].get("Sns", {}).get("Message", "")
133+
json_sns_message = json.loads(event_message)
134+
instance_id: str = json_sns_message.get("instanceId", "")
135+
project_id: str = json_sns_message.get("projectId", "")
136+
137+
if json_sns_message.get("Pass", "NA") != "True":
138+
raise InvalidPayload(f"{instance_id} did not pass post migration testing! Not creating an AMI.")
87139
else:
88-
logger.info(
89-
"%s passed post migration testing. Creating an AMI."
90-
% (json_sns_message["instanceId"])
91-
)
92-
create_ami("", json_sns_message["instanceId"])
93-
except ClientError as err:
94-
logger.error(err.response)
140+
logger.info("%s passed post migration testing. Creating an AMI." % (instance_id))
141+
create_ami(project_id, instance_id)
142+
except ClientError as e:
143+
logger.error(e.response)
144+
return False
145+
except InvalidPayload as e:
146+
logger.error(e)
147+
return False
148+
return True

pydocmd.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ generate:
1515
- cloudendure.models++
1616
- code/cloudendure/utils.md:
1717
- cloudendure.utils++
18+
- code/lambda/copy_ami.md:
19+
- lambda.copy_ami++
20+
- code/lambda/exceptions.md:
21+
- lambda.exceptions++
22+
- code/lambda/handler.md:
23+
- lambda.handler++
1824

1925
pages:
2026
- General:
@@ -28,6 +34,10 @@ pages:
2834
- Exceptions: code/cloudendure/exceptions.md
2935
- Models: code/cloudendure/models.md
3036
- Utilities: code/cloudendure/utils.md
37+
- AWS Lambda:
38+
- Handler: code/lambda/handler.md
39+
- Exceptions: code/lambda/exceptions.md
40+
- Copy AMI Utils: code/lambda/copy_ami.md
3141
- API Documentation:
3242
- MAIN: api/API_README.md << docs/API_README.md
3343
- AccountApi: api/AccountApi.md << docs/AccountApi.md
@@ -133,3 +143,4 @@ theme:
133143

134144
additional_search_paths:
135145
- cloudendure/
146+
- lambda/

0 commit comments

Comments
 (0)