Jose Diaz-Gonzalez
Committed by GitHub

Merge pull request #70 from coopernurse/role-delete-idempotent

Role delete idempotent
......@@ -38,11 +38,13 @@ class Role(object):
Path = '/kappa/'
def __init__(self, context, config):
def __init__(self, context, config, iam_client=None):
self._context = context
self._config = config
self._iam_client = kappa.awsclient.create_client(
'iam', context.session)
self._iam_client = iam_client
if not iam_client:
self._iam_client = kappa.awsclient.create_client(
'iam', context.session)
self._arn = None
@property
......@@ -52,28 +54,28 @@ class Role(object):
@property
def arn(self):
if self._arn is None:
try:
response = self._iam_client.call(
'get_role', RoleName=self.name)
LOG.debug(response)
self._arn = response['Role']['Arn']
except Exception:
role = self._get_role()
if role:
self._arn = role['Arn']
else:
LOG.debug('Unable to find ARN for role: %s', self.name)
return self._arn
def _find_all_roles(self):
def _get_role(self):
try:
response = self._iam_client.call('list_roles')
response = self._iam_client.call('get_role', RoleName=self.name)
if response and 'Role' in response:
response = response['Role']
return response
except ClientError as e:
if e.response['Error']['Code'] != 'NoSuchEntity':
LOG.exception('Error getting role')
except Exception:
LOG.exception('Error listing roles')
response = {}
return response.get('Roles', list())
LOG.exception('Error getting role')
return None
def exists(self):
for role in self._find_all_roles():
if role['RoleName'] == self.name:
return role
return None
return self._get_role() is not None
def create(self):
LOG.info('creating role %s', self.name)
......@@ -99,6 +101,10 @@ class Role(object):
def delete(self):
response = None
if not self.exists():
LOG.debug('role %s does not exist - skipping delete', self.name)
return response
LOG.debug('deleting role %s', self.name)
try:
LOG.debug('First detach the policy from the role')
......@@ -117,11 +123,7 @@ class Role(object):
def status(self):
LOG.debug('getting status for role %s', self.name)
try:
response = self._iam_client.call(
'get_role', RoleName=self.name)
LOG.debug(response)
except ClientError:
role = self._get_role()
if not role:
LOG.debug('role %s not found', self.name)
response = None
return response
return role
......
......@@ -108,8 +108,8 @@ def status(ctx):
click.echo(click.style('Role', bold=True))
if status['role']:
line = ' {} ({})'.format(
status['role']['Role']['RoleName'],
status['role']['Role']['Arn'])
status['role']['RoleName'],
status['role']['Arn'])
click.echo(click.style(line, fg='green'))
click.echo(click.style('Function', bold=True))
if status['function']:
......
# -*- coding: utf-8 -*-
# Copyright (c) 2015 Mitch Garnaat http://garnaat.org/
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import unittest
import random
import string
from mock import Mock, call
from kappa.role import Role
def randomword(length):
return ''.join(random.choice(string.printable) for i in range(length))
class TestRole(unittest.TestCase):
def setUp(self):
self.iam_client = Mock()
policy = type('Policy', (object,), {
'arn': None
})
self.context = type('Context', (object,), {
'name': randomword(10),
'environment': randomword(10),
'policy': policy
})
self.role_record = {
'RoleName': '%s_%s' % (self.context.name,
self.context.environment),
'Arn': randomword(10)
}
self.role = Role(self.context, None, iam_client=self.iam_client)
def _expect_get_role(self):
get_role_resp = {'Role': self.role_record}
self.iam_client.configure_mock(**{
'call.return_value': get_role_resp
})
return get_role_resp
def test_delete_no_ops_if_role_not_found(self):
self.iam_client.configure_mock(**{
'call.return_value': None
})
self.assertEquals(None, self.role.delete())
self.assertEquals(1, self.iam_client.call.call_count)
def test_delete_when_role_exists(self):
get_role_resp = self._expect_get_role()
self.assertEquals(get_role_resp, self.role.delete())
self.iam_client.call.assert_has_calls([call('get_role',
RoleName=self.role.name),
call('delete_role',
RoleName=self.role.name)])
def test_delete_policy_when_context_arn_exists(self):
self.context.policy.arn = randomword(10)
get_role_resp = self._expect_get_role()
self.assertEquals(get_role_resp, self.role.delete())
calls = [call('get_role',
RoleName=self.role.name),
call('detach_role_policy',
RoleName=self.role.name,
PolicyArn=self.context.policy.arn),
call('delete_role',
RoleName=self.role.name)]
self.iam_client.call.assert_has_calls(calls)