forked from awslabs/amazon-documentdb-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfailover_and_convert_lambda_function.py
More file actions
166 lines (131 loc) · 7.67 KB
/
Copy pathfailover_and_convert_lambda_function.py
File metadata and controls
166 lines (131 loc) · 7.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import json
import os
import time
import boto3
from botocore.exceptions import ClientError
from failover_and_convert_to_global import get_global_cluster_members, prepare_to_convert
"""
This function is dependent on failoverToSecondary and convertRegionalClusterToGlobal lambda functions.
The ARN for these lambda functions will be retrieved from the environment variables FAILOVER_FUNCTION and CONVERT_TO_GLOBAL_FUNCTION.
The Cloud Formation template will set the environment variables.
"""
# Define the client to interact with AWS Lambda
session = boto3.Session()
client = session.client('lambda')
dynamodb = session.resource('dynamodb')
def get_current_state(global_cluster_id, target_cluster_arn):
try:
dynamodb_table_name = os.environ['DYNAMODB_TABLE_NAME']
table = dynamodb.Table(dynamodb_table_name)
response = table.get_item(
Key={'global_cluster_id': global_cluster_id, 'target_cluster_arn': target_cluster_arn})
if "Item" in response:
current_state = response['Item']['current_state']
else:
current_state = ""
except ClientError as e:
print('ERROR OCCURRED WHILE PROCESSING: ', e)
print('PROCESSING WILL STOP')
raise RuntimeError
return current_state
def lambda_handler(event, context):
try:
# For BCP scenario, we always delete the old global cluster and create a new one with new primary as
# indicated by the input Before initiating failover, ensure that the function is able to create requests for
# converting back to global
event['is_delete_global_cluster'] = True
print('Started process to failover and convert standalone cluster to global cluster')
# Before initiating failover, ensure that the function is able to create requests for converting back to global
validate_input(event)
print('Getting global cluster members for global cluster ', event['global_cluster_id'])
global_cluster_members = get_global_cluster_members(global_cluster_id=event['global_cluster_id'])
print('Begin process to create request to convert regional cluster to global cluster ')
convert_to_global_request = prepare_to_convert(global_cluster_members,
global_cluster_id=event['global_cluster_id'],
secondary_cluster_arn=event['secondary_cluster_arn'],
io_optimized_storage=event['io_optimized_storage'],
enable_performance_insights=event['enable_performance_insights'])
print('Created request to convert back to global cluster.')
print('Starting process to failover')
failover_function = os.environ['FAILOVER_FUNCTION']
response_from_lambda1 = client.invoke(
FunctionName=failover_function,
InvocationType='RequestResponse',
Payload=json.dumps(event)
)
response_from_failover_to_secondary = json.load(response_from_lambda1['Payload'])
print('Failover process completed with response ', response_from_failover_to_secondary)
if response_from_failover_to_secondary['statusCode'] == 200:
"""
Check status in DynamoDB table to verify if the failover process completed. Lambda can be run multiple times
and duplicate requests process faster than original. The below check is to ensure that the convert to
regional cluster is called only after failover is completed. The initial value for current state is set in
DynamoDB as FAILOVER_PROCESS_STARTED
"""
failover_process_current_state = "FAILOVER_PROCESS_STARTED"
while failover_process_current_state != "FAILOVER_PROCESS_COMPLETED":
print('Waiting for failover process to complete...')
failover_process_current_state = get_current_state(global_cluster_id=event['global_cluster_id'],
target_cluster_arn=event['secondary_cluster_arn']
)
if failover_process_current_state == 'FAILOVER_PROCESS_ERRORED':
print('ERROR OCCURRED during Failover process.')
raise RuntimeError
time.sleep(5)
print('Starting process to convert to global cluster')
convert_to_global_function = os.environ['CONVERT_TO_GLOBAL_FUNCTION']
response_from_lambda2 = client.invoke(
FunctionName=convert_to_global_function,
InvocationType='RequestResponse',
Payload=json.dumps(convert_to_global_request)
)
response_from_convert_to_global = json.load(response_from_lambda2['Payload'])
print('Convert to global cluster process completed with response ', response_from_convert_to_global)
if response_from_convert_to_global['statusCode'] == 200:
while failover_process_current_state != "CONVERT_TO_GLOBAL_PROCESS_COMPLETED":
print('Waiting for convert to global process to complete...')
failover_process_current_state = get_current_state(global_cluster_id=event['global_cluster_id'],
target_cluster_arn=event[
'secondary_cluster_arn'])
if failover_process_current_state == 'CONVERT_TO_GLOBAL_PROCESS_ERRORED':
print('ERROR OCCURRED during Convert to Global process.')
raise RuntimeError
time.sleep(2)
print("SUCCESS: Completed failover and conversion to global cluster ")
else:
print('ERROR OCCURRED. Response from convert_to_global lambda function is ',
response_from_convert_to_global)
raise RuntimeError
else:
print('ERROR OCCURRED. Response from failover_to_secondary lambda function is ',
response_from_failover_to_secondary)
raise RuntimeError
except RuntimeError as e:
print('ERROR OCCURRED WHILE PROCESSING: ', e)
print('PROCESSING WILL STOP')
raise RuntimeError
return {
'statusCode': 200,
'body': json.dumps('Successfully failover and converted to global cluster')
}
def validate_input(event):
try:
if not event['secondary_cluster_arn']:
print('Secondary cluster ARN', event['secondary_cluster_arn'],
'is invalid. Please provide a valid secondary cluster ARN ')
raise RuntimeError
if not event['global_cluster_id']:
print('Global Cluster Identifier ', event['global_cluster_id'], 'is invalid. Please provide a valid global '
'cluster id')
raise RuntimeError
if not event['hosted_zone_id']:
print('Hosted zone id ', event['hosted_zone_id'], 'is invalid. Please provide a valid hosted zone id')
raise RuntimeError
if not event['primary_cluster_cname']:
print('Primary Cluster CNAME ', event['primary_cluster_cname'], 'is invalid. Please provide a valid CNAME '
'to manage endpoint')
raise RuntimeError
except KeyError as e:
print('ERROR OCCURRED WHILE PROCESSING: ', e)
print('PROCESSING WILL STOP')
raise KeyError