Adding a subscription to an existing sns topic in troposphere - python

I have a use case in which i have an existing sns topic and i am creating lambda functions using cloudformation and troposphere . I have to somehow create my stack in such a way in which the topic sends subscriptions to my lambda functions, but the topic itself should not be recreated.
Below is my code :
from troposphere import FindInMap, GetAtt, Join, Output
from troposphere import Template, Ref
from troposphere.awslambda import Function, Code, Permission
from troposphere.sns import Topic, SubscriptionResource
folder_names = ["welt", "jukin"]
t = Template()
t.set_version("2010-09-09")
t.add_mapping("MapperToTenantId",
{
u'welt': {'id': u't-012'},
u'jukin': {'id': u't-007'}
}
)
t.add_mapping("LambdaExecutionRole",
{u'lambda-execution-role': {u'ARN': u'arn:aws:iam::498129003450:role/service-role/lambda_execution_role'}}
)
code = [
"def lambda_handler(event, context):\n",
" message = event[‘Records’][0][‘Sns’][‘Message’]\n",
" print(“From SNS: “ + message)\n",
" return message\n"
]
for cp in folder_names:
lambda_function = t.add_resource(Function(
f"{cp}MapperLambda",
Code=Code(
ZipFile=Join("", code)
),
Handler="index.handler",
Role=FindInMap("LambdaExecutionRole", "lambda-execution-role", "ARN"),
Runtime="python3.6",
)
)
t.add_resource(Permission(
f"InvokeLambda{cp}Permission",
FunctionName=GetAtt(lambda_function, "Arn"),
Action="lambda:InvokeFunction",
SourceArn='arn:aws:sns:us-west-2:498129003450:IngestStateTopic',
Principal="sns.amazonaws.com"
))
t.add_resource(SubscriptionResource(
EndPoint=GetAtt(lambda_function, "Arn"),
Protocol='lambda',
TopicArn='arn:aws:sns:us-west-2:498129003450:IngestStateTopic'
))
with open('mapper_cf.yaml', 'w') as y:
y.write(t.to_yaml())
I am getting the below error and i am not able to figure a way out :
Traceback (most recent call last):
File "create_cloudformation.py", line 54, in <module>
TopicArn='arn:aws:sns:us-west-2:498129003450:IngestStateTopic'
TypeError: __init__() missing 1 required positional argument: 'title'
Is this possible to do in troposphere. I don't want to hardcode the block in cloud formation but i want to generate that in troposphere.
Is this even possible to do ?
Kindly give me some hints.

The error you are getting is related to not specifying a title string. Try this:
t.add_resource(SubscriptionResource(
f"{cp}Subscription",
EndPoint=GetAtt(lambda_function, "Arn"),
Protocol='lambda',
TopicArn='arn:aws:sns:us-west-2:498129003450:IngestStateTopic'
))

Related

"The document path provided in the update expression is invalid for update" when trying to update a map value

Here is my sample code
import boto3
import os
ENV = "dev"
DB = "http://awsservice.com"
REGION = "us-east-1"
TABLE = "traffic-count"
def main():
os.environ["AWS_PROFILE"] = ENV
client = boto3.resource("dynamodb", endpoint_url=DB, region_name=REGION)
kwargs = {'Key': {'id': 'D-D0000012345-P-1'},
'UpdateExpression': 'ADD #count.#car :delta \n SET #parentKey = :parent_key, #objectKey = :object_key',
'ExpressionAttributeValues': {':delta': 1, ':parent_key': 'District-D0000012345', ':object_key': 'Street-1'},
'ExpressionAttributeNames': {'#car': 'car', '#count': 'count', '#parentKey': 'parentKey', '#objectKey': 'objectKey'}}
client.Table(TABLE).update_item(**kwargs)
if __name__ == "__main__":
main()
What I want to achieve is this:
With a single API call (in this update_item), I want to be able to
If the item does not exit. create an item with a map count and initialise it with {'car': 1} and set the fields parent_key and object_key.
or
If the item already exists, update the field to {'car': 2} (if the original count is 1)
Previously, if I did not use a map, I can successfully update with this expression,
SET #count = if_not_exist(#count, :zero) + :delta,
#parentKey = :parent_key, #objectKey = :object_key
However I am getting this error:
botocore.exceptions.ClientError: An error occurred
(ValidationException) when calling the UpdateItem operation: The
document path provided in the update expression is invalid for update
Which document path is causing the problem? How can I fix it?
For those who landed on this page with similar error:
The document path provided in the update expression is invalid for update
The reason may be:
for the item on which the operation is being performed,
this attribute (count, for example) is not yet set.
Considering the sample code from question,
The exception could be coming from all those items where count is empty or not set. So the update query doesn't know on which map the new value(s) (car, for example) needs to be set or updated.
In the question, it was possible for the OP in the beginning because, the attribute is not a map and the process is simply setting the value to count as is. It's not trying to access a key of an unknown map to set the value.
This can be handled by catching the exception. For example:
from botocore.exceptions import ClientError
...
try:
response = table.update_item(
Key={
"pk": pk
},
UpdateExpression="set count.car = :c,
ExpressionAttributeValues={
':c': "some car"
},
ReturnValues="UPDATED_NEW"
)
except ClientError as e:
if e.response['Error']['Code'] == 'ValidationException':
response = table.update_item(
Key={
"pk": pk
},
UpdateExpression="set count = :count",
ExpressionAttributeValues={
':count': {
':c': "some car"
}
},
ReturnValues="UPDATED_NEW"
)

Partition Key bad request when adding data to a container - python

So I've been going through the tutorial code for cosmodb in python to have better understanding with it, and currently am stuck on an issue with the partition key when trying to add data.
This code creates the container object, with a partition key called account_number.
container = db.create_container(id=CONTAINER_ID, partition_key=PartitionKey(path='/account_number') ,offer_throughput=400)
Here is some dummy data:
def get_sales_order(item_id):
order1 = {'id' : item_id,
'account_number' : 'Account1',
'purchase_order_number' : 'PO18009186470',
'order_date' : datetime.date(2005,1,10).strftime('%c'),
'subtotal' : 419.4589,
'tax_amount' : 12.5838,
'freight' : 472.3108,
'total_due' : 985.018,
'items' : [
{'order_qty' : 1,
'product_id' : 100,
'unit_price' : 418.4589,
'line_price' : 418.4589
}
],
'ttl' : 60 * 60 * 24 * 30
}
return order1
When trying to run the following code to add the dummy data into the db I use the following:
sales_order = get_sales_order("SalesOrder1")
container.create_item(body = sales_order)
When I run this I get the following error message.
(BadRequest) Message: {"Errors":["The partition key supplied in x-ms-partitionkey header has fewer components than defined in the collection."]}
After looking at the documentation for the create_item method, I noticed the initial_headers param. So I created a dict with the necessary info as seen here:
headers = {}
headers["x-ms-documentdb-partitionkey"]= "account_number"
Next I ran the following code with the addition of the `"x-ms-documentdb-partitionkey" header.
container.create_item(body = sales_order, initial_headers = headers)
However I still receive the same error. Help is very much appreciated.

TypeError while logging in python

I have an JSON input and I am trying to log the value of a specific key. But It's showing me TypeError: Not all arguments converted during string formatting.
msg = {'job_id' = '11', 'status' = 'complete', 'tr_no' = 'S$Wed'}
logger.info('Process Completed:')
logger.info(msg["tr_no"], 'Successful' )
I am using python 2.7. I tried it in following way as well but got same error message
logger.info(str(msg["tr_no"]), 'Successful' )
import logging as logger
msg = dict(job_id='11', status='complete', tr_no='S$Wed')
logger.info('Process Completed:')
logger.info(msg["tr_no"], 'Successful' )
Try this code ..... .
Or replace 2nd line as :
msg = {'job_id':'11', 'status':'complete', 'tr_no':'S$Wed'}
Thank you .

Get replicationLag in mongo with pyMongo

I am trying, to get replication-delay using db.rs.printSlaveReplicationInfo from python with pymongo, but not getting any proper way to do so.
I tried the following, but no help.
>>>from pymongo import MongoClient
>>>client = MongoClient()
>>>db = client.test_database
>>>db.rs.printSlaveReplicationInfo
Collection(Database(MongoClient([u'10.0.0.19:10006', u'10.0.0.68:10002']), u'xyz'), u'rs.printSlaveReplicationInfo')
db.rs.printSlaveReplicationInfo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/local/lib64/python2.7/site-packages/pymongo/collection.py", line 2413, in __call__
self.__name.split(".")[-1])
TypeError: 'Collection' object is not callable. If you meant to call the 'printSlaveReplicationInfo' method on a 'Collection' object it is failing because no such method exists.
>>> db.rs
Collection(Database(MongoClient([u'10.0.0.19:10006', u'10.0.0.68:10002']), u'xyz'), u'rs')
Can anyone help with this? or how to do it?
Thanks in advance.
I found out the answer.Here is the complete code :
(Note: You need to have admin privileges to run this command.)
uri = "mongodb://usernamen:password#host:port/admin"
conn = pymongo.MongoClient(uri)
db = conn['admin']
db_stats = db.command({'replSetGetStatus' :1})
primary_optime = 0
secondary_optime = 0
for key in db_stats['members'] :
if key['stateStr'] == 'SECONDARY' :
secondary_optime = key['optimeDate']
if key['stateStr'] == 'PRIMARY' :
primary_optime =key['optimeDate']
print 'primary_optime : ' + str(primary_optime)
print 'secondary_optime : ' + str(secondary_optime)
seconds_lag = (primary_optime - secondary_optime ).total_seconds()
#total_seconds() userd to get the lag in seconds rather than datetime object
print 'secondary_lag : ' + str(seconds_lag)
optime reperesents the date,till which that mongo-node has data.
You can read more about it here :
https://docs.mongodb.com/manual/reference/command/replSetGetStatus/

Python evernote api Error

Trying to build app that connects with Evernote API, in Python/Django. For the below code i get the following error message: " 'Store' object has no attribute 'NoteFilter' " from http://dev.evernote.com/documentation/reference/NoteStore.html#Svc_NoteStore One can see, that NoteFilter is attribute of NoteStore.
def list(request):
nbname="mihkel's notebook"
client = EvernoteClient(token=token, sandbox=False)
note_store = client.get_note_store()
notebooks = note_store.listNotebooks()
for nb in notebooks:
if nbname == nb.name:
nb = nb
filter = note_store.NoteFilter()
filter.notebookGuid = nb.guid
notelist = note_store.findNotes(token,filter,0,10)
break
return render_to_response('list.html', {'nb': nb, 'notelist':notelist})
Solution:
from evernote.edam.notestore import NoteStore
....
....
def list.. :
...
Filter = NoteStore.NoteFilter()
notestore/ttypes.py has the definition for NoteFilter
Some of the examples in the API code import like this
import evernote.edam.notestore.NoteStore as NoteStore
import evernote.edam.type.ttypes as Types
Not sure if this would be an acceptable way to correct, but I added this:
import evernote.edam.notestore.ttypes as NoteStoreTypes
and created my filter like this:
filter = NoteStoreTypes.NoteFilter()

Categories