diff --git a/CHANGES.rst b/CHANGES.rst index 8d423472..3bc40263 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -22,9 +22,9 @@ or bug reports specific to 3.2 will be closed. and `get_limits `_ methods now take an optional ``service`` *list* keyword argument instead of a *string* for a single service name. -* `PR #251 `_ to handle GovCloud-specific edge cases; specifically, UnsupportedOperation errors +* `PR #251 `_ - Handle GovCloud-specific edge cases; specifically, UnsupportedOperation errors for EC2 Spot Instance-related API calls, and limits returned as 0 by the DescribeAccountAttributes EC2 API action. -* `PR #249 `_ to add support for RedShift limits (Redshift subnet groups and Redshift manual snapshots). +* `PR #249 `_ - Add support for RedShift limits (Redshift subnet groups and Redshift manual snapshots). This requires the ``redshift:DescribeClusterSnapshots`` and ``redshift:DescribeClusterSubnetGroups`` IAM permissions. * `Issue #259 `_ - remove duplicates from required IAM policy returned by ``awslimitchecker.checker.AwsLimitChecker.get_required_iam_policy`` and ``awslimitchecker --iam-policy``. * Various TravisCI/tox build fixes: @@ -34,8 +34,8 @@ or bug reports specific to 3.2 will be closed. * Switch integration3 tox env from py3.4 to py3.6 * `PR #256 `_ - Add example of wrapping awslimitchecker in a script to send metrics to `Prometheus `_. -* `Issue #236 `_ Drop support for Python 3.2; stop testing under py32. - +* `Issue #236 `_ - Drop support for Python 3.2; stop testing under py32. +* `Issue #257 `_ - Handle ElastiCache DescribeCacheCluster responses that are missing ``CacheNodes`` key in a cluster description. 0.7.0 (2017-01-15) ------------------ diff --git a/awslimitchecker/services/elasticache.py b/awslimitchecker/services/elasticache.py index d62be589..e5b1d5a7 100644 --- a/awslimitchecker/services/elasticache.py +++ b/awslimitchecker/services/elasticache.py @@ -79,7 +79,7 @@ def _find_usage_nodes(self): for cluster in page['CacheClusters']: try: num_nodes = len(cluster['CacheNodes']) - except (IndexError, TypeError): + except (IndexError, TypeError, KeyError): # sometimes CacheNodes is None... logger.debug( "Cache Cluster '%s' returned dict with CacheNodes " diff --git a/awslimitchecker/tests/services/result_fixtures.py b/awslimitchecker/tests/services/result_fixtures.py index c69293a9..8bec2824 100644 --- a/awslimitchecker/tests/services/result_fixtures.py +++ b/awslimitchecker/tests/services/result_fixtures.py @@ -1304,6 +1304,39 @@ class ElastiCache(object): 'PreferredMaintenanceWindow': 'mon:05:30-mon:06:30', 'CacheNodes': None, }, + { + 'Engine': 'redis', + 'CacheParameterGroup': { + 'CacheNodeIdsToReboot': [], + 'CacheParameterGroupName': 'default.redis2.8', + 'ParameterApplyStatus': 'in-sync' + }, + 'CacheClusterId': 'redis3', + 'CacheSecurityGroups': [ + { + 'Status': 'active', + 'CacheSecurityGroupName': 'csg-redis2' + } + ], + 'ConfigurationEndpoint': None, + 'CacheClusterCreateTime': 1412253787.123, + 'ReplicationGroupId': None, + 'AutoMinorVersionUpgrade': True, + 'CacheClusterStatus': 'available', + 'NumCacheNodes': 4, + 'PreferredAvailabilityZone': 'us-east-1a', + 'SecurityGroups': None, + 'CacheSubnetGroupName': None, + 'EngineVersion': '2.8.6', + 'PendingModifiedValues': { + 'NumCacheNodes': None, + 'EngineVersion': None, + 'CacheNodeIdsToRemove': None + }, + 'CacheNodeType': 'cache.m3.medium', + 'NotificationConfiguration': None, + 'PreferredMaintenanceWindow': 'mon:05:30-mon:06:30' + }, ], }) diff --git a/awslimitchecker/tests/services/test_elasticache.py b/awslimitchecker/tests/services/test_elasticache.py index 10e04aa5..e76da01f 100644 --- a/awslimitchecker/tests/services/test_elasticache.py +++ b/awslimitchecker/tests/services/test_elasticache.py @@ -134,20 +134,22 @@ def test_find_usage_nodes(self): usage = cls.limits['Nodes'].get_current_usage() assert len(usage) == 1 - assert usage[0].get_value() == 7 + assert usage[0].get_value() == 11 usage = cls.limits['Clusters'].get_current_usage() assert len(usage) == 1 - assert usage[0].get_value() == 3 + assert usage[0].get_value() == 4 usage = sorted(cls.limits['Nodes per Cluster'].get_current_usage()) - assert len(usage) == 3 + assert len(usage) == 4 assert usage[0].get_value() == 1 assert usage[0].resource_id == 'memcached1' assert usage[1].get_value() == 2 assert usage[1].resource_id == 'redis1' assert usage[2].get_value() == 4 assert usage[2].resource_id == 'redis2' + assert usage[3].get_value() == 4 + assert usage[3].resource_id == 'redis3' assert mock_conn.mock_calls == [ call.get_paginator('describe_cache_clusters'),