I want to create multiple Flask endpoints I read from my Config. Is it possible to make a for or while loop to create them? The Endpoints Address would be variable, but there would be no limit on how many I would need.
My Idea was:
for x in myList:
#app.route(var, ...)
def route():
do smt ...
Thanks for your help
You can use
app.add_url_rule(rule, endpoint=None, view_func=None, provide_automatic_options=None, **options)
to achieve this.
For example:
for endpoint in endpoint_list:
app.add_url_rule(endpoint['route'], view_func=endpoint['view_func'])
Check out the docs.
Note that the endpoint_list contains records of endpoints. It should be a list of dictionaries, for example:
endpoint_list = [
{
"route": "/",
"view_func": index
},
.....
]
Avoid using "list" as the variable name. It would override Python's default list function.
Related
I have a Twilio number and a dictionary full of params that I want to set up. The only way I know to set number properties is by passing them into the number.update() function, so this is kinda annoying.
If the dictionary keys have the same name as the inputs I'm trying to pass to the update function, is there a cleaner way to do this?
or, can I do better than this?
params = {
'voice_url':'http://endpoint.co',
'friendly_name':'call a dinosaur'
}
number.update(voice_url=params['voice_url'], friendly_name=params['friendly_name']
Twilio developer evangelist here.
To do this you can use the unpacking operator ** like this:
number.update(**params)
** unpacks a dictionary into keyword arguments.
I'm using the CDK to create some infrastructure from a yaml template file. Some resources require multiple instances. I thought writing a function would be the easiest way to create multiple instance of the resource
Function
def create_vpn_connection_route(cidr_count, destination_cidr):
vpn_connection_route = aws_ec2.CfnVPNConnectionRoute(
self,
f'vpn_connection_route{cidr_count}',
vpn_connection_id=vpn_connection.ref,
destination_cidr_block=destination_cidr
)
return vpn_connection_route
I then loop over it and generate the "Id" by enumarating over the destination_cidrs like so
for cidr_count, destination_cidr in enumerate(tenant_config['vpn_config'][0]['destination_cidrs']):
create_vpn_connection_route(cidr_count, destination_cidr)
This is what's in my yaml
vpn_config:
- private_ip:
- 10.1.195.201/32
- 10.1.80.20/32
- 10.1.101.8/32
Is there a better way to do this in the CDK? and can I dynamically generate Id'S for resources?
Cheers
I don't know that it makes your code much better, but you can use a Construct instead of a function.
class VpnConnectionRoute(core.Construct):
def __init__(self, scope, id_, vpn_connection, destination_cidr):
super().__init__(scope, id_)
self.vpn_connection_route = aws_ec2.CfnVPNConnectionRoute(
self,
'vpn_connection_route',
vpn_connection_id=vpn_connection.vpn_id,
destination_cidr_block=destination_cidr
)
# ...
for cidr_count, destination_cidr in enumerate(tenant_config['vpn_config'][0]['destination_cidrs']):
VpnConnectionRoute(self, f"route{cidr_count}", vpn_connection, destination_cidr)
VpnConnectionRoute(self, f"route{cidr_count}", vpn_connection, destination_cidr)
VpnConnectionRoute(self, f"route{cidr_count}", vpn_connection, destination_cidr)
CDK will automatically name your resources based on both the construct and your name. So the end result will look like:
"route1vpnconnectionrouteAE1C11A9": {
"Type": "AWS::EC2::VPNConnectionRoute",
"Properties": {
"DestinationCidrBlock": "10.1.195.201/32",
"VpnConnectionId": {
"Ref": "Vpn6F669752"
}
},
"Metadata": {
"aws:cdk:path": "app/route1/vpn_connection_route"
}
},
You can also just put destination_cidr inside your route name. CDK will remove all unsupported characters for you automatically.
for destination_cidr in tenant_config['vpn_config'][0]['destination_cidrs']:
aws_ec2.CfnVPNConnectionRoute(
self,
f'VPN Connection Route for {destination_cidr}',
vpn_connection_id=vpn_connection.vpn_id,
destination_cidr_block=destination_cidr
)
The best solution here probably depends on what you want to happen when these addresses change. For this particular resource type, any change in the name or the values will require a replacement anyway. So keeping the names consistent while the values change might not matter that much.
I aws create a 2 api-gateway
First one
https://xx.xx-api.us-east-1.amazonaws.com/v1/uploadapi/?search=all
then my lambda function will invoke below
searchone = es.search(index="my-index", body={"query": {"match_all": {}}})
return searchone
Second One
https://xx.xx-api.us-east-1.amazonaws.com/v1/uploadapi/?search=matchphrase=name_computer
searchtwo = es.search(index="my-index", body={"query": {"match": {"name":"computer"}}})
return searchtwo
Basically need to create single lambda function
if api url is first one then return searchone if the api url is second one then return searchtwo
Disclaimer ? DO i need to create separate Lambda function for above two api's
No, you do not need multiple lambda functions. Among the two url's that you have specified, what changes is only the query params.
...?search=all
...?search=matchphrase=name_computer
Search parameters are used for such conditions i.e. to notify the server if it needs to handle the request in some specific way. You can access them in the lambda function.
Before we move ahead, I would suggest you consider the following schema:
...?search=all --> ?search_type=match_all
...?search=matchphrase=name_computer -> ...?search_type=match_some&match_phrase=name_computer
Now, if you explore the event object that is passed to the lambda function, you will find these query parameters at event.requestContext.queryStringParameters. It would look something like this:
{
<...>
'resource': '/v1/uploadapi',
'requestContext': {
'queryStringParameters': {
'search_type': 'match_some',
'match_phrase': 'name_computer'
},
<...>
}
You can now use this to build logic on top of it.
I'm using Troposphere to build CloudFormation stacks and would like to pass the Elastic Load Balancer ConnectionSettings attribute only if it's set in my configuration, otherwise I don't want to specify it.
If I set it to default None then I get an error about the value not being of the expected type of troposphere.elasticloadbalancing.ConnectionSettings.
I'd rather try to avoid setting an explicit default in the call because it might override other settings.
Idealy, I would like to be able to add attributes to an existing object, e.g.:
lb = template.add_resource(elb.LoadBalancer(
...
))
if condition:
lb.add_attribute(ConnectionSettings = elb.ConnectionSettings(
...
))
Is there a way to achieve that?
UPDATE: I achieved it using a hidden Troposphere method, which works but I'm not happy with:
if condition:
lb.__setattr__('ConnectionSettings', elb.ConnectionSettings(
....
))
I'm still interested in a solution which doesn't involve using a private method from outside the module.
The main README eludes to just using the attribute names like this:
from troposphere import Template
import troposphere.elasticloadbalancing as elb
template = Template()
webelb = elb.LoadBalancer(
'ElasticLoadBalancer',
Listeners=[
elb.Listener(
LoadBalancerPort="80",
InstancePort="80",
Protocol="HTTP",
),
],
)
if True:
webelb.ConnectionSettings = elb.ConnectionSettings(IdleTimeout=30)
elasticLB = template.add_resource(webelb)
print(template.to_json())
So the big question is - where does the configuration for ConnectionSettings come from? Inside Cloudformation itself (and troposphere) there are Conditions, Parameters, and the AWS::NoValue Ref. I use that fairly heavily in the stacker RDS templates:
Here's the Parameter: https://github.com/remind101/stacker/blob/master/stacker/blueprints/rds/base.py#L126
Here's the condition: https://github.com/remind101/stacker/blob/master/stacker/blueprints/rds/base.py#L243
And here's how it's used in a resource later, optionally - if the StorageType Parameter is blank, we use AWS::NoValue, which is a pseudo Ref for not actually setting something: (Sorry, can't post more than 2 links - go to line 304 in the same file to see what I'm talking about)
If you're not using Parameters however, and instead doing all your conditions in python, you could do something similar. Something like:
connection_setting = condition and <actual connection setting code> or Ref("AWS::NoValue")
The other option is to do it entirely in python, which is basically your example. Hopefully that helps, there are a lot of ways to deal with this, including creating two different ELB objects (one with connection settings, one without) and then picking either one with either python code (if condtion) or cloudformation conditions.
If the value is known in Python (i.e., it does not originate from a CloudFormation Parameter), you can use a dictionary to add optional attributes to resources in a Troposphere template:
from troposphere import Template
import troposphere.elasticloadbalancing as elb
template = Template()
my_idle_timeout = 30 # replace this with your method for determining the value
my_elb_params = {}
if my_idle_timeout is not None:
my_elb_params['ConnectionSettings'] = elb.ConnectionSettings(
IdleTimeout=my_idle_timeout,
)
my_elb = template.add_resource(elb.LoadBalancer(
'ElasticLoadBalancer',
Listeners=[
elb.Listener(
LoadBalancerPort="80",
InstancePort="80",
Protocol="HTTP",
),
],
**my_elb_params,
))
print(template.to_json())
If the value originates from a CloudFormation Parameter, you need to create a Condition to test the value of the parameter and use Ref("AWS::NoValue") if no value was provided for the parameter, e.g.:
from troposphere import Template, Parameter, Equals, Ref, If
import troposphere.elasticloadbalancing as elb
template = Template()
my_idle_timeout = template.add_parameter(Parameter(
"ElbIdleTimeout",
Description="Idle timeout for the Elastic Load Balancer",
Type="Number",
))
no_idle_timeout = "NoIdleTimeout"
template.add_condition(
no_idle_timeout,
Equals(Ref(my_idle_timeout), ""),
)
my_elb = template.add_resource(elb.LoadBalancer(
'ElasticLoadBalancer',
Listeners=[
elb.Listener(
LoadBalancerPort="80",
InstancePort="80",
Protocol="HTTP",
),
],
ConnectionSettings=If(
no_idle_timeout,
Ref("AWS::NoValue"),
elb.ConnectionSettings(
IdleTimeout=Ref(my_idle_timeout),
),
),
))
print(template.to_json())
I'm a newb trying to figure out how to accomplish the following:
I have dicts named after users in the following format:
<user>_hx
I want to access them with a function like so:
def foo(user, other_stuff):
user_hx[key]
......etc.
I attempted to access with % as follows:
def foo(user, other_stuff):
%r_hx %user[key]
but for obvious reasons I know that can't work.
Adivce?
What I think you are asking is how to access a variable based on a string representing its name. Python makes this quite easy:
globals()['%s_hx' % user]
will give you the variable <user>_hx, so you just want
globals()['%s_hx' % user][key]
(I'm not sure whether you should be using globals() or locals(); it will depend on how your variables are defined and where you are trying to access them from)
That said, there is probably an easier/cleaner way to do whatever you are doing. For instance, have you considered putting all these dictionaries in a 'master' dictionary so that you can pass them around, as well as just access them
def foo(user,other_stuff):
fname = "%s_hx"%user[key] #not sure where key is comming from... but would result in something like "bob_hx"
with open(fname) as f:
print f.read()
maybe?/
Don't use the name as a variable. You can put your collection of dictionaries inside another, top-level, dictionary with those names as keys.
users = {
"user1_hx": {"name": "User 1"},
"user2_hx": {"name": "User 2"),
}
etc.
Then access as:
realname = users["%s_hx" % name]["name"]
etc.