how to mock lambda cache ssm getattr - python

I am using lambda_cache library in python to implement caching for coupld of parameters stored in ssm. The following code is working
as expected.
from lambda_cache import ssm
#ssm.cache(parameter=['applicationId', 'sharedSecret'], entry_name='parameters', max_age_in_seconds=300)
def lambda_handler(event, context):
applicationId = getattr(context,'parameters').get('applicationId')
sharedSecret = getattr(context,'parameters').get('sharedSecret')
#rest of code
I want to implement a unit test where I need to assign some values to applicationId and sharedSecret. I started as
follows
#mock_ssm
def test_lambda_handler1(self):
ssm = boto3.client('ssm', region_name=REGION_NAME)
response = ssm.put_parameter (
Name = 'parameters',
Value = 'applicationIdValue',
KeyId='applicationId',
Type = 'String',
Overwrite = True
)
response = ssm.put_parameter (
Name = 'parameters',
Value = 'sharedSecretValue',
KeyId = 'sharedSecret',
Type = 'String',
Overwrite = True
)
lambda_handler(event, None)
this has thrown the error 'NoneType' object has no attribute 'parameters'.
So I then created a context as
context = {
'parameters':'parameters'
}
and then called the lambda as
lambda_handler(event, context)
it then thrown the error 'dict' object has no attribute 'parameters'
I have then tried creating a class
class context:
parameters = {"applicationId":"testapplicationId", "sharedSecret":"testsharedSecret"}
and in the unit test
s1 = context()
lambda_handler(event,s1)
But this is returning None for both applicationId and sharedSecret as getattr(context, 'parameters') itself show the len() as 0.
Parameters set in the context class in TestCase are not passed to the lamdba_handler. I think it only allows the attributes defined in the link https://docs.aws.amazon.com/lambda/latest/dg/python-context.html like aws_request_id,memory_limit_in_mb , etc. How can I mock ssm cache to get the parameter values?

Related

moto unit test EC2 - 'NoneType' object has no attribute 'id'

I want to unittest some methods inside my aws_manager:
class aws_manager:
def __init__(self) -> None:
self.ec2 = boto3.client('ec2', region_name=REGION_NAME)
self.running_instances = []
def launchInstances(self, count: int):
instances = self.ec2.run_instances(
ImageId=IMAGE_ID,
MinCount=count,
MaxCount=count,
InstanceType=INSTANCE_TYPE,
KeyName=KEY_NAME,
SecurityGroupIds=[SECURITY_GROUP_ID]
)["Instances"][0]
my test looks like this:
import unittest
import boto3
from moto import mock_ec2
import aws_manager
#mock_ec2
class TestAwsManager(unittest.TestCase):
def test_launchInstances(self):
manager = aws_manager.aws_manager()
manager.launchInstances(2)
client = boto3.client('ec2',region_name='eu-central-1')
instances = client.describe_instances()
print(instances)
But the instances variable looks like this when printed:
{'Reservations': [], 'ResponseMetadata': {'RequestId': 'fdcdcab1-ae5c-489e-9c33-4637c5dda355', 'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'amazon.com'}, 'RetryAttempts': 0}}
I also get this error:
[ERROR][2022-07-02 22:30:42,452]: 'NoneType' object has no attribute 'id'
Have I overseen something? Shouldn't client contain the instances?
Thanks in advance
This should be your code the mock ec2 and security group id.
def ec2():
"""
Returns a mock EC2 resource
"""
with mock_ec2():
ec2_res = boto3.resource("ec2", region_name="us-west-1")
ec2_res.create_key_pair(KeyName="your_key")
yield ec2_res
#pytest.fixture
def security_group_id(ec2):
"""
Returns a mock EC2 instance
"""
security_group = ec2.create_security_group(
GroupName="your_group_name",
Description="test",
)
yield security_group.id

Unexpected keyword argument when working with several objects

I'm trying to work with severals objects to achieve an action.
My models.py
class LogBook(models.Model):
name = models.CharField()
class LogMessage(models.Model):
logbook = models.ForeignKey(LogBook, on_delete=models.CASCADE)
class LogDone(models.Model):
logmessage = models.ForeignKey(LogMessage)
done_status = models.BooleanField(default=False)
My view :
def logmessage_done(request, logmessage_id, log_id, token ):
log = get_object_or_404(LogBook, pk=log_id)
logmessages = LogMessage.objects.filter(logbook=log)
logdone = LogDone.objects.get_or_create(logmessage=logmessages)
logdone.done_status = True
logdone.update()
My url :
"done/<int:logmessage_id>/<int:log_id>/<str:token>"
What I want to achieve :
I want to change the status of the logdone object which is link to the logmessage object but I am not sure I have access object correctly.
What error I have :
The QuerySet value for an exact lookup must be limited to one result using slicing.
Change your view like this:
def logmessage_done(request, logmessage_id, log_id, token ):
log = get_object_or_404(LogBook, pk=log_id)
logmessages = LogMessage.objects.filter(logbook=log)
for log_message in logmessages:
LogDone.objects.update_or_create(logmessage=log_message,defaults={"done_status": True})
Here , log returns a single object with id . logmessages returns a queryset with logbook = the log returned in first query. Have to use update_or_create method

AWS - Automatic remediation of EC2s with Public IP

Looking develop a python script that will scan EC2 instances for Public IP's and if found, stop those instances.
I'm sorta new to Python so have challenges trying to put two pieces of code together.
ec2Resource = boto3.resource('ec2')
def lambda_handler(event, context):
instances = ec2Resource.instances.all()
for instance in instances:
#print("inst.public_ip_address",inst.public_ip_address)
if instance.public_ip_address:
print("Instance ID: ",instance.id," , Instance Platform: ",instance.platform," , Public IP: ",instance.public_ip_address,", Instance Type:",instance.instance_type,",Instance Image Id: ",instance.image.id)
response = client.stop_instances(
InstanceIds=[
'string',
],
Hibernate=True,
DryRun=False,
Force=False
)
Basically, looking for a automated script that discovers public IPs on EC2's and then stops them. My apologies if the script above looks hamburglar
UPDATE:
Think I cleaned it up to create lists and then from that list, issue a stop.
#!/usr/bin/python
'''
Finds instance id, Instance Platform, Public IP, instance type based on tags.
Returns a list of instances found
'''
import boto3
def instances_find(name, value):
'''
Finds instance id's based on tags.
Returns a list of instances found.
'''
list_instances = []
# filter based on tags
filters =[
{
'Name': name,
'Values': [
value,
]
},
]
instances = ec2_resource.instances.filter(Filters=filters)
for instance in instances:
# for each instance, append to list
list_instances.append("Instance ID: ",instance.id," , Instance Platform: ",instance.platform," , Public IP: ",instance.public_ip_address,", Instance Type:",instance.instance_type,",Instance Image Id: ",instance.image.id)
return list_instances
def instances_stop(list):
'''
Stops instances defined in the list.
'''
ec2_client.stop_instances(InstanceIds=list)
# enter tag name and value
tag_name = 'tag:environment'
tag_value = 'dev'
ec2_resource = boto3.resource('ec2')
ec2_client = boto3.client('ec2')
# find instances
ec2_list = instances_find(tag_name, tag_value)
# stop instances
ec2_stop = instances_stop(ec2_list)
print('stopped instances: ' + str(ec2_list))

how to return only the value of enum rather than kv pair in flask-sqlalchemy

I'm using flask-sqlalchemy api developing and i don't know how to return only the value of an Enum type data
I decide to change the db column 'status' from String to Enum but without changing frontend code, so I need to only return the value of enumerated column. I already tried Message.status.value but it doesn't work(AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Message.status has an attribute 'value')
Message model:
class Message(SurrogatePK, Model):
__tablename__ = 'message'
title = Column(db.String(30))
url = Column(db.String(180))
body = Column(db.String(300))
category = Column(db.String(20))
status = Column(db.Enum(MessageStatusEnum))
Enum class:
#unique
class MessageStatusEnum(Enum):
WORKING = "working on it, please wait..."
COMPLETE = "complete sending message"
view:
#blueprint.route('', methods=['GET'])
def get_msg_list():
…
messages = UserMessage.query.join(Message).filter(…).with_entities(
Message.id, Message.title, … Message.status
).paginate(page=page_index, per_page=page_size, error_out=False)
data = {'pages': messages.pages, 'items': messages.items}
return request_handler(SUCCESS, data=data)
I solved this question and post here for reference
msg_items = []
for i in messages.items:
element = {j.name: getattr(i, j.name) for j in [Message.status, Message.categ...., Message.body]}
element['status'] = element['status'].value
msg_items.append(element)
data = {'pages': messages.pages, 'items': msg_items}
return auth_handler(SYS_STATUS.SUCCESS, data=data)

Can't get ec2_client.describe_instances() to print instances passed as parameter

I'm having a hard time trying to figure out how to make the code below to take InstanceID as parameter and display info only about this instance:
import boto3
ec2_client = boto3.client('ec2')
instance_data = []
instanceid = 'i-123456'
def get_instace_data(instanceid):
reservations = ec2_client.describe_instances()
for reservation in reservations['Reservations']:
for instance in reservation['Instances']:
instance_data.append(
{
'instanceId': instance['InstanceId'],
'instanceType': instance['InstanceType'],
'launchDate': instance['LaunchTime'].strftime('%Y-%m-%dT%H:%M:%S.%f')
}
)
print(instance_data)
get_instace_data(instanceid)
My understanding is that get_instance_data() should be taking the instance ID and display only info for that instance, even though it iterates to the whole 'Reservations' dict that returned.
Am i missing something here?
Thanks in advance!
Your code will append all instances to the instance_data.
In order to receive only required instance you need change
reservations = ec2_client.describe_instances()
To the following:
reservations = ec2_client.describe_instances(
Filters=
InstanceIds=['i-123456'])
describe_instance() let you specify filters, however, the syntax can be quite confusing. Because you can specify instance ID inside the Filters parameter or inside InsanceIds parameter.
e.g.
# method 1
response = client.describe_instances(
InstanceIds=[
'i-123456',
],
)
# method 2
response = client.describe_instances(
Filters=[
{
'Name': 'instance-id',
'Values': ['i-123456']
},
],
)
The documentation showing Filters (list) is utterly confusing, because the only way to pass in the correct filtering name into the list, you must embrace the "Name" and "Value" explicitly inside a dict.
# This will not works
response = client.describe_instances(
Filters=[
'instance-id:i-123456'
])
# Neither will this!
response = client.describe_instances(
Filters=[
{'instance-id': ['i-123456']}
])

Categories

Resources