Skip to content

Commit

Permalink
Fail gracefully when no region is configured
Browse files Browse the repository at this point in the history
If we can't create a client for server side completion,
we should not propogate an exception.  Instead we should
return no server side completion values.

In the future, it would be nice to have some sort of
notification area in the shell where we could let the
user know that server side completion won't work because
they don't have a region configured.

Fixes #84.
  • Loading branch information
jamesls committed Dec 29, 2015
1 parent bdc703a commit f993ac4
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 8 deletions.
19 changes: 16 additions & 3 deletions awsshell/resource/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import jmespath
from botocore import xform_name
from botocore.exceptions import BotoCoreError

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -221,11 +222,21 @@ def retrieve_candidate_values(self, service, operation, param):
# param='InstanceIds'.
if service not in self._describer_creator.services_with_completions():
return []
client = self._client_creator.create_client(service)
try:
client = self._client_creator.create_client(service)
except BotoCoreError as e:
# create_client() could raise an exception if the session
# isn't fully configured (say it's missing a region).
# However, we don't want to turn off all server side
# completions because it's still possible to create
# clients for some services without a region, e.g. IAM.
LOG.debug("Error when trying to create a client for %s",
service, exc_info=True)
return []
api_operation_name = client.meta.method_to_api_mapping.get(
operation.replace('-', '_'))
if api_operation_name is None:
return
return []
# Now we need to convert the param name to the
# casing used by the API.
completer = self._describer_creator.create_completer_query(service)
Expand All @@ -235,7 +246,9 @@ def retrieve_candidate_values(self, service, operation, param):
return
try:
response = getattr(client, xform_name(result.operation, '_'))()
except Exception:
except Exception as e:
LOG.debug("Error when calling %s.%s: %s", service,
result.operation, e, exc_info=True)
return
results = jmespath.search(result.path, response)
return results
Expand Down
53 changes: 48 additions & 5 deletions tests/unit/test_resources.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
"""Index and retrive information from the resource JSON."""
import pytest
import mock

from botocore.exceptions import NoRegionError

from awsshell.resource import index


@pytest.fixture
def describer_creator():
class FakeDescriberCreator(object):
SERVICES = ['ec2']

def services_with_completions(self):
return self.SERVICES

return FakeDescriberCreator()


def test_build_from_has_many():
resource = {
'service': {
Expand Down Expand Up @@ -211,14 +226,42 @@ def services_with_completions(self):
assert factory.create_completer_query('ec2') == result


def test_empty_results_returned_when_no_completion_data_exists():
class FakeDescriberCreator(object):
def services_with_completions(self):
return []
def test_empty_results_returned_when_no_completion_data_exists(describer_creator):
describer_creator.SERVICES = []

completer = index.ServerSideCompleter(
client_creator=None,
describer_creator=FakeDescriberCreator()
describer_creator=describer_creator,
)
assert completer.retrieve_candidate_values(
'ec2', 'run-instances', 'ImageId') == []


def test_no_completions_when_cant_create_client(describer_creator):
client_creator = mock.Mock(spec=index.CachedClientCreator)
# This is raised when you don't have a region configured via config file
# env var or manually via a session.
client_creator.create_client.side_effect = NoRegionError()
completer = index.ServerSideCompleter(
client_creator=client_creator,
describer_creator=describer_creator)

assert completer.retrieve_candidate_values(
'ec2', 'foo', 'Bar') == []


def test_no_completions_returned_on_unknown_operation(describer_creator):
client = mock.Mock()
client_creator = mock.Mock(spec=index.CachedClientCreator)
client_creator.create_client.return_value = client

client.meta.method_to_api_mapping = {
'describe_foo': 'DescribeFoo'
}

completer = index.ServerSideCompleter(
client_creator=client_creator,
describer_creator=describer_creator)

assert completer.retrieve_candidate_values(
'ec2', 'not_describe_foo', 'Bar') == []

0 comments on commit f993ac4

Please sign in to comment.