Skip to content

Commit 6d4c92a

Browse files
SD-1070. From v1.9.4. TF 0.12. Everest VPC - Fixes
1 parent e05e6de commit 6d4c92a

File tree

4 files changed

+133
-191
lines changed

4 files changed

+133
-191
lines changed

aws.tf

Lines changed: 0 additions & 2 deletions
This file was deleted.

include/lambda.py

Lines changed: 132 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#!/usr/bin/python2
2+
13
import boto3
24
import json
35
import os
@@ -7,7 +9,6 @@
79
zone_id = os.environ['ZONE_ID']
810
service = os.environ['SERVICE']
911
ttl = int(os.environ['TTL'])
10-
dns_role_arn = os.environ.get('DNS_ROLE_ARN')
1112

1213
private_instance_record_template = os.environ['PRIVATE_INSTANCE_RECORD_TEMPLATE']
1314
private_asg_record_template = os.environ['PRIVATE_ASG_RECORD_TEMPLATE']
@@ -18,33 +19,17 @@
1819
manage_public_asg_dns = os.environ['MANAGE_PUBLIC_ASG_DNS'].lower() in ["true", "1"]
1920

2021
aws_region = os.environ.get('AWS_DEFAULT_REGION')
21-
22+
r53_client = boto3.client('route53')
2223
ec2_resource = boto3.resource('ec2', region_name=aws_region)
2324
asg_client = boto3.client('autoscaling', region_name=aws_region)
2425

25-
26-
def get_session(role_arn, sts_client):
27-
"""
28-
Assumes a role in the given account and returns a Boto3 session.
29-
30-
"""
31-
32-
# Assume the role and get back credentials.
33-
acc_id = sts_client.get_caller_identity()['Account']
34-
response = sts_client.assume_role(
35-
RoleArn=role_arn,
36-
RoleSessionName='{}-updates-in-{}-from-{}'.format(service, zone_id, acc_id),
37-
DurationSeconds=900,
38-
)
39-
creds = response['Credentials']
40-
41-
# Return a new session using those credentials.
42-
session = boto3.Session(
43-
aws_access_key_id=creds['AccessKeyId'],
44-
aws_secret_access_key=creds['SecretAccessKey'],
45-
aws_session_token=creds['SessionToken'],
46-
)
47-
return session
26+
# get domain name
27+
try:
28+
hostedzone = r53_client.get_hosted_zone(Id=zone_id)
29+
domain = hostedzone['HostedZone']['Name']
30+
except Exception as e:
31+
print e
32+
raise
4833

4934

5035
def generate_record_name(template, **kwargs):
@@ -73,136 +58,48 @@ def generate_record_name(template, **kwargs):
7358
return names_map[template].substitute(**kwargs)
7459

7560

76-
def change_rrs(changes, zoneid, r53_client):
77-
"""
78-
make DNS update
79-
:param changes: list of R53 changes
80-
:param zoneid: string
81-
:return: response dict
82-
"""
83-
print('Performing change {}'.format(json.dumps(changes)))
84-
response = r53_client.change_resource_record_sets(
85-
HostedZoneId='/hostedzone/{}'.format(zoneid),
86-
ChangeBatch={
87-
'Comment': 'Updated by Lambda Function',
88-
'Changes': changes
89-
}
90-
)
91-
return response
92-
93-
94-
def parse_event(event):
95-
"""
96-
:param event: event object received by lambda
97-
:return: tuple containing message and metadata from an event object
98-
"""
99-
metadata = {}
100-
message = json.loads(event['Records'][0]['Sns']['Message'])
101-
if 'NotificationMetadata' in list(message.keys()):
102-
metadata = json.loads(message['NotificationMetadata'])
103-
return message, metadata
104-
105-
106-
def get_instance_metadata(instance_id):
107-
"""
108-
:param instance_id: string
109-
:return: dict containing info about instance
110-
"""
111-
112-
instance = ec2_resource.Instance(instance_id)
113-
# we only want running instances
114-
if instance.state['Name'] not in ['running']:
115-
return False
116-
metadata = {
117-
'private_ip': instance.private_ip_address,
118-
'private_hostname': instance.private_dns_name,
119-
'public_ip': instance.public_ip_address,
120-
'az': instance.placement['AvailabilityZone']
121-
}
122-
return metadata
123-
124-
125-
def get_asg_instances(asg_name, asg_event, message):
126-
"""
127-
:param asg_name: string
128-
:return: dict of dicts with keys being instance IDs and values
129-
being instance information as returned by get_instance_metadata
130-
"""
131-
asg = asg_client.describe_auto_scaling_groups(
132-
AutoScalingGroupNames=[asg_name])
133-
# get instances IDs
134-
asg_instances = [i['InstanceId']
135-
for i in asg['AutoScalingGroups'][0]['Instances']]
136-
137-
# ensure launching instance is found in the asg
138-
ec2_instance = ""
139-
if asg_event == "autoscaling:EC2_INSTANCE_LAUNCH":
140-
ec2_instance = message['EC2InstanceId']
141-
if ec2_instance not in asg_instances:
142-
raise Exception('Launched instance not found in asg')
143-
144-
return_value = {}
145-
for instance in asg_instances:
146-
metadata = get_instance_metadata(instance)
147-
if metadata:
148-
return_value[instance] = metadata
149-
else:
150-
# ensure metadata is found for launching instance
151-
if instance == ec2_instance and asg_event == "autoscaling:EC2_INSTANCE_LAUNCH":
152-
raise Exception('No metadata returned for ' + instance)
153-
return return_value
154-
155-
15661
def lambda_handler(event, context):
15762

158-
# assume role in DNS account if dns_role_arn is specified
159-
if dns_role_arn:
160-
sts_client = boto3.client('sts')
161-
dns_session = get_session(dns_role_arn, sts_client)
162-
r53_client = dns_session.client('route53')
163-
else:
164-
r53_client = boto3.client('route53')
165-
166-
# get domain name
167-
hostedzone = r53_client.get_hosted_zone(Id=zone_id)
168-
domain = hostedzone['HostedZone']['Name']
169-
170-
# parse event
17163
message, metadata = parse_event(event)
17264

173-
print('Received event: {}'.format(json.dumps(event)))
174-
print('Message: {}'.format(json.dumps(message)))
175-
print('Metadata: {}'.format(json.dumps(metadata)))
65+
print 'Received event: {}'.format(json.dumps(event))
66+
print 'Message: {}'.format(json.dumps(message))
67+
print 'Metadata: {}'.format(json.dumps(metadata))
17668

17769
# asg name and event
17870
asg_name = message['AutoScalingGroupName']
17971
asg_event = message['Event']
18072

18173
# get metadata of all instances in ASG
18274
instances_metadata = get_asg_instances(asg_name, asg_event, message)
183-
print('ASG INSTANCES: {}'.format(json.dumps(instances_metadata)))
75+
print 'ASG INSTANCES: {}'.format(json.dumps(instances_metadata))
18476

18577
# create a list of public addresses of all instances in ASG
18678
asg_public_ips = []
187-
for _metadata in instances_metadata.values():
79+
for _metadata in instances_metadata.itervalues():
18880
if _metadata['public_ip'] is not None:
18981
asg_public_ips.append(_metadata['public_ip'])
19082
else:
19183
continue
192-
print('ASG_PUBLIC_IPS: {}'.format(json.dumps(asg_public_ips)))
84+
print 'ASG_PUBLIC_IPS: {}'.format(json.dumps(asg_public_ips))
19385

19486
# create a list of private addresses of asg instances
19587
asg_private_ips = [instances_metadata[i]['private_ip']
196-
for i in instances_metadata.keys()]
197-
print('ASG_PRIVATE_IPS: {}'.format(json.dumps(asg_private_ips)))
88+
for i in instances_metadata.iterkeys()]
89+
print 'ASG_PRIVATE_IPS: {}'.format(json.dumps(asg_private_ips))
19890

19991
# holds DNS changes to do
20092
changes = []
93+
asg_event_types = {
94+
'launch': 'autoscaling:EC2_INSTANCE_LAUNCH',
95+
'terminate': 'autoscaling:EC2_INSTANCE_TERMINATE',
96+
'test': 'autoscaling:TEST_NOTIFICATION',
97+
}
20198

20299
if manage_instance_dns:
203100
# instance has been launched or asg created
204101
if instances_metadata:
205-
for instance_id, instance_info in instances_metadata.items():
102+
for instance_id, instance_info in instances_metadata.iteritems():
206103
instance_ip = instance_info['private_ip']
207104
az = instance_info['az']
208105
az_short = az.split('-')[-1]
@@ -268,4 +165,111 @@ def lambda_handler(event, context):
268165

269166
# apply dns updates
270167
if changes:
271-
change_rrs(changes, zone_id, r53_client)
168+
change_rrs(changes, zone_id)
169+
170+
171+
# helpers
172+
173+
def find_record(name, zone_id):
174+
"""
175+
find resource record
176+
:param name: string, record name to find
177+
:param zone_id: string
178+
:return: resource record:
179+
[{u'Name': 'myrecord.kops.askredhat.com.',
180+
u'ResourceRecords': [{u'Value': '10.10.10.10'},
181+
{u'Value': '10.10.10.11'},
182+
{u'Value': '10.10.10.12'}],
183+
u'TTL': 300,
184+
u'Type': 'A'}]
185+
"""
186+
paginator = r53_client.get_paginator('list_resource_record_sets')
187+
page_iterator = paginator.paginate(
188+
HostedZoneId='hostedzone/{}'.format(zone_id))
189+
rrs = None
190+
for page in page_iterator:
191+
rrs = filter(lambda record:
192+
record['Name'] == name, page['ResourceRecordSets'])
193+
if rrs:
194+
break
195+
return rrs
196+
197+
198+
def change_rrs(changes, zoneid):
199+
"""
200+
make DNS update
201+
:param changes: list of R53 changes
202+
:param zoneid: string
203+
:return: response dict
204+
"""
205+
print 'Performing change {}'.format(json.dumps(changes))
206+
response = r53_client.change_resource_record_sets(
207+
HostedZoneId='/hostedzone/{}'.format(zoneid),
208+
ChangeBatch={
209+
'Comment': 'Updated by Lambda Function',
210+
'Changes': changes
211+
}
212+
)
213+
return response
214+
215+
216+
def parse_event(event):
217+
"""
218+
:param event: event object received by lambda
219+
:return: tuple containing message and metadata from an event object
220+
"""
221+
metadata = {}
222+
message = json.loads(event['Records'][0]['Sns']['Message'])
223+
if 'NotificationMetadata' in message.keys():
224+
metadata = json.loads(message['NotificationMetadata'])
225+
return message, metadata
226+
227+
228+
def get_instance_metadata(instance_id):
229+
"""
230+
:param instance_id: string
231+
:return: dict containing info about instance
232+
"""
233+
234+
instance = ec2_resource.Instance(instance_id)
235+
# we only want running instances
236+
if instance.state['Name'] not in ['running']:
237+
return False
238+
metadata = {
239+
'private_ip': instance.private_ip_address,
240+
'private_hostname': instance.private_dns_name,
241+
'public_ip': instance.public_ip_address,
242+
'az': instance.placement['AvailabilityZone']
243+
}
244+
return metadata
245+
246+
247+
def get_asg_instances(asg_name, asg_event, message):
248+
"""
249+
:param asg_name: string
250+
:return: dict of dicts with keys being instance IDs and values
251+
being instance information as returned by get_instance_metadata
252+
"""
253+
asg = asg_client.describe_auto_scaling_groups(
254+
AutoScalingGroupNames=[asg_name])
255+
# get instances IDs
256+
asg_instances = [i['InstanceId']
257+
for i in asg['AutoScalingGroups'][0]['Instances']]
258+
259+
# ensure launching instance is found in the asg
260+
ec2_instance = ""
261+
if asg_event == "autoscaling:EC2_INSTANCE_LAUNCH":
262+
ec2_instance = message['EC2InstanceId']
263+
if ec2_instance not in asg_instances:
264+
raise Exception('Launched instance not found in asg')
265+
266+
return_value = {}
267+
for instance in asg_instances:
268+
metadata = get_instance_metadata(instance)
269+
if metadata:
270+
return_value[instance] = metadata
271+
else:
272+
# ensure metadata is found for launching instance
273+
if instance == ec2_instance and asg_event == "autoscaling:EC2_INSTANCE_LAUNCH":
274+
raise Exception('No metadata returned for ' + instance)
275+
return return_value

include/publish.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/env python3
1+
#!/usr/bin/env python2
22

33
import boto3
44
import json

lambda.tf

Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
 (0)