Merge pull request #24199 from lyft/route53-fix-elb

Various fixes for boto_route53 and boto_elb
This commit is contained in:
Nicole Thomas 2015-05-28 21:02:41 -06:00
commit ce8e43b774
3 changed files with 114 additions and 167 deletions

View file

@ -71,6 +71,14 @@ def _is_valid_resource(_type):
return False
def _encode_name(name):
return name.replace('*', r'\052')
def _decode_name(name):
return name.replace(r'\052', '*')
def get_record(name, zone, record_type, fetch_all=False, region=None, key=None,
keyid=None, profile=None):
'''
@ -94,6 +102,7 @@ def get_record(name, zone, record_type, fetch_all=False, region=None, key=None,
if not _is_valid_resource(_type):
return None
name = _encode_name(name)
if _type == 'A':
_record = _zone.get_a(name, fetch_all)
elif _type == 'CNAME':
@ -102,7 +111,7 @@ def get_record(name, zone, record_type, fetch_all=False, region=None, key=None,
_record = _zone.get_mx(name, fetch_all)
if _record:
ret['name'] = _record.name
ret['name'] = _decode_name(_record.name)
ret['value'] = _record.to_print()
ret['record_type'] = _record.type
ret['ttl'] = _record.ttl

View file

@ -317,13 +317,31 @@ def present(
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _cnames_present(name, cnames, region, key, keyid, profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
if cnames:
lb = __salt__['boto_elb.get_elb_config'](
name, region, key, keyid, profile
)
for cname in cnames:
_ret = __salt__['state.single'](
'boto_route53.present',
name=cname.get('name'),
value=lb['dns_name'],
zone=cname.get('zone'),
record_type='CNAME',
identifier=cname.get('identifier', None),
ttl=cname.get('ttl', None),
region=region,
key=key,
keyid=keyid,
profile=profile
)
_ret = _ret.values()[0]
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
if not _ret['result']:
ret['result'] = _ret['result']
if ret['result'] is False:
return ret
_ret = _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profile)
ret['changes'] = dictupdate.update(ret['changes'], _ret['changes'])
ret['comment'] = ' '.join([ret['comment'], _ret['comment']])
@ -741,108 +759,6 @@ def _subnets_present(
return ret
def _cnames_present(
name,
cnames,
region,
key,
keyid,
profile):
ret = {'result': True, 'comment': '', 'changes': {}}
if not cnames:
cnames = []
lb = __salt__['boto_elb.get_elb_config'](name, region, key, keyid, profile)
if not lb:
if not __opts__['test']:
ret['result'] = False
msg = 'Failed to retrieve ELB {0}.'.format(name)
ret['comment'] = msg
return ret
to_create = []
to_update = []
for cname in cnames:
_name = cname.get('name', None)
_zone = cname.get('zone', None)
if not _name or not _zone:
raise SaltInvocationError('cnames must provide name and zone'
' attributes.')
record = __salt__['boto_route53.get_record'](_name, _zone, 'CNAME',
False, region, key,
keyid, profile)
if not record:
to_create.append(cname)
elif record['value'].rstrip('.') != lb['dns_name'].rstrip('.'):
to_update.append(cname)
if to_create or to_update:
if __opts__['test']:
msg = 'ELB {0} to have cnames modified.'.format(name)
ret['comment'] = msg
ret['result'] = None
return ret
if to_create:
created = []
not_created = []
for cname in to_create:
_name = cname.get('name')
_zone = cname.get('zone')
_iden = cname.get('identifier', None)
_ttl = cname.get('ttl', None)
_created = __salt__['boto_route53.add_record'](
_name, lb['dns_name'], _zone, 'CNAME', _iden, _ttl, region,
key, keyid, profile)
if _created:
created.append(_name)
else:
not_created.append(_name)
if created:
msg = 'Created cnames {0}.'.format(','.join(created))
ret['comment'] = msg
if not_created:
msg = 'Failed to create cnames {0}.'
msg = msg.format(','.join(not_created))
if 'comment' in ret:
ret['comment'] = ret['comment'] + ' ' + msg
else:
ret['comment'] = msg
ret['result'] = False
if to_update:
updated = []
not_updated = []
for cname in to_update:
_name = cname.get('name')
_zone = cname.get('zone')
_iden = cname.get('identifier', None)
_ttl = cname.get('ttl', None)
_updated = __salt__['boto_route53.update_record'](
_name, lb['dns_name'], _zone, 'CNAME', _iden, _ttl, region,
key, keyid, profile)
if _updated:
updated.append(_name)
else:
not_updated.append(_name)
if updated:
msg = 'Updated cnames {0}.'.format(','.join(updated))
if 'comment' in ret:
ret['comment'] = ret['comment'] + ' ' + msg
else:
ret['comment'] = msg
if not_updated:
msg = 'Failed to update cnames {0}.'
msg = msg.format(','.join(not_updated))
if 'comment' in ret:
ret['comment'] = ret['comment'] + ' ' + msg
else:
ret['comment'] = msg
ret['result'] = False
# We can't track old, since we'd need to know the zone to
# search for the ELB in the value.
ret['changes']['new'] = {'cnames': to_create + to_update}
else:
msg = 'cnames already set on ELB {0}.'.format(name)
ret['comment'] = msg
return ret
def _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profile):
'''helper method for present. ensure that cloudwatch_alarms are set'''
# load data from alarms_from_pillar
@ -870,7 +786,7 @@ def _alarms_present(name, alarms, alarms_from_pillar, region, key, keyid, profil
ret = __salt__["state.single"]('boto_cloudwatch_alarm.present', **kwargs)
results = ret.values()[0]
if not results["result"]:
merged_return_value["result"] = False
merged_return_value["result"] = results["result"]
if results.get("changes", {}) != {}:
merged_return_value["changes"][info["name"]] = results["changes"]
if "comment" in results:

View file

@ -39,78 +39,100 @@ class BotoElbTestCase(TestCase):
name = 'myelb'
listeners = [{'elb_port': 'ELBPORT', 'instance_port': 'PORT',
'elb_protocol': 'HTTPS', 'certificate': 'A'}]
attributes = {'alarm_actions': ['arn:aws:sns:us-east-1:12345:myalarm'],
'insufficient_data_actions': [],
'ok_actions': ['arn:aws:sns:us-east-1:12345:myalarm']}
alarms = {'MyAlarm': {'name': name,
'attributes': {'description': 'A'}}}
attrs = {'alarm_actions': ['arn:aws:sns:us-east-1:12345:myalarm'],
'insufficient_data_actions': [],
'ok_actions': ['arn:aws:sns:us-east-1:12345:myalarm']}
health_check = {'target:': 'HTTP:80/'}
avail_zones = ['us-east-1a', 'us-east-1c', 'us-east-1d']
alarms = {'alarm_actions': {'name': name,
'attributes': {'description': 'A'}}}
cnames = [{'name': 'www.test.com', 'zone': 'test.com', 'ttl': 60}]
ret = {'name': name,
'result': False,
'result': True,
'changes': {},
'comment': ''}
ret1 = copy.deepcopy(ret)
mock = MagicMock(return_value={})
mock_bool = MagicMock(return_value=False)
with patch.dict(boto_elb.__salt__,
{'config.option': mock,
'boto_elb.exists': mock_bool,
'boto_elb.create': mock_bool,
'boto_elb.get_attributes': mock}):
with patch.dict(boto_elb.__opts__, {'test': False}):
comt = (' Failed to create myelb ELB.')
ret.update({'comment': comt})
self.assertDictEqual(boto_elb.present
(name, listeners, attributes=attributes,
availability_zones=avail_zones), ret)
mock_false_bool = MagicMock(return_value=False)
mock_true_bool = MagicMock(return_value=True)
mock_attributes = MagicMock(return_value=attrs)
mock_health_check = MagicMock(return_value=health_check)
mock_ret = MagicMock(return_value={'myelb': ret1})
mock = MagicMock(return_value={})
mock_ret = MagicMock(return_value={'result': {'result': False}})
comt1 = (' Failed to retrieve health_check for ELB myelb.')
with patch.dict(boto_elb.__salt__,
{'config.option': mock,
'boto_elb.get_attributes': mock,
'boto_elb.get_health_check': mock,
'boto_elb.exists': mock_false_bool,
'boto_elb.create': mock_false_bool}):
with patch.dict(boto_elb.__opts__, {'test': False}):
ret = boto_elb.present(
name,
listeners,
availability_zones=avail_zones
)
self.assertTrue(boto_elb.__salt__['boto_elb.exists'].called)
self.assertTrue(boto_elb.__salt__['boto_elb.create'].called)
self.assertIn('Failed to create myelb ELB.', ret['comment'])
self.assertFalse(ret['result'])
mock = MagicMock(return_value={})
mock_ret = MagicMock(return_value={'myelb': ret1})
with patch.dict(boto_elb.__salt__,
{'config.option': mock,
'boto_elb.exists': mock_false_bool,
'boto_elb.create': mock_true_bool,
'boto_elb.get_attributes': mock_attributes,
'boto_elb.get_health_check': mock_health_check,
'boto_elb.get_elb_config': mock,
'state.single': mock_ret}):
with patch.dict(boto_elb.__opts__, {'test': False}):
ret1.update({'result': True})
mock_elb_present = MagicMock(return_value=ret1)
with patch.object(boto_elb, '_elb_present', mock_elb_present):
comt = (' Failed to retrieve attributes for ELB myelb.')
ret.update({'comment': comt})
self.assertDictEqual(boto_elb.present
(name, listeners), ret)
ret = boto_elb.present(
name,
listeners,
availability_zones=avail_zones,
health_check=health_check,
alarms=alarms
)
self.assertTrue(boto_elb.__salt__['boto_elb.exists'].called)
self.assertTrue(boto_elb.__salt__['boto_elb.create'].called)
self.assertTrue(boto_elb.__salt__['state.single'].called)
self.assertTrue(
boto_elb.__salt__['boto_elb.get_attributes'].called
)
self.assertTrue(
boto_elb.__salt__['boto_elb.get_health_check'].called
)
self.assertIn('ELB myelb created.', ret['comment'])
self.assertTrue(ret['result'])
with patch.object(boto_elb, '_attributes_present',
mock_elb_present):
ret.update({'comment': comt1})
self.assertDictEqual(boto_elb.present
(name, listeners), ret)
with patch.object(boto_elb, '_health_check_present',
mock_elb_present):
comt = (' Failed to retrieve ELB myelb.')
ret.update({'comment': comt})
self.assertDictEqual(boto_elb.present
(name, listeners), ret)
with patch.object(boto_elb, '_cnames_present',
mock_elb_present):
comt = (' ')
ret.update({'comment': comt})
self.assertDictEqual(boto_elb.present
(name, listeners,
alarms=alarms), ret)
with patch.object(boto_elb, '_alarms_present',
mock_elb_present):
ret.update({'result': True})
self.assertDictEqual(boto_elb.present
(name, listeners,
alarms=alarms), ret)
mock = MagicMock(return_value={})
mock_elb = MagicMock(return_value={'dns_name': 'myelb.amazon.com'})
mock_ret = MagicMock(return_value={'myelb': ret1})
with patch.dict(boto_elb.__salt__,
{'config.option': mock,
'boto_elb.exists': mock_false_bool,
'boto_elb.create': mock_true_bool,
'boto_elb.get_attributes': mock_attributes,
'boto_elb.get_health_check': mock_health_check,
'boto_elb.get_elb_config': mock_elb,
'state.single': mock_ret}):
with patch.dict(boto_elb.__opts__, {'test': False}):
ret = boto_elb.present(
name,
listeners,
availability_zones=avail_zones,
health_check=health_check,
cnames=cnames
)
self.assertTrue(boto_elb.__salt__['state.single'].called)
cname_call = boto_elb.__salt__['state.single'].mock_calls[0]
self.assertEqual(
cname_call[1][0],
'boto_route53.present'
)
self.assertTrue(ret['result'])
# 'absent' function tests: 1