Skip to content

Commit 6b99aeb

Browse files
authored
Merge pull request #201 from SumoLogic/cf-telemetry-awso
Telemetry lambda code
2 parents 6a97ef0 + c6f1dd4 commit 6b99aeb

File tree

3 files changed

+198
-0
lines changed

3 files changed

+198
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
# Pull the Amazon Linux image from Docker Hub
4+
docker pull amazonlinux
5+
6+
# Run the Amazon Linux container in detached mode
7+
docker run -d --name telemetry amazonlinux tail -f /dev/null
8+
9+
# Install Python, pip, and other dependencies inside the container
10+
docker exec -it telemetry /bin/bash -c "yum update -y && yum install -y python3-pip zip && python3 -m pip install virtualenv"
11+
12+
# Create a virtual environment and install dependencies
13+
docker exec -it telemetry /bin/bash -c "python3 -m venv temp-venv && source temp-venv/bin/activate && mkdir telemetry && cd telemetry && pip install crhelper sumologic-appclient-sdk future_fstrings setuptools -t ."
14+
15+
# Copy python file from host to container
16+
docker cp ./lambda_function.py telemetry:/telemetry
17+
18+
# Zip the contents of the telemetry directory
19+
docker exec -it telemetry /bin/bash -c "cd telemetry && ls -l && zip -r ../telemetry.zip ."
20+
21+
# Copy the telemetry.zip file from the container to the host
22+
docker cp telemetry:/telemetry.zip ./telemetry.zip
23+
24+
# Stop and remove the container
25+
docker stop telemetry
26+
docker rm telemetry
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
from collections import defaultdict
2+
import boto3
3+
import time
4+
from crhelper import CfnResource
5+
from sumoappclient.sumoclient.outputhandlers import HTTPHandler
6+
from sumoappclient.common.utils import read_yaml_file
7+
from abc import ABC, abstractmethod
8+
9+
helper = CfnResource(json_logging=False, log_level='INFO', sleep_on_delete=30)
10+
11+
@helper.create
12+
def create(event, context):
13+
try:
14+
T = telemetryFactory(event, context)
15+
T.fetch_and_send_telemetry()
16+
except Exception as e:
17+
print(e)
18+
return "Telemetry failed to sent for Create Stack"
19+
helper.Status = "SUCCESS"
20+
return "Telemetry sent for Create Stack"
21+
22+
23+
@helper.update
24+
def update(event, context):
25+
try:
26+
T = telemetryFactory(event, context)
27+
T.fetch_and_send_telemetry()
28+
except Exception as e:
29+
print(e)
30+
return "Telemetry failed to sent for Update Stack"
31+
helper.Status = "SUCCESS"
32+
return "Telemetry sent for Update Stack"
33+
34+
35+
@helper.delete
36+
def delete(event, context):
37+
lambda_client = boto3.client('lambda')
38+
try:
39+
T = telemetryFactory(event, context)
40+
T.fetch_and_send_telemetry()
41+
# Self Delete the Telemetry Lambda function
42+
if event['RequestType']=='Delete':
43+
response = lambda_client.delete_function(FunctionName=context.invoked_function_arn)
44+
except Exception as e:
45+
print(e)
46+
helper.Status = "SUCCESS"
47+
48+
49+
def lambda_handler(event, context):
50+
helper(event, context)
51+
52+
def telemetryFactory(event, context):
53+
# create an obj of default class and return in case of none
54+
if event['ResourceProperties']['solutionName'] == 'AWSO':
55+
return awsoTelemetry(event, context)
56+
else:
57+
return parentStackTelemetry(event, context)
58+
# elif event['ResourceProperties']['solutionName'] == 'CIS':
59+
# return cisTelemetry(event, context)
60+
61+
# Interface
62+
class baseTelemetry(ABC):
63+
64+
def __init__(self, event,context,*args, **kwargs):
65+
self.event=event
66+
self.context=context
67+
self.config = read_yaml_file("./metadata.yaml")
68+
self.config['SumoLogic']['SUMO_ENDPOINT'] = self.event['ResourceProperties']['TelemetryEndpoint']
69+
self.sumoHttpHandler = HTTPHandler(self.config)
70+
self.log = self.sumoHttpHandler.log
71+
self.log.debug("Telemetry enabled")
72+
73+
@abstractmethod
74+
def fetch_and_send_telemetry(self):
75+
raise NotImplementedError
76+
77+
def send_telemetry(self, data):
78+
r = self.sumoHttpHandler.send(data)
79+
80+
# class cisTelemetry(baseTelemetry): # parentStackSetTelemetry
81+
# def create_telemetry_data(self):
82+
# pass
83+
84+
class parentStackTelemetry(baseTelemetry):
85+
def __init__(self, event,context,*args, **kwargs):
86+
super().__init__(event,context)
87+
self.stackID = event['ResourceProperties']['stackID']
88+
self.cfclient = boto3.client('cloudformation')
89+
self.all_resource_statuses=defaultdict(list)
90+
91+
def enrich_telemetry_data(self, log_data_list):
92+
return log_data_list
93+
94+
# This function will return True if any of the child resources are *IN_PROGRESS state.
95+
def _has_any_child_resources_in_progress_state(self):
96+
all_stacks = self.cfclient.describe_stack_resources(StackName=self.stackID)
97+
# PrimeInvoke - only responsible for triggering lambda
98+
# Removing 'Primerinvoke' status from all_stacks status so that it is not considered during status checking else it'll result in endless loop becoz if PriveInvoke is not completed overall stack can't be completed.
99+
for stack_resource in filter(lambda x: x["LogicalResourceId"] != self.event['LogicalResourceId'] ,all_stacks["StackResources"]):
100+
stackStatus = stack_resource["ResourceStatus"]
101+
if stackStatus.endswith('_IN_PROGRESS'):
102+
return True
103+
return False # None of the child resources are in IN_PROGRESS state
104+
105+
def _create_telemetry_data(self):
106+
log_data_list=[]
107+
all_stacks_events = self.cfclient.describe_stack_events(StackName= self.stackID)
108+
for stack_resource in all_stacks_events["StackEvents"]:
109+
resourceID = stack_resource["PhysicalResourceId"]
110+
status = stack_resource["ResourceStatus"]
111+
resource_status_reason = stack_resource.get('ResourceStatusReason', '')
112+
if status not in self.all_resource_statuses.get(resourceID, []):
113+
self.all_resource_statuses[resourceID].append(status)
114+
log_data = {
115+
'requestid': self.context.aws_request_id,
116+
'timestamp': stack_resource['Timestamp'].isoformat(timespec='milliseconds'),
117+
'data': {
118+
'stackId': self.event['StackId'],
119+
'resourceType': stack_resource["ResourceType"],
120+
'resourceName': stack_resource["LogicalResourceId"],
121+
'resourceID': stack_resource["PhysicalResourceId"],
122+
'status': stack_resource["ResourceStatus"],
123+
'details': resource_status_reason
124+
}
125+
}
126+
log_data_list.append(log_data)
127+
return log_data_list
128+
129+
def fetch_and_send_telemetry(self):
130+
resources_in_progress = True
131+
while (resources_in_progress):
132+
resources_in_progress = self._has_any_child_resources_in_progress_state()
133+
log_data_list = self._create_telemetry_data()
134+
log_data_list = self.enrich_telemetry_data(log_data_list)
135+
self.send_telemetry(log_data_list)
136+
# If all child resources are completed except PrimeInvoker, marking PrimeInvoker as completed
137+
if not resources_in_progress:
138+
helper._cfn_response(self.event)
139+
time.sleep(int(self.event['ResourceProperties']['scanInterval']))
140+
# If all resources are completed, make final call to know Parent stack status
141+
if not resources_in_progress :
142+
log_data_list = self._create_telemetry_data()
143+
log_data_list = self.enrich_telemetry_data(log_data_list)
144+
self.send_telemetry(log_data_list)
145+
146+
147+
class awsoTelemetry(parentStackTelemetry):
148+
def __init__(self,event,context):
149+
super().__init__(event,context)
150+
151+
def enrich_telemetry_data(self, log_data_list):
152+
static_data = {
153+
'profile': {
154+
'sumo': {
155+
'deployment': self.event['ResourceProperties']['sumoDeployment'],
156+
'orgid': self.event['ResourceProperties']['sumoOrgId'],
157+
},
158+
'solution': {
159+
'name': self.event['ResourceProperties']['solutionName'],
160+
'version': self.event['ResourceProperties']['solutionVersion'],
161+
'deploymentSource': self.event['ResourceProperties']['deploymentSource']
162+
},
163+
}
164+
}
165+
for log_data in log_data_list:
166+
log_data.update(static_data)
167+
return log_data_list
168+
169+
if __name__=="__main__":
170+
event={}
171+
context = {"aws_request_id":"5678-sxcvbnm-fghjk-123456789"}
172+
create(event,context)
2.15 MB
Binary file not shown.

0 commit comments

Comments
 (0)