def setup(self): self.mock_ver_info = Mock(release='1.2.3', url='http://myurl', commit='abcd', tag='mytag', version_str='1.2.3@mytag') self.mock_svc1 = Mock(spec_set=_AwsService) self.mock_svc2 = Mock(spec_set=ApiServiceSpec) self.mock_foo = Mock(spec_set=_AwsService) self.mock_bar = Mock(spec_set=_AwsService) self.mock_ta = Mock(spec_set=TrustedAdvisor) self.mock_foo.return_value = self.mock_svc1 self.mock_bar.return_value = self.mock_svc2 self.svcs = {'SvcFoo': self.mock_foo, 'SvcBar': self.mock_bar} with patch.dict('%s._services' % pbm, values=self.svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, ServiceQuotasClient=DEFAULT, autospec=True, ) as mocks: self.mock_logger = mocks['logger'] self.mock_version = mocks['_get_version_info'] self.mock_ta_constr = mocks['TrustedAdvisor'] self.mock_glv = mocks['_get_latest_version'] self.mock_quotas = mocks['ServiceQuotasClient'] mocks['TrustedAdvisor'].return_value = self.mock_ta mocks['_get_latest_version'].return_value = None self.mock_version.return_value = self.mock_ver_info self.cls = AwsLimitChecker(check_version=False)
def check_limits(self, verbose=False): """ Run the actual usage and limit check, with overrides. see: http://awslimitchecker.readthedocs.org/en/latest/python_usage.html#ci-deployment-checks """ # instantiate the class checker = AwsLimitChecker() # set your overrides checker.set_threshold_overrides(AWS_THRESHOLD_OVERRIDES) checker.set_limit_overrides(AWS_LIMIT_OVERRIDES) print("Checking AWS resource usage; WARNING threshold {w}% of " "limit, CRITICAL threshold {c}% of limit".format( w=checker.warning_threshold, c=checker.critical_threshold)) # check usage against thresholds # if we didn't support verbose output, we could just iterate the return # value of this to be a bit more efficient. checker.check_thresholds() # save state for exit code and summary warnings = [] criticals = [] # iterate the results for service, svc_limits in sorted(checker.get_limits().items()): for limit_name, limit in sorted(svc_limits.items()): have_alarms = False # check warnings and criticals for each Limit for warn in limit.get_warnings(): warnings.append("{service} '{limit_name}' usage " "({u}) exceeds warning threshold " "(limit={l})".format( service=service, limit_name=limit_name, u=str(warn), l=limit.get_limit(), )) have_alarms = True for crit in limit.get_criticals(): criticals.append("{service} '{limit_name}' usage " "({u}) exceeds critical threshold" " (limit={l})".format( service=service, limit_name=limit_name, u=str(crit), l=limit.get_limit(), )) have_alarms = True if not have_alarms and verbose: print( "{service} '{limit_name}' OK: {u} (limit={l})".format( service=service, limit_name=limit_name, u=limit.get_current_usage_str(), l=limit.get_limit())) if verbose: print("\n\n") return (warnings, criticals)
def scrape_limits(event, context): data = base64.b64decode(os.environ.get('CONFIG_DATA_BASE64')) fd = io.StringIO(data.decode('utf-8')) config = yaml.safe_load(fd) if config is None: log.error( f'Unable to read configuration file "{CONFIG_NAME}" exiting...') sys.exit(1) alarm_actions = os.environ.get('ALARM_ACTIONS', "").split(',') alarm_actions = list(filter(lambda a: a != '', alarm_actions)) limits = [] for service in config.get('services', []): for limit in service.get('limits', []): try: limits.append(Limit(service.get('name'), limit)) except InvalidObjectInConfig as e: log.error(e) c = AwsLimitChecker(ta_refresh_mode=config.get('ta_refresh_mode', 21600), ta_refresh_timeout=config.get('ta_refresh_timeout', 1800)) for limit in limits: limit.override(c) c.remove_services(config.get('skip', [])) cwc = CloudWatchClient(METRICS_NAMESPACE) metrics = [] alarms = [] results = c.get_limits() log.info('----------------------------------------------') for service, limits in results.items(): for limit, value in limits.items(): log.info( f'service={service} limit={limit} usage={value.get_current_usage_str()}' ) for usage in value.get_current_usage(): metric = Metric(value, usage) metrics.append(metric) if not value.has_resource_limits(): log.warning(f'Limit {value.name} has no limit') else: alarms.append( Alarm(value, 'warn', metric.dimensions, alarm_actions)) alarms.append( Alarm(value, 'crit', metric.dimensions, alarm_actions)) cwc.put_metric_data(metrics) cwc.put_metric_alarms(alarms) return {'message': 'Done'}
def check_limits(self, acct_id, region_name, role_name=None, limit_overrides={}, threshold_overrides={}): """ Run the actual usage and limit check, with overrides, against a specific account in a specific region, optionally assuming a role in the account and optionally setting limit and/or threshold overrides. Return a 2-tuple of lists, warning strings and critical strings. see: http://awslimitchecker.readthedocs.org/en/latest/python_usage.html :returns: 2-tuple of lists of strings, warnings and criticals """ # instantiate the class if role_name is not None: checker = AwsLimitChecker( account_id=acct_id, region=region_name, account_role=role_name ) else: checker = AwsLimitChecker(region=region_name) # set your overrides if len(threshold_overrides) > 0: checker.set_threshold_overrides(threshold_overrides) if len(limit_overrides) > 0: checker.set_limit_overrides(limit_overrides) # check usage against thresholds checker.check_thresholds() # save state for exit code and summary warnings = [] criticals = [] # iterate the results for service, svc_limits in sorted(checker.get_limits().items()): for limit_name, limit in sorted(svc_limits.items()): # check warnings and criticals for each Limit for warn in limit.get_warnings(): warnings.append(colored("{service} '{limit_name}' usage " "({u}) exceeds warning threshold " "(limit={l})".format( service=service, limit_name=limit_name, u=str(warn), l=limit.get_limit(), ), 'yellow')) for crit in limit.get_criticals(): criticals.append(colored("{service} '{limit_name}' usage " "({u}) exceeds critical threshold" " (limit={l})".format( service=service, limit_name=limit_name, u=str(crit), l=limit.get_limit(), ), 'red')) return warnings, criticals
def setup(self): self.mock_ver_info = Mock( release="1.2.3", url="http://myurl", commit="abcd", tag="mytag", version_str="1.2.3@mytag" ) self.mock_svc1 = Mock(spec_set=_AwsService) self.mock_svc2 = Mock(spec_set=_AwsService) self.mock_foo = Mock(spec_set=_AwsService) self.mock_bar = Mock(spec_set=_AwsService) self.mock_ta = Mock(spec_set=TrustedAdvisor) self.mock_foo.return_value = self.mock_svc1 self.mock_bar.return_value = self.mock_svc2 self.svcs = {"SvcFoo": self.mock_foo, "SvcBar": self.mock_bar} with patch.dict("awslimitchecker.checker._services", values=self.svcs, clear=True): with patch.multiple( "awslimitchecker.checker", logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: self.mock_logger = mocks["logger"] self.mock_version = mocks["_get_version_info"] mocks["TrustedAdvisor"].return_value = self.mock_ta self.mock_version.return_value = self.mock_ver_info self.cls = AwsLimitChecker()
def test_init_sts_external_id_ta_refresh(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch('%s.boto3' % pbm) as mock_boto: mock_boto.client.return_value.assume_role.return_value = { 'Credentials': { 'AccessKeyId': 'akid', 'SecretAccessKey': 'sk', 'SessionToken': 'stoken', 'Expiration': '0' }, 'AssumedRoleUser': { 'AssumedRoleId': 'arid', 'Arn': 'arn' } } with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mocks['TrustedAdvisor'].return_value = mock_ta mocks['_get_latest_version'].return_value = None cls = AwsLimitChecker(account_id='123456789012', account_role='myrole', region='myregion', external_id='myextid', mfa_serial_number=123, mfa_token=456, ta_refresh_mode=123, ta_refresh_timeout=456, role_partition='mypart') # dict should be of _AwsService instances services = {'SvcFoo': mock_svc1, 'SvcBar': mock_svc2} assert cls.services == services assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info assert mock_boto.mock_calls == [ call.client('sts', region_name='myregion'), call.client().assume_role( ExternalId='myextid', RoleArn='arn:mypart:iam::123456789012:role/myrole', RoleSessionName='awslimitchecker', SerialNumber=123, TokenCode=456) ]
def test_init_region_profile(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch('%s.boto3' % pbm) as mock_boto3: with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_boto3.Session.return_value._session = Mock() mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker(region='regionX', profile_name='foo') # dict should be of _AwsService instances services = { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } assert cls.profile_name == 'foo' assert cls.region == 'regionX' assert cls.services == services assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info
def test_boto3_connection_kwargs_profile(self): m_creds = Mock() type(m_creds).access_key = 'ak' type(m_creds).secret_key = 'sk' type(m_creds).token = 'tkn' mock_session = Mock() m_sess = Mock() m_sess.get_credentials.return_value = m_creds type(mock_session)._session = m_sess with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: with patch.dict('%s._services' % pbm, {}, clear=True): mock_sess.return_value = mock_session cls = AwsLimitChecker(profile_name='myprof') mock_get_sts.reset_mock() mock_logger.reset_mock() mock_sess.reset_mock() res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [] assert mock_logger.mock_calls == [ call.debug('Using credentials profile: %s', 'myprof') ] assert mock_sess.mock_calls == [call(profile_name='myprof')] assert res == { 'region_name': None, 'aws_access_key_id': 'ak', 'aws_secret_access_key': 'sk', 'aws_session_token': 'tkn' }
def test_boto3_connection_kwargs_sts(self): mock_creds = Mock() type(mock_creds).access_key = 'sts_ak' type(mock_creds).secret_key = 'sts_sk' type(mock_creds).session_token = 'sts_token' with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: with patch.dict('%s._services' % pbm, {}, clear=True): cls = AwsLimitChecker(account_id='123', account_role='myrole', region='myregion') mock_get_sts.return_value = mock_creds mock_get_sts.reset_mock() mock_logger.reset_mock() mock_sess.reset_mock() res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [call()] assert mock_logger.mock_calls == [ call.debug( "Connecting for account %s role '%s' with STS " "(region: %s)", '123', 'myrole', 'myregion') ] assert mock_sess.mock_calls == [] assert res == { 'region_name': 'myregion', 'aws_access_key_id': 'sts_ak', 'aws_secret_access_key': 'sts_sk', 'aws_session_token': 'sts_token' }
def setup(self): self.mock_ver_info = Mock( release='1.2.3', url='http://myurl', commit='abcd', tag='mytag', version_str='1.2.3@mytag' ) self.mock_svc1 = Mock(spec_set=_AwsService) self.mock_svc2 = Mock(spec_set=ApiServiceSpec) self.mock_foo = Mock(spec_set=_AwsService) self.mock_bar = Mock(spec_set=_AwsService) self.mock_ta = Mock(spec_set=TrustedAdvisor) self.mock_foo.return_value = self.mock_svc1 self.mock_bar.return_value = self.mock_svc2 self.svcs = {'SvcFoo': self.mock_foo, 'SvcBar': self.mock_bar} with patch.dict('awslimitchecker.checker._services', values=self.svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: self.mock_logger = mocks['logger'] self.mock_version = mocks['_get_version_info'] self.mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = self.mock_ta self.mock_version.return_value = self.mock_ver_info self.cls = AwsLimitChecker()
def test_init_sts(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch('%s.boto3' % pbm): with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mocks['TrustedAdvisor'].return_value = mock_ta mocks['_get_latest_version'].return_value = None cls = AwsLimitChecker(account_id='123456789012', account_role='myrole', region='myregion') # dict should be of _AwsService instances services = {'SvcFoo': mock_svc1, 'SvcBar': mock_svc2} assert cls.services == services assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info
def test_init_sts_external_id(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('awslimitchecker.checker._services', values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( account_id='123456789012', account_role='myrole', region='myregion', external_id='myextid', mfa_serial_number=None, mfa_token=None ) # dict should be of _AwsService instances assert cls.services == { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(80, 99, '123456789012', 'myrole', 'myregion', 'myextid', None, None) ] assert mock_bar.mock_calls == [ call(80, 99, '123456789012', 'myrole', 'myregion', 'myextid', None, None) ] assert mock_ta_constr.mock_calls == [ call( account_id='123456789012', account_role='myrole', region='myregion', external_id='myextid', mfa_serial_number=None, mfa_token=None ) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info
def override(self, checker: AwsLimitChecker): log.info(f'Overriding limits for {self}') checker.set_limit_override(self.service_name, self.name, self.value, override_ta=self.override_ta) overrides = [ 'warn_percent', 'crit_percent', 'warn_count', 'crit_count' ] params = {} for field in overrides: value = getattr(self, field) if value is not None: params[field] = value log.info(f'Overriding threshold for {self}') if self.warn_percent is not None and self.crit_percent is not None: checker.set_threshold_override(self.service_name, self.name, **params)
def test_boto3_connection_kwargs(self): cls = AwsLimitChecker() with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [] assert mock_logger.mock_calls == [ call.debug('Connecting to region %s', None) ] assert mock_sess.mock_calls == [] assert res == {'region_name': None}
def build_docs(): """ Trigger rebuild of all documentation that is dynamically generated from awslimitchecker. """ if os.environ.get('CI', None) is not None: print("Not building dynamic docs in CI environment") raise SystemExit(0) logger.info("Beginning build of dynamically-generated docs") logger.info("Instantiating AwsLimitChecker") c = AwsLimitChecker() build_iam_policy(c) build_limits(c) build_runner_examples()
def test_init_thresholds(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('awslimitchecker.checker._services', values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( warning_threshold=5, critical_threshold=22, ) # dict should be of _AwsService instances services = {'SvcFoo': mock_svc1, 'SvcBar': mock_svc2} assert cls.services == services # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(5, 22, None, None, None, None, None, None) ] assert mock_bar.mock_calls == [ call(5, 22, None, None, None, None, None, None) ] assert mock_ta_constr.mock_calls == [ call(services, account_id=None, account_role=None, region=None, external_id=None, mfa_serial_number=None, mfa_token=None) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info
def main(): port = int(os.environ.get('ALC_PORT', '8080')) interval = int(os.environ.get('ALC_INTERVAL', '60')) logger = logging.getLogger() logger.setLevel(logging.ERROR) checkers = {} for region in ['us-east-1', 'us-east-2', 'us-west-1', 'us-west-2']: checkers[region] = AwsLimitChecker(region=region) start_http_server(port) for region, checker in checkers.items(): update(checker, region) time.sleep(interval)
def test_init_thresholds(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, ServiceQuotasClient=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta mocks['_get_latest_version'].return_value = None cls = AwsLimitChecker( warning_threshold=5, critical_threshold=22, ) # dict should be of _AwsService instances services = {'SvcFoo': mock_svc1, 'SvcBar': mock_svc2} assert cls.services == services # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(5, 22, {'region_name': None}, mocks['ServiceQuotasClient'].return_value) ] assert mock_bar.mock_calls == [ call(5, 22, {'region_name': None}, mocks['ServiceQuotasClient'].return_value) ] assert mock_ta_constr.mock_calls == [ call(services, {'region_name': None}, ta_api_region='us-east-1', ta_refresh_mode=None, ta_refresh_timeout=None) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info
def limits_for_ec2(): limit_info = '.. _limits.EC2:\n\n' limit_info += "EC2\n---\n\n" limit_info += dedent(""" As of October 2019, the "standard" EC2 regions use the new `vCPU-based limits <https://aws.amazon.com/blogs/compute/preview-vcpu-based- instance-limits/>`__, while the China (``cn-``) and GovCloud (``us-gov-``) regions still use the old per-instance-type limits. Please see the sections for either :ref:`limits.ec2-standard` or :ref:`limits.ec2-nonvcpu` for details. """) limit_info += '.. _limits.ec2-standard:\n\n' limit_info += "EC2 - Standard Regions\n" limit_info += "----------------------\n" limit_info += "\n" + dedent(""" **Note on On-Demand vs Reserved Instances:** The EC2 limits for "Running On-Demand" EC2 Instances apply only to On-Demand instances, not Reserved Instances. If you list all EC2 instances that are running in the Console or API, you'll get back instances of all types (On-Demand, Reserved, etc.). The value that awslimitchecker reports for Running On-Demand Instances current usage will *not* match the number of instances you see in the Console or API. **Important:** The limits for **Running On-Demand Instances** are now measured in vCPU count per instance family, not instance count per instance type. """) + "\n" limit_info += "\n" limit_info += format_limits_for_service( AwsLimitChecker(region='us-east-1').get_limits()['EC2']) limit_info += '.. _limits.ec2-nonvcpu:\n\n' limit_info += "EC2 - China and GovCloud\n" limit_info += "------------------------\n" limit_info += "\n" + dedent(""" **Note on On-Demand vs Reserved Instances:** The EC2 limits for "Running On-Demand" EC2 Instances apply only to On-Demand instances, not Reserved Instances. If you list all EC2 instances that are running in the Console or API, you'll get back instances of all types (On-Demand, Reserved, etc.). The value that awslimitchecker reports for Running On-Demand Instances current usage will *not* match the number of instances you see in the Console or API. """) + "\n" limit_info += "\n" fname = os.path.join(my_dir, 'source', 'ec2_nonvcpu_limits.txt') with open(fname, 'r') as fh: limit_info += fh.read() limit_info += "\n\n" return limit_info
def test_check_version_not_old(self): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, autospec=True, ) as mocks: mocks['_get_version_info'].return_value = self.mock_ver_info mocks['_get_latest_version'].return_value = None AwsLimitChecker() assert mocks['_get_latest_version'].mock_calls == [call()] assert mocks['logger'].mock_calls == [ call.debug('Connecting to region %s', None) ]
def test_init_region_profile_role_partition_ta_region(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch('%s.boto3' % pbm) as mock_boto3: with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, autospec=True, ) as mocks: mock_boto3.Session.return_value._session = Mock() mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mocks['TrustedAdvisor'].return_value = mock_ta mocks['_get_latest_version'].return_value = None with patch('%s._boto_conn_kwargs' % pb, new_callable=PropertyMock) as m_bck: m_bck.return_value = {'region_name': 'rName'} cls = AwsLimitChecker(region='regionX', profile_name='foo', role_partition='rpName', ta_api_region='taRegion') # dict should be of _AwsService instances services = {'SvcFoo': mock_svc1, 'SvcBar': mock_svc2} assert cls.profile_name == 'foo' assert cls.region == 'regionX' assert cls.services == services assert cls.role_partition == 'rpName' assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info assert mocks['TrustedAdvisor'].mock_calls == [ call(services, {'region_name': 'rName'}, ta_api_region='taRegion', ta_refresh_mode=None, ta_refresh_timeout=None) ]
def build_docs(): """ Trigger rebuild of all documentation that is dynamically generated from awslimitchecker. """ if os.environ.get('CI', None) is not None: print("Not building dynamic docs in CI environment") raise SystemExit(0) region = os.environ.get('AWS_DEFAULT_REGION', None) if region is None: raise SystemExit("ERROR: Please export AWS_DEFAULT_REGION") logger.info("Beginning build of dynamically-generated docs") logger.info("Instantiating AwsLimitChecker") c = AwsLimitChecker(region=region) build_iam_policy(c) build_limits(c) build_runner_examples()
def test_check_version_old(self): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, autospec=True, ) as mocks: mocks['_get_version_info'].return_value = self.mock_ver_info mocks['_get_latest_version'].return_value = '3.4.5' AwsLimitChecker() assert mocks['_get_latest_version'].mock_calls == [call()] assert mocks['logger'].mock_calls == [ call.warning( 'You are running awslimitchecker %s, but the latest version' ' is %s; please consider upgrading.', '1.2.3', '3.4.5'), call.debug('Connecting to region %s', None) ]
class ResourceCheckAwsEc2CloudFormation: """ Class to encapsulate retrieval of EC2 and CloudFormation service quotas, current usage and whether this suffices to run ElasticBLAST """ def __init__(self, boto_cfg = None): """ Initialize this object AwsLimitChecker object. boto_cfg: boto3 library configuration """ self.checker = AwsLimitChecker() if boto_cfg is None else AwsLimitChecker(region=boto_cfg.region_name) def __call__(self): """ Retrieve the current usage of the relevant AWS resources and compare it with the service quotas. Throws a UserReportError if there aren't enough resources available to run ElasticBLAST """ SERVICES = [ 'EC2', 'CloudFormation' ] result = self.checker.check_thresholds(service=SERVICES) if not result: # No service thresholds were exceeded :) return fatal_errors = '' warnings = '' for svc_name in result.keys(): for usage_metric in result[svc_name].keys(): if svc_name == 'EC2' and not usage_metric.startswith('Running On-Demand'): continue aws_limit = result[svc_name][usage_metric] criticals = aws_limit.get_criticals() warnings = aws_limit.get_warnings() if len(criticals): for c in criticals: fatal_errors += f'{svc_name} metric "{usage_metric}" has reached a critical usage level ({c}) that is too close to the limit ({aws_limit.get_limit()}) to run ElasticBLAST. ' elif len(warnings): for w in warnings: warnings += f'{svc_name} metric "{usage_metric}" has reached a level of usage ({w}) that is close to the limit ({aws_limit.get_limit()}) and may run into problems. ' if fatal_errors: raise UserReportError(DEPENDENCY_ERROR, fatal_errors) if warnings: logging.warning(warnings)
def get_aws_limit_checker(): return AwsLimitChecker(region=settings.REGION_NAME, account_id=settings.ACCOUNT_ID, account_role=settings.ACCOUNT_ROLE)
def check_limits(self, verbose=False): """ Run the actual usage and limit check, with overrides. see: http://awslimitchecker.readthedocs.org/en/latest/python_usage.html#ci-deployment-checks """ # instantiate the class checker = AwsLimitChecker() # set your overrides checker.set_threshold_overrides(AWS_THRESHOLD_OVERRIDES) checker.set_limit_overrides(AWS_LIMIT_OVERRIDES) print("Checking AWS resource usage; WARNING threshold {w}% of " "limit, CRITICAL threshold {c}% of limit".format( w=checker.warning_threshold, c=checker.critical_threshold)) # check usage against thresholds # if we didn't support verbose output, we could just iterate the return # value of this to be a bit more efficient. checker.check_thresholds() # save state for exit code and summary warnings = [] criticals = [] # iterate the results for service, svc_limits in sorted(checker.get_limits().items()): for limit_name, limit in sorted(svc_limits.items()): have_alarms = False # check warnings and criticals for each Limit for warn in limit.get_warnings(): warnings.append(colored("{service} '{limit_name}' usage " "({u}) exceeds warning threshold " "(limit={l})".format( service=service, limit_name=limit_name, u=str(warn), l=limit.get_limit(), ), 'yellow')) have_alarms = True for crit in limit.get_criticals(): criticals.append(colored("{service} '{limit_name}' usage " "({u}) exceeds critical threshold" " (limit={l})".format( service=service, limit_name=limit_name, u=str(crit), l=limit.get_limit(), ), 'red')) have_alarms = True if not have_alarms and verbose: print("{service} '{limit_name}' OK: {u} (limit={l})".format( service=service, limit_name=limit_name, u=limit.get_current_usage_str(), l=limit.get_limit() )) if verbose: print("\n\n") return (warnings, criticals)
class TestAwsLimitChecker(object): def setup(self): self.mock_ver_info = Mock( release="1.2.3", url="http://myurl", commit="abcd", tag="mytag", version_str="1.2.3@mytag" ) self.mock_svc1 = Mock(spec_set=_AwsService) self.mock_svc2 = Mock(spec_set=_AwsService) self.mock_foo = Mock(spec_set=_AwsService) self.mock_bar = Mock(spec_set=_AwsService) self.mock_ta = Mock(spec_set=TrustedAdvisor) self.mock_foo.return_value = self.mock_svc1 self.mock_bar.return_value = self.mock_svc2 self.svcs = {"SvcFoo": self.mock_foo, "SvcBar": self.mock_bar} with patch.dict("awslimitchecker.checker._services", values=self.svcs, clear=True): with patch.multiple( "awslimitchecker.checker", logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: self.mock_logger = mocks["logger"] self.mock_version = mocks["_get_version_info"] mocks["TrustedAdvisor"].return_value = self.mock_ta self.mock_version.return_value = self.mock_ver_info self.cls = AwsLimitChecker() def test_init(self): # dict should be of _AwsService instances assert self.cls.services == {"SvcFoo": self.mock_svc1, "SvcBar": self.mock_svc2} # _AwsService instances should exist, but have no other calls assert self.mock_foo.mock_calls == [call(80, 99)] assert self.mock_bar.mock_calls == [call(80, 99)] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [] assert self.cls.ta == self.mock_ta assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_thresholds(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {"SvcFoo": mock_foo, "SvcBar": mock_bar} with patch.dict("awslimitchecker.checker._services", values=svcs, clear=True): with patch.multiple( "awslimitchecker.checker", logger=DEFAULT, _get_version_info=DEFAULT, autospec=True ) as mocks: mock_version = mocks["_get_version_info"] mock_version.return_value = self.mock_ver_info cls = AwsLimitChecker(warning_threshold=5, critical_threshold=22) # dict should be of _AwsService instances assert cls.services == {"SvcFoo": mock_svc1, "SvcBar": mock_svc2} # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [call(5, 22)] assert mock_bar.mock_calls == [call(5, 22)] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_logger(self): """ensure we log a license message""" assert self.mock_logger.mock_calls == [ call.warning( "awslimitchecker %s is AGPL-licensed free software; " "all users have a right to the full source code of " "this version. See <%s>", "1.2.3@mytag", "http://myurl", ) ] def test_get_version(self): with patch("awslimitchecker.checker._get_version_info", spec_set=_get_version_info) as mock_version: self.cls.vinfo = self.mock_ver_info res = self.cls.get_version() assert res == "1.2.3@mytag" assert mock_version.mock_calls == [] def test_get_project_url(self): with patch("awslimitchecker.checker._get_version_info", spec_set=_get_version_info) as mock_version: self.cls.vinfo = self.mock_ver_info res = self.cls.get_project_url() assert res == "http://myurl" assert mock_version.mock_calls == [] def test_get_service_names(self): res = self.cls.get_service_names() assert res == ["SvcBar", "SvcFoo"] def test_get_limits(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits["SvcFoo"] self.mock_svc2.get_limits.return_value = limits["SvcBar"] res = self.cls.get_limits() assert res == limits assert self.mock_ta.mock_calls == [call.update_limits({"SvcFoo": self.mock_svc1, "SvcBar": self.mock_svc2})] def test_get_limits_no_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits["SvcFoo"] self.mock_svc2.get_limits.return_value = limits["SvcBar"] res = self.cls.get_limits(use_ta=False) assert res == limits assert self.mock_ta.mock_calls == [] def test_get_limits_service(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits["SvcFoo"] self.mock_svc2.get_limits.return_value = limits["SvcBar"] res = self.cls.get_limits(service="SvcFoo") assert res == {"SvcFoo": limits["SvcFoo"]} assert self.mock_ta.mock_calls == [call.update_limits({"SvcFoo": self.mock_svc1})] def test_find_usage(self): self.cls.find_usage() assert self.mock_svc1.mock_calls == [call.find_usage()] assert self.mock_svc2.mock_calls == [call.find_usage()] assert self.mock_ta.mock_calls == [call.update_limits({"SvcFoo": self.mock_svc1, "SvcBar": self.mock_svc2})] def test_find_usage_no_ta(self): self.cls.find_usage(use_ta=False) assert self.mock_svc1.mock_calls == [call.find_usage()] assert self.mock_svc2.mock_calls == [call.find_usage()] assert self.mock_ta.mock_calls == [] def test_find_usage_service(self): self.cls.find_usage(service="SvcFoo") assert self.mock_svc1.mock_calls == [call.find_usage()] assert self.mock_svc2.mock_calls == [] assert self.mock_ta.mock_calls == [call.update_limits({"SvcFoo": self.mock_svc1})] def test_set_threshold_overrides(self): limits = sample_limits() limits["SvcFoo"]["zz3"] = AwsLimit("zz3", self.mock_svc1, 1, 2, 3) self.mock_svc1.get_limits.return_value = limits["SvcFoo"] self.mock_svc2.get_limits.return_value = limits["SvcBar"] overrides = { "SvcBar": { "barlimit1": {"warning": {"percent": 10, "count": 12}, "critical": {"percent": 14, "count": 16}}, "bar limit2": {"critical": {"count": 15}}, "zz3": {"warning": {"count": 41}, "critical": {"percent": 52}}, }, "SvcFoo": {"foo limit3": {"warning": {"percent": 91}}}, } self.cls.set_threshold_overrides(overrides) assert self.mock_svc1.mock_calls == [call.set_threshold_override("foo limit3", warn_percent=91)] assert self.mock_svc2.mock_calls == [ call.set_threshold_override("bar limit2", crit_count=15), call.set_threshold_override("barlimit1", warn_percent=10, warn_count=12, crit_percent=14, crit_count=16), call.set_threshold_override("zz3", warn_count=41, crit_percent=52), ] def test_set_limit_overrides(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits["SvcFoo"] self.mock_svc2.get_limits.return_value = limits["SvcBar"] overrides = {"SvcBar": {"barlimit1": 100}, "SvcFoo": {"foo limit3": 99}} self.cls.set_limit_overrides(overrides) assert self.mock_svc1.mock_calls == [call.set_limit_override("foo limit3", 99, override_ta=True)] assert self.mock_svc2.mock_calls == [call.set_limit_override("barlimit1", 100, override_ta=True)] def test_set_limit_overrides_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits["SvcFoo"] self.mock_svc2.get_limits.return_value = limits["SvcBar"] overrides = {"SvcBar": {"bar limit2": 100}, "SvcFoo": {"foo limit3": 3}} self.cls.set_limit_overrides(overrides, override_ta=False) assert self.mock_svc1.mock_calls == [call.set_limit_override("foo limit3", 3, override_ta=False)] assert self.mock_svc2.mock_calls == [call.set_limit_override("bar limit2", 100, override_ta=False)] def test_set_limit_override(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits["SvcFoo"] self.cls.set_limit_override("SvcFoo", "foo limit3", 99) assert self.mock_svc1.mock_calls == [call.set_limit_override("foo limit3", 99, override_ta=True)] def test_set_limit_override_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits["SvcFoo"] self.cls.set_limit_override("SvcFoo", "foo limit3", 99, override_ta=False) assert self.mock_svc1.mock_calls == [call.set_limit_override("foo limit3", 99, override_ta=False)] def test_set_threshold_override(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits["SvcFoo"] self.cls.set_threshold_override( "SvcFoo", "foo limit3", warn_percent=10, warn_count=12, crit_percent=14, crit_count=16 ) assert self.mock_svc1.mock_calls == [ call.set_threshold_override("foo limit3", warn_percent=10, warn_count=12, crit_percent=14, crit_count=16) ] def test_get_required_iam_policy(self): expected = { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Resource": "*", "Action": ["ec2:bar", "ec2:foo", "foo:perm1", "foo:perm2", "support:*", "trustedadvisor:Describe*"], } ], } self.mock_svc1.required_iam_permissions.return_value = ["ec2:foo", "ec2:bar"] self.mock_svc2.required_iam_permissions.return_value = ["foo:perm1", "foo:perm2"] res = self.cls.get_required_iam_policy() assert res == expected assert self.mock_svc1.mock_calls == [call.required_iam_permissions()] assert self.mock_svc2.mock_calls == [call.required_iam_permissions()] def test_check_thresholds(self): self.mock_svc1.check_thresholds.return_value = {"foo": "bar", "baz": "blam"} self.mock_svc2.check_thresholds.return_value = {} res = self.cls.check_thresholds() assert res == {"SvcFoo": {"foo": "bar", "baz": "blam"}} assert self.mock_ta.mock_calls == [call.update_limits({"SvcFoo": self.mock_svc1, "SvcBar": self.mock_svc2})] def test_check_thresholds_service(self): self.mock_svc1.check_thresholds.return_value = {"foo": "bar"} self.mock_svc2.check_thresholds.return_value = {"baz": "blam"} res = self.cls.check_thresholds(service="SvcFoo") assert res == {"SvcFoo": {"foo": "bar"}} assert self.mock_ta.mock_calls == [call.update_limits({"SvcFoo": self.mock_svc1})] def test_check_thresholds_no_ta(self): self.mock_svc1.check_thresholds.return_value = {"foo": "bar", "baz": "blam"} self.mock_svc2.check_thresholds.return_value = {} self.cls.use_ta = False res = self.cls.check_thresholds(use_ta=False) assert res == {"SvcFoo": {"foo": "bar", "baz": "blam"}} assert self.mock_ta.mock_calls == []
def __init__(self, boto_cfg = None): """ Initialize this object AwsLimitChecker object. boto_cfg: boto3 library configuration """ self.checker = AwsLimitChecker() if boto_cfg is None else AwsLimitChecker(region=boto_cfg.region_name)
def verify_limits(self, checker_args, creds, service_name, use_ta, expect_api_source): """ This essentially replicates what's done when awslimitchecker is called from the command line with ``-l``. This replicates some of the internal logic of :py:class:`~awslimitchecker.runner.Runner`. The main purpose is: 1. to allow passing in an existing :py:class:`awslimitchecker.checker.Checker` instance for testing the various authentication options, and, 2. to verify that at least some limits are found This method is largely a duplication of :py:meth:`~awslimitchecker.runner.Runner.list_limits`. :param checker_args: dict of kwargs to pass to :py:class:`awslimitchecker.checker.Checker` constructor :type checker_args: dict :param creds: AWS access key ID and secret key :type creds: tuple :param service_name: the Service name to test limits for; if None, check for all. :type service_name: str :param use_ta: whether or not to use TrustedAdvisor :type use_ta: bool :param expect_api_source: whether or not to expect a limit with an API source :type expect_api_source: bool """ # clear the Connectable credentials Connectable.credentials = None # destroy boto3's session, so it creates a new one boto3.DEFAULT_SESSION = None # set the env vars to the creds we want os.environ['AWS_ACCESS_KEY_ID'] = creds[0] os.environ['AWS_SECRET_ACCESS_KEY'] = creds[1] # this has to be generated inside the method, not in the method that # yields it if 'mfa_token' in checker_args: checker_args['mfa_token'] = self.totp_code(creds[3]) # pytest-capturelog looked good, but won't work with our yielded # test functions, per https://github.com/pytest-dev/pytest/issues/227 with LogCapture() as l: checker = AwsLimitChecker(**checker_args) limits = checker.get_limits(use_ta=use_ta, service=service_name) logs = LogRecordHelper(l) have_api_source = False data = {} for svc in sorted(limits.keys()): for lim in sorted(limits[svc].keys()): src_str = '' if limits[svc][lim].get_limit_source() == SOURCE_API: have_api_source = True src_str = ' (API)' if limits[svc][lim].get_limit_source() == SOURCE_TA: src_str = ' (TA)' data["{s}/{l}".format(s=svc, l=lim)] = '{v}{t}'.format( v=limits[svc][lim].get_limit(), t=src_str) # this is the normal Runner output print(dict2cols(data)) if expect_api_source: assert have_api_source is True # ensure we didn't log anything at WARN or above, except possibly # a TrustedAdvisor subscription required message records = logs.unexpected_logs() assert len(records) == 0, "awslimitchecker emitted unexpected log " \ "messages at WARN or higher: \n%s" % "\n".join(records)
def check_limits(self, acct_id, region_name, role_name=None, limit_overrides={}, threshold_overrides={}): """ Run the actual usage and limit check, with overrides, against a specific account in a specific region, optionally assuming a role in the account and optionally setting limit and/or threshold overrides. Return a 2-tuple of lists, warning strings and critical strings. see: http://awslimitchecker.readthedocs.org/en/latest/python_usage.html :returns: 2-tuple of lists of strings, warnings and criticals """ # instantiate the class if role_name is not None: checker = AwsLimitChecker(account_id=acct_id, region=region_name, account_role=role_name) else: checker = AwsLimitChecker(region=region_name) # set your overrides if len(threshold_overrides) > 0: checker.set_threshold_overrides(threshold_overrides) if len(limit_overrides) > 0: checker.set_limit_overrides(limit_overrides) # check usage against thresholds checker.check_thresholds() # save state for exit code and summary warnings = [] criticals = [] # iterate the results for service, svc_limits in sorted(checker.get_limits().items()): for limit_name, limit in sorted(svc_limits.items()): # check warnings and criticals for each Limit for warn in limit.get_warnings(): warnings.append( colored( "{service} '{limit_name}' usage " "({u}) exceeds warning threshold " "(limit={l})".format( service=service, limit_name=limit_name, u=str(warn), l=limit.get_limit(), ), 'yellow')) for crit in limit.get_criticals(): criticals.append( colored( "{service} '{limit_name}' usage " "({u}) exceeds critical threshold" " (limit={l})".format( service=service, limit_name=limit_name, u=str(crit), l=limit.get_limit(), ), 'red')) return warnings, criticals
class TestAwsLimitChecker(object): def setup(self): self.mock_ver_info = Mock( release='1.2.3', url='http://myurl', commit='abcd', tag='mytag', version_str='1.2.3@mytag' ) self.mock_svc1 = Mock(spec_set=_AwsService) self.mock_svc2 = Mock(spec_set=ApiServiceSpec) self.mock_foo = Mock(spec_set=_AwsService) self.mock_bar = Mock(spec_set=_AwsService) self.mock_ta = Mock(spec_set=TrustedAdvisor) self.mock_foo.return_value = self.mock_svc1 self.mock_bar.return_value = self.mock_svc2 self.svcs = {'SvcFoo': self.mock_foo, 'SvcBar': self.mock_bar} with patch.dict('awslimitchecker.checker._services', values=self.svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: self.mock_logger = mocks['logger'] self.mock_version = mocks['_get_version_info'] self.mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = self.mock_ta self.mock_version.return_value = self.mock_ver_info self.cls = AwsLimitChecker() def test_init(self): # dict should be of _AwsService instances assert self.cls.services == { 'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2 } # _AwsService instances should exist, but have no other calls assert self.mock_foo.mock_calls == [ call(80, 99, None, None, None, None, None, None) ] assert self.mock_bar.mock_calls == [ call(80, 99, None, None, None, None, None, None) ] assert self.mock_ta_constr.mock_calls == [ call(account_id=None, account_role=None, region=None, external_id=None, mfa_serial_number=None, mfa_token=None) ] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [] assert self.cls.ta == self.mock_ta assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info assert self.mock_logger.mock_calls == [] def test_init_AGPL_message(self, capsys): # get rid of the class self.cls = None # clear out/err out, err = capsys.readouterr() # run setup again self.setup() # check out/err out, err = capsys.readouterr() assert out == '' assert (err) == ( "awslimitchecker 1.2.3@mytag is AGPL-licensed free software; " "all users have a right to the full source code of " "this version. See <http://myurl>\n") def test_init_thresholds(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('awslimitchecker.checker._services', values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( warning_threshold=5, critical_threshold=22, ) # dict should be of _AwsService instances assert cls.services == { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(5, 22, None, None, None, None, None, None) ] assert mock_bar.mock_calls == [ call(5, 22, None, None, None, None, None, None) ] assert mock_ta_constr.mock_calls == [ call(account_id=None, account_role=None, region=None, external_id=None, mfa_serial_number=None, mfa_token=None) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_region(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('awslimitchecker.checker._services', values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker(region='myregion') # dict should be of _AwsService instances assert cls.services == { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(80, 99, None, None, 'myregion', None, None, None) ] assert mock_bar.mock_calls == [ call(80, 99, None, None, 'myregion', None, None, None) ] assert mock_ta_constr.mock_calls == [ call(account_id=None, account_role=None, region='myregion', external_id=None, mfa_serial_number=None, mfa_token=None) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_sts(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('awslimitchecker.checker._services', values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( account_id='123456789012', account_role='myrole', region='myregion' ) # dict should be of _AwsService instances assert cls.services == { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(80, 99, '123456789012', 'myrole', 'myregion', None, None, None) ] assert mock_bar.mock_calls == [ call(80, 99, '123456789012', 'myrole', 'myregion', None, None, None) ] assert mock_ta_constr.mock_calls == [ call( account_id='123456789012', account_role='myrole', region='myregion', external_id=None, mfa_serial_number=None, mfa_token=None ) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_sts_external_id(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('awslimitchecker.checker._services', values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( account_id='123456789012', account_role='myrole', region='myregion', external_id='myextid', mfa_serial_number=None, mfa_token=None ) # dict should be of _AwsService instances assert cls.services == { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(80, 99, '123456789012', 'myrole', 'myregion', 'myextid', None, None) ] assert mock_bar.mock_calls == [ call(80, 99, '123456789012', 'myrole', 'myregion', 'myextid', None, None) ] assert mock_ta_constr.mock_calls == [ call( account_id='123456789012', account_role='myrole', region='myregion', external_id='myextid', mfa_serial_number=None, mfa_token=None ) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_get_version(self): with patch('awslimitchecker.checker._get_version_info', spec_set=_get_version_info) as mock_version: self.cls.vinfo = self.mock_ver_info res = self.cls.get_version() assert res == '1.2.3@mytag' assert mock_version.mock_calls == [] def test_get_project_url(self): with patch('awslimitchecker.checker._get_version_info', spec_set=_get_version_info) as mock_version: self.cls.vinfo = self.mock_ver_info res = self.cls.get_project_url() assert res == 'http://myurl' assert mock_version.mock_calls == [] def test_get_service_names(self): res = self.cls.get_service_names() assert res == ['SvcBar', 'SvcFoo'] def test_get_limits(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits() assert res == limits assert self.mock_ta.mock_calls == [ call.update_limits({ 'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2, }) ] assert self.mock_svc1.mock_calls == [ call.get_limits() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.get_limits() ] def test_get_limits_no_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(use_ta=False) assert res == limits assert self.mock_ta.mock_calls == [] assert self.mock_svc1.mock_calls == [ call.get_limits() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.get_limits() ] def test_get_limits_service(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(service='SvcFoo') assert res == {'SvcFoo': limits['SvcFoo']} assert self.mock_ta.mock_calls == [ call.update_limits({ 'SvcFoo': self.mock_svc1, }) ] assert self.mock_svc1.mock_calls == [ call.get_limits() ] assert self.mock_svc2.mock_calls == [] def test_get_limits_service_with_api(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(service='SvcBar') assert res == {'SvcBar': limits['SvcBar']} assert self.mock_ta.mock_calls == [ call.update_limits({ 'SvcBar': self.mock_svc2, }) ] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.get_limits() ] def test_find_usage(self): self.cls.find_usage() assert self.mock_svc1.mock_calls == [ call.find_usage() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.find_usage() ] assert self.mock_ta.mock_calls == [ call.update_limits({ 'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2, }) ] def test_find_usage_no_ta(self): self.cls.find_usage(use_ta=False) assert self.mock_svc1.mock_calls == [ call.find_usage() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.find_usage() ] assert self.mock_ta.mock_calls == [] def test_find_usage_service(self): self.cls.find_usage(service='SvcFoo') assert self.mock_svc1.mock_calls == [ call.find_usage() ] assert self.mock_svc2.mock_calls == [] assert self.mock_ta.mock_calls == [ call.update_limits({'SvcFoo': self.mock_svc1}) ] def test_find_usage_service_with_api(self): self.cls.find_usage(service='SvcBar') assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.find_usage() ] assert self.mock_ta.mock_calls == [ call.update_limits({'SvcBar': self.mock_svc2}) ] def test_set_threshold_overrides(self): limits = sample_limits() limits['SvcFoo']['zz3'] = AwsLimit( 'zz3', self.mock_svc1, 1, 2, 3, ) self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'barlimit1': { 'warning': { 'percent': 10, 'count': 12 }, 'critical': { 'percent': 14, 'count': 16 } }, 'bar limit2': { 'critical': { 'count': 15, } }, 'zz3': { 'warning': { 'count': 41 }, 'critical': { 'percent': 52 } } }, 'SvcFoo': { 'foo limit3': { 'warning': { 'percent': 91 }, } }, } self.cls.set_threshold_overrides(overrides) assert self.mock_svc1.mock_calls == [ call.set_threshold_override( 'foo limit3', warn_percent=91, ) ] assert self.mock_svc2.mock_calls == [ call.set_threshold_override( 'bar limit2', crit_count=15 ), call.set_threshold_override( 'barlimit1', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16 ), call.set_threshold_override( 'zz3', warn_count=41, crit_percent=52 ), ] def test_set_limit_overrides(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'barlimit1': 100, }, 'SvcFoo': { 'foo limit3': 99, }, } self.cls.set_limit_overrides(overrides) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 99, override_ta=True) ] assert self.mock_svc2.mock_calls == [ call.set_limit_override('barlimit1', 100, override_ta=True) ] def test_set_limit_overrides_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'bar limit2': 100, }, 'SvcFoo': { 'foo limit3': 3, }, } self.cls.set_limit_overrides(overrides, override_ta=False) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 3, override_ta=False) ] assert self.mock_svc2.mock_calls == [ call.set_limit_override('bar limit2', 100, override_ta=False) ] def test_set_limit_override(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_limit_override('SvcFoo', 'foo limit3', 99) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 99, override_ta=True) ] def test_set_limit_override_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_limit_override( 'SvcFoo', 'foo limit3', 99, override_ta=False ) assert self.mock_svc1.mock_calls == [ call.set_limit_override( 'foo limit3', 99, override_ta=False ) ] def test_set_threshold_override(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_threshold_override( 'SvcFoo', 'foo limit3', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16 ) assert self.mock_svc1.mock_calls == [ call.set_threshold_override( 'foo limit3', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16 ) ] def test_get_required_iam_policy(self): expected = { 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Resource': '*', 'Action': [ 'ec2:bar', 'ec2:foo', 'foo:perm1', 'foo:perm2', 'support:*', 'trustedadvisor:Describe*' ], }], } self.mock_svc1.required_iam_permissions.return_value = [ 'ec2:foo', 'ec2:bar', ] self.mock_svc2.required_iam_permissions.return_value = [ 'foo:perm1', 'foo:perm2', ] res = self.cls.get_required_iam_policy() assert res == expected assert self.mock_svc1.mock_calls == [call.required_iam_permissions()] assert self.mock_svc2.mock_calls == [call.required_iam_permissions()] def test_check_thresholds(self): self.mock_svc1.check_thresholds.return_value = { 'foo': 'bar', 'baz': 'blam', } self.mock_svc2.check_thresholds.return_value = {} res = self.cls.check_thresholds() assert res == { 'SvcFoo': { 'foo': 'bar', 'baz': 'blam', } } assert self.mock_ta.mock_calls == [ call.update_limits({ 'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2 }), ] assert self.mock_svc1.mock_calls == [ call.check_thresholds() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.check_thresholds() ] def test_check_thresholds_service(self): self.mock_svc1.check_thresholds.return_value = {'foo': 'bar'} self.mock_svc2.check_thresholds.return_value = {'baz': 'blam'} res = self.cls.check_thresholds(service='SvcFoo') assert res == { 'SvcFoo': { 'foo': 'bar', } } assert self.mock_ta.mock_calls == [ call.update_limits({'SvcFoo': self.mock_svc1}) ] assert self.mock_svc1.mock_calls == [ call.check_thresholds() ] assert self.mock_svc2.mock_calls == [] def test_check_thresholds_service_api(self): self.mock_svc1.check_thresholds.return_value = {'foo': 'bar'} self.mock_svc2.check_thresholds.return_value = {'baz': 'blam'} res = self.cls.check_thresholds(service='SvcBar') assert res == { 'SvcBar': { 'baz': 'blam', } } assert self.mock_ta.mock_calls == [ call.update_limits({'SvcBar': self.mock_svc2}) ] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.check_thresholds() ] def test_check_thresholds_no_ta(self): self.mock_svc1.check_thresholds.return_value = { 'foo': 'bar', 'baz': 'blam', } self.mock_svc2.check_thresholds.return_value = {} self.cls.use_ta = False res = self.cls.check_thresholds(use_ta=False) assert res == { 'SvcFoo': { 'foo': 'bar', 'baz': 'blam', } } assert self.mock_ta.mock_calls == [] assert self.mock_svc1.mock_calls == [ call.check_thresholds() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.check_thresholds() ]
class TestAwsLimitChecker(object): def setup(self): self.mock_ver_info = Mock( release='1.2.3', url='http://myurl', commit='abcd', tag='mytag', version_str='1.2.3@mytag' ) self.mock_svc1 = Mock(spec_set=_AwsService) self.mock_svc2 = Mock(spec_set=ApiServiceSpec) self.mock_foo = Mock(spec_set=_AwsService) self.mock_bar = Mock(spec_set=_AwsService) self.mock_ta = Mock(spec_set=TrustedAdvisor) self.mock_foo.return_value = self.mock_svc1 self.mock_bar.return_value = self.mock_svc2 self.svcs = {'SvcFoo': self.mock_foo, 'SvcBar': self.mock_bar} with patch.dict('%s._services' % pbm, values=self.svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: self.mock_logger = mocks['logger'] self.mock_version = mocks['_get_version_info'] self.mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = self.mock_ta self.mock_version.return_value = self.mock_ver_info self.cls = AwsLimitChecker() def test_init(self): # dict should be of _AwsService instances services = { 'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2 } assert self.cls.services == services # _AwsService instances should exist, but have no other calls assert self.mock_foo.mock_calls == [ call(80, 99, {'region_name': None}) ] assert self.mock_bar.mock_calls == [ call(80, 99, {'region_name': None}) ] assert self.mock_ta_constr.mock_calls == [ call(services, {'region_name': None}, ta_refresh_mode=None, ta_refresh_timeout=None) ] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [] assert self.cls.ta == self.mock_ta assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info assert self.mock_logger.mock_calls == [ call.debug('Connecting to region %s', None) ] def test_init_AGPL_message(self, capsys): # get rid of the class self.cls = None # clear out/err out, err = capsys.readouterr() # run setup again self.setup() # check out/err out, err = capsys.readouterr() assert out == '' assert (err) == ( "awslimitchecker 1.2.3@mytag is AGPL-licensed free software; " "all users have a right to the full source code of " "this version. See <http://myurl>\n") def test_init_thresholds(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( warning_threshold=5, critical_threshold=22, ) # dict should be of _AwsService instances services = { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } assert cls.services == services # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(5, 22, {'region_name': None}) ] assert mock_bar.mock_calls == [ call(5, 22, {'region_name': None}) ] assert mock_ta_constr.mock_calls == [ call(services, {'region_name': None}, ta_refresh_mode=None, ta_refresh_timeout=None) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_region_profile(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch('%s.boto3' % pbm) as mock_boto3: with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_boto3.Session.return_value._session = Mock() mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker(region='regionX', profile_name='foo') # dict should be of _AwsService instances services = { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } assert cls.profile_name == 'foo' assert cls.region == 'regionX' assert cls.services == services assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_sts(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch('%s.boto3' % pbm): with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( account_id='123456789012', account_role='myrole', region='myregion' ) # dict should be of _AwsService instances services = { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } assert cls.services == services assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_sts_external_id_ta_refresh(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch('%s.boto3' % pbm): with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( account_id='123456789012', account_role='myrole', region='myregion', external_id='myextid', mfa_serial_number=123, mfa_token=456, ta_refresh_mode=123, ta_refresh_timeout=456 ) # dict should be of _AwsService instances services = { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } assert cls.services == services assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_boto3_connection_kwargs(self): cls = AwsLimitChecker() with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [] assert mock_logger.mock_calls == [ call.debug('Connecting to region %s', None) ] assert mock_sess.mock_calls == [] assert res == { 'region_name': None } def test_boto3_connection_kwargs_profile(self): with patch('%s.boto3' % pbm): cls = AwsLimitChecker(profile_name='myprof') m_creds = Mock() type(m_creds).access_key = 'ak' type(m_creds).secret_key = 'sk' type(m_creds).token = 'tkn' mock_session = Mock() m_sess = Mock() m_sess.get_credentials.return_value = m_creds type(mock_session)._session = m_sess with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: mock_sess.return_value = mock_session res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [] assert mock_logger.mock_calls == [ call.debug('Using credentials profile: %s', 'myprof') ] assert mock_sess.mock_calls == [call(profile_name='myprof')] assert res == { 'region_name': None, 'aws_access_key_id': 'ak', 'aws_secret_access_key': 'sk', 'aws_session_token': 'tkn' } def test_boto3_connection_kwargs_region(self): with patch('%s.boto3' % pbm): cls = AwsLimitChecker(region='myregion') with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [] assert mock_logger.mock_calls == [ call.debug('Connecting to region %s', 'myregion') ] assert mock_sess.mock_calls == [] assert res == { 'region_name': 'myregion' } def test_boto3_connection_kwargs_sts(self): with patch('%s.boto3' % pbm): cls = AwsLimitChecker(account_id='123', account_role='myrole', region='myregion') mock_creds = Mock() type(mock_creds).access_key = 'sts_ak' type(mock_creds).secret_key = 'sts_sk' type(mock_creds).session_token = 'sts_token' with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: mock_get_sts.return_value = mock_creds res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [call()] assert mock_logger.mock_calls == [ call.debug("Connecting for account %s role '%s' with STS " "(region: %s)", '123', 'myrole', 'myregion') ] assert mock_sess.mock_calls == [] assert res == { 'region_name': 'myregion', 'aws_access_key_id': 'sts_ak', 'aws_secret_access_key': 'sts_sk', 'aws_session_token': 'sts_token' } def test_get_version(self): with patch('%s._get_version_info' % pbm, spec_set=_get_version_info) as mock_version: self.cls.vinfo = self.mock_ver_info res = self.cls.get_version() assert res == '1.2.3@mytag' assert mock_version.mock_calls == [] def test_get_project_url(self): with patch('%s._get_version_info' % pbm, spec_set=_get_version_info) as mock_version: self.cls.vinfo = self.mock_ver_info res = self.cls.get_project_url() assert res == 'http://myurl' assert mock_version.mock_calls == [] def test_get_service_names(self): res = self.cls.get_service_names() assert res == ['SvcBar', 'SvcFoo'] def test_get_limits(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits() assert res == limits assert self.mock_ta.mock_calls == [ call.update_limits() ] assert self.mock_svc1.mock_calls == [ call.get_limits() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.get_limits() ] def test_get_limits_no_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(use_ta=False) assert res == limits assert self.mock_ta.mock_calls == [] assert self.mock_svc1.mock_calls == [ call.get_limits() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.get_limits() ] def test_get_limits_service(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(service=['SvcFoo']) assert res == {'SvcFoo': limits['SvcFoo']} assert self.mock_ta.mock_calls == [ call.update_limits() ] assert self.mock_svc1.mock_calls == [ call.get_limits() ] assert self.mock_svc2.mock_calls == [] def test_get_limits_service_with_api(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(service=['SvcBar']) assert res == {'SvcBar': limits['SvcBar']} assert self.mock_ta.mock_calls == [ call.update_limits() ] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.get_limits() ] def test_find_usage(self): self.cls.find_usage() assert self.mock_svc1.mock_calls == [ call.find_usage() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.find_usage() ] assert self.mock_ta.mock_calls == [ call.update_limits() ] def test_find_usage_no_ta(self): self.cls.find_usage(use_ta=False) assert self.mock_svc1.mock_calls == [ call.find_usage() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.find_usage() ] assert self.mock_ta.mock_calls == [] def test_find_usage_service(self): self.cls.find_usage(service=['SvcFoo']) assert self.mock_svc1.mock_calls == [ call.find_usage() ] assert self.mock_svc2.mock_calls == [] assert self.mock_ta.mock_calls == [ call.update_limits() ] def test_find_usage_service_with_api(self): self.cls.find_usage(service=['SvcBar']) assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.find_usage() ] assert self.mock_ta.mock_calls == [ call.update_limits() ] def test_set_threshold_overrides(self): limits = sample_limits() limits['SvcFoo']['zz3'] = AwsLimit( 'zz3', self.mock_svc1, 1, 2, 3, ) self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'barlimit1': { 'warning': { 'percent': 10, 'count': 12 }, 'critical': { 'percent': 14, 'count': 16 } }, 'bar limit2': { 'critical': { 'count': 15, } }, 'zz3': { 'warning': { 'count': 41 }, 'critical': { 'percent': 52 } } }, 'SvcFoo': { 'foo limit3': { 'warning': { 'percent': 91 }, } }, } self.cls.set_threshold_overrides(overrides) assert self.mock_svc1.mock_calls == [ call.set_threshold_override( 'foo limit3', warn_percent=91, ) ] assert self.mock_svc2.mock_calls == [ call.set_threshold_override( 'bar limit2', crit_count=15 ), call.set_threshold_override( 'barlimit1', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16 ), call.set_threshold_override( 'zz3', warn_count=41, crit_percent=52 ), ] def test_set_limit_overrides(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'barlimit1': 100, }, 'SvcFoo': { 'foo limit3': 99, }, } self.cls.set_limit_overrides(overrides) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 99, override_ta=True) ] assert self.mock_svc2.mock_calls == [ call.set_limit_override('barlimit1', 100, override_ta=True) ] def test_set_limit_overrides_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'bar limit2': 100, }, 'SvcFoo': { 'foo limit3': 3, }, } self.cls.set_limit_overrides(overrides, override_ta=False) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 3, override_ta=False) ] assert self.mock_svc2.mock_calls == [ call.set_limit_override('bar limit2', 100, override_ta=False) ] def test_set_limit_override(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_limit_override('SvcFoo', 'foo limit3', 99) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 99, override_ta=True) ] def test_set_limit_override_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_limit_override( 'SvcFoo', 'foo limit3', 99, override_ta=False ) assert self.mock_svc1.mock_calls == [ call.set_limit_override( 'foo limit3', 99, override_ta=False ) ] def test_set_threshold_override(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_threshold_override( 'SvcFoo', 'foo limit3', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16 ) assert self.mock_svc1.mock_calls == [ call.set_threshold_override( 'foo limit3', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16 ) ] def test_get_required_iam_policy(self): expected = { 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Resource': '*', 'Action': [ 'ec2:bar', 'ec2:foo', 'foo:perm1', 'foo:perm2', 'support:*', 'trustedadvisor:Describe*', 'trustedadvisor:RefreshCheck' ], }], } self.mock_svc1.required_iam_permissions.return_value = [ 'ec2:foo', 'ec2:bar', 'foo:perm1' ] self.mock_svc2.required_iam_permissions.return_value = [ 'foo:perm1', 'foo:perm2', ] res = self.cls.get_required_iam_policy() assert res == expected assert self.mock_svc1.mock_calls == [call.required_iam_permissions()] assert self.mock_svc2.mock_calls == [call.required_iam_permissions()] def test_check_thresholds(self): self.mock_svc1.check_thresholds.return_value = { 'foo': 'bar', 'baz': 'blam', } self.mock_svc2.check_thresholds.return_value = {} res = self.cls.check_thresholds() assert res == { 'SvcFoo': { 'foo': 'bar', 'baz': 'blam', } } assert self.mock_ta.mock_calls == [ call.update_limits(), ] assert self.mock_svc1.mock_calls == [ call.check_thresholds() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.check_thresholds() ] def test_check_thresholds_service(self): self.mock_svc1.check_thresholds.return_value = {'foo': 'bar'} self.mock_svc2.check_thresholds.return_value = {'baz': 'blam'} res = self.cls.check_thresholds(service=['SvcFoo']) assert res == { 'SvcFoo': { 'foo': 'bar', } } assert self.mock_ta.mock_calls == [ call.update_limits() ] assert self.mock_svc1.mock_calls == [ call.check_thresholds() ] assert self.mock_svc2.mock_calls == [] def test_check_thresholds_service_api(self): self.mock_svc1.check_thresholds.return_value = {'foo': 'bar'} self.mock_svc2.check_thresholds.return_value = {'baz': 'blam'} res = self.cls.check_thresholds(service=['SvcBar']) assert res == { 'SvcBar': { 'baz': 'blam', } } assert self.mock_ta.mock_calls == [ call.update_limits() ] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.check_thresholds() ] def test_check_thresholds_no_ta(self): self.mock_svc1.check_thresholds.return_value = { 'foo': 'bar', 'baz': 'blam', } self.mock_svc2.check_thresholds.return_value = {} self.cls.use_ta = False res = self.cls.check_thresholds(use_ta=False) assert res == { 'SvcFoo': { 'foo': 'bar', 'baz': 'blam', } } assert self.mock_ta.mock_calls == [] assert self.mock_svc1.mock_calls == [ call.check_thresholds() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.check_thresholds() ]
logger = logging.getLogger('AWSlimitchecker') hdlr = logging.FileHandler( '/opt/zabbix/externalscripts/externalscriptsLogs/AWSlimitchecker/AWSlimitchecker.log' ) formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') hdlr.setFormatter(formatter) logger.addHandler(hdlr) logger.setLevel(logging.INFO) #parameter for the initiator script# # You can find the Project AwsLimitChecker in this link: https://github.com/jantman/awslimitchecker region = args.myRegionVariable print region region = 'ca-central-1' checker = AwsLimitChecker(region=region) if region in problems_regions_dict.keys(): limit_path = str(problems_regions_dict[region]) + str(auto_complete) with open('//opt//zabbix//externalscripts//AWSlimitchecker//%s' % limit_path) as limit_conf: problems_regions_dict[region] = json.load(limit_conf) try: checker.find_usage() except: pass #print args.myRegionVariable #print args.myZabbixHost for aws_module, svc_limits in sorted(checker.get_limits().items()): for limit_name, limit in sorted(svc_limits.items()): if 'Firehose' not in aws_module: try:
class TestAwsLimitChecker(object): def setup(self): self.mock_ver_info = Mock( release='1.2.3', url='http://myurl', commit='abcd', tag='mytag', version_str='1.2.3@mytag' ) self.mock_svc1 = Mock(spec_set=_AwsService) self.mock_svc2 = Mock(spec_set=ApiServiceSpec) self.mock_foo = Mock(spec_set=_AwsService) self.mock_bar = Mock(spec_set=_AwsService) self.mock_ta = Mock(spec_set=TrustedAdvisor) self.mock_foo.return_value = self.mock_svc1 self.mock_bar.return_value = self.mock_svc2 self.svcs = {'SvcFoo': self.mock_foo, 'SvcBar': self.mock_bar} with patch.dict('awslimitchecker.checker._services', values=self.svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: self.mock_logger = mocks['logger'] self.mock_version = mocks['_get_version_info'] self.mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = self.mock_ta self.mock_version.return_value = self.mock_ver_info self.cls = AwsLimitChecker() def test_init(self): # dict should be of _AwsService instances assert self.cls.services == { 'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2 } # _AwsService instances should exist, but have no other calls assert self.mock_foo.mock_calls == [ call(80, 99, None, None, None, None) ] assert self.mock_bar.mock_calls == [ call(80, 99, None, None, None, None) ] assert self.mock_ta_constr.mock_calls == [ call(account_id=None, account_role=None, region=None, external_id=None) ] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [] assert self.cls.ta == self.mock_ta assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info assert self.mock_logger.mock_calls == [] def test_init_AGPL_message(self, capsys): # get rid of the class self.cls = None # clear out/err out, err = capsys.readouterr() # run setup again self.setup() # check out/err out, err = capsys.readouterr() assert out == '' assert (err) == ( "awslimitchecker 1.2.3@mytag is AGPL-licensed free software; " "all users have a right to the full source code of " "this version. See <http://myurl>\n") def test_init_thresholds(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('awslimitchecker.checker._services', values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( warning_threshold=5, critical_threshold=22, ) # dict should be of _AwsService instances assert cls.services == { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [call(5, 22, None, None, None, None)] assert mock_bar.mock_calls == [call(5, 22, None, None, None, None)] assert mock_ta_constr.mock_calls == [ call(account_id=None, account_role=None, region=None, external_id=None) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_region(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('awslimitchecker.checker._services', values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker(region='myregion') # dict should be of _AwsService instances assert cls.services == { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(80, 99, None, None, 'myregion', None) ] assert mock_bar.mock_calls == [ call(80, 99, None, None, 'myregion', None) ] assert mock_ta_constr.mock_calls == [ call(account_id=None, account_role=None, region='myregion', external_id=None) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_sts(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('awslimitchecker.checker._services', values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( account_id='123456789012', account_role='myrole', region='myregion' ) # dict should be of _AwsService instances assert cls.services == { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(80, 99, '123456789012', 'myrole', 'myregion', None) ] assert mock_bar.mock_calls == [ call(80, 99, '123456789012', 'myrole', 'myregion', None) ] assert mock_ta_constr.mock_calls == [ call( account_id='123456789012', account_role='myrole', region='myregion', external_id=None ) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_sts_external_id(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('awslimitchecker.checker._services', values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta cls = AwsLimitChecker( account_id='123456789012', account_role='myrole', region='myregion', external_id='myextid' ) # dict should be of _AwsService instances assert cls.services == { 'SvcFoo': mock_svc1, 'SvcBar': mock_svc2 } # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(80, 99, '123456789012', 'myrole', 'myregion', 'myextid') ] assert mock_bar.mock_calls == [ call(80, 99, '123456789012', 'myrole', 'myregion', 'myextid') ] assert mock_ta_constr.mock_calls == [ call( account_id='123456789012', account_role='myrole', region='myregion', external_id='myextid' ) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_get_version(self): with patch('awslimitchecker.checker._get_version_info', spec_set=_get_version_info) as mock_version: self.cls.vinfo = self.mock_ver_info res = self.cls.get_version() assert res == '1.2.3@mytag' assert mock_version.mock_calls == [] def test_get_project_url(self): with patch('awslimitchecker.checker._get_version_info', spec_set=_get_version_info) as mock_version: self.cls.vinfo = self.mock_ver_info res = self.cls.get_project_url() assert res == 'http://myurl' assert mock_version.mock_calls == [] def test_get_service_names(self): res = self.cls.get_service_names() assert res == ['SvcBar', 'SvcFoo'] def test_get_limits(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits() assert res == limits assert self.mock_ta.mock_calls == [ call.update_limits({ 'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2, }) ] assert self.mock_svc1.mock_calls == [ call.get_limits() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.get_limits() ] def test_get_limits_no_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(use_ta=False) assert res == limits assert self.mock_ta.mock_calls == [] assert self.mock_svc1.mock_calls == [ call.get_limits() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.get_limits() ] def test_get_limits_service(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(service='SvcFoo') assert res == {'SvcFoo': limits['SvcFoo']} assert self.mock_ta.mock_calls == [ call.update_limits({ 'SvcFoo': self.mock_svc1, }) ] assert self.mock_svc1.mock_calls == [ call.get_limits() ] assert self.mock_svc2.mock_calls == [] def test_get_limits_service_with_api(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(service='SvcBar') assert res == {'SvcBar': limits['SvcBar']} assert self.mock_ta.mock_calls == [ call.update_limits({ 'SvcBar': self.mock_svc2, }) ] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.get_limits() ] def test_find_usage(self): self.cls.find_usage() assert self.mock_svc1.mock_calls == [ call.find_usage() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.find_usage() ] assert self.mock_ta.mock_calls == [ call.update_limits({ 'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2, }) ] def test_find_usage_no_ta(self): self.cls.find_usage(use_ta=False) assert self.mock_svc1.mock_calls == [ call.find_usage() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.find_usage() ] assert self.mock_ta.mock_calls == [] def test_find_usage_service(self): self.cls.find_usage(service='SvcFoo') assert self.mock_svc1.mock_calls == [ call.find_usage() ] assert self.mock_svc2.mock_calls == [] assert self.mock_ta.mock_calls == [ call.update_limits({'SvcFoo': self.mock_svc1}) ] def test_find_usage_service_with_api(self): self.cls.find_usage(service='SvcBar') assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.find_usage() ] assert self.mock_ta.mock_calls == [ call.update_limits({'SvcBar': self.mock_svc2}) ] def test_set_threshold_overrides(self): limits = sample_limits() limits['SvcFoo']['zz3'] = AwsLimit( 'zz3', self.mock_svc1, 1, 2, 3, ) self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'barlimit1': { 'warning': { 'percent': 10, 'count': 12 }, 'critical': { 'percent': 14, 'count': 16 } }, 'bar limit2': { 'critical': { 'count': 15, } }, 'zz3': { 'warning': { 'count': 41 }, 'critical': { 'percent': 52 } } }, 'SvcFoo': { 'foo limit3': { 'warning': { 'percent': 91 }, } }, } self.cls.set_threshold_overrides(overrides) assert self.mock_svc1.mock_calls == [ call.set_threshold_override( 'foo limit3', warn_percent=91, ) ] assert self.mock_svc2.mock_calls == [ call.set_threshold_override( 'bar limit2', crit_count=15 ), call.set_threshold_override( 'barlimit1', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16 ), call.set_threshold_override( 'zz3', warn_count=41, crit_percent=52 ), ] def test_set_limit_overrides(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'barlimit1': 100, }, 'SvcFoo': { 'foo limit3': 99, }, } self.cls.set_limit_overrides(overrides) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 99, override_ta=True) ] assert self.mock_svc2.mock_calls == [ call.set_limit_override('barlimit1', 100, override_ta=True) ] def test_set_limit_overrides_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'bar limit2': 100, }, 'SvcFoo': { 'foo limit3': 3, }, } self.cls.set_limit_overrides(overrides, override_ta=False) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 3, override_ta=False) ] assert self.mock_svc2.mock_calls == [ call.set_limit_override('bar limit2', 100, override_ta=False) ] def test_set_limit_override(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_limit_override('SvcFoo', 'foo limit3', 99) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 99, override_ta=True) ] def test_set_limit_override_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_limit_override( 'SvcFoo', 'foo limit3', 99, override_ta=False ) assert self.mock_svc1.mock_calls == [ call.set_limit_override( 'foo limit3', 99, override_ta=False ) ] def test_set_threshold_override(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_threshold_override( 'SvcFoo', 'foo limit3', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16 ) assert self.mock_svc1.mock_calls == [ call.set_threshold_override( 'foo limit3', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16 ) ] def test_get_required_iam_policy(self): expected = { 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Resource': '*', 'Action': [ 'ec2:bar', 'ec2:foo', 'foo:perm1', 'foo:perm2', 'support:*', 'trustedadvisor:Describe*' ], }], } self.mock_svc1.required_iam_permissions.return_value = [ 'ec2:foo', 'ec2:bar', ] self.mock_svc2.required_iam_permissions.return_value = [ 'foo:perm1', 'foo:perm2', ] res = self.cls.get_required_iam_policy() assert res == expected assert self.mock_svc1.mock_calls == [call.required_iam_permissions()] assert self.mock_svc2.mock_calls == [call.required_iam_permissions()] def test_check_thresholds(self): self.mock_svc1.check_thresholds.return_value = { 'foo': 'bar', 'baz': 'blam', } self.mock_svc2.check_thresholds.return_value = {} res = self.cls.check_thresholds() assert res == { 'SvcFoo': { 'foo': 'bar', 'baz': 'blam', } } assert self.mock_ta.mock_calls == [ call.update_limits({ 'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2 }), ] assert self.mock_svc1.mock_calls == [ call.check_thresholds() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.check_thresholds() ] def test_check_thresholds_service(self): self.mock_svc1.check_thresholds.return_value = {'foo': 'bar'} self.mock_svc2.check_thresholds.return_value = {'baz': 'blam'} res = self.cls.check_thresholds(service='SvcFoo') assert res == { 'SvcFoo': { 'foo': 'bar', } } assert self.mock_ta.mock_calls == [ call.update_limits({'SvcFoo': self.mock_svc1}) ] assert self.mock_svc1.mock_calls == [ call.check_thresholds() ] assert self.mock_svc2.mock_calls == [] def test_check_thresholds_service_api(self): self.mock_svc1.check_thresholds.return_value = {'foo': 'bar'} self.mock_svc2.check_thresholds.return_value = {'baz': 'blam'} res = self.cls.check_thresholds(service='SvcBar') assert res == { 'SvcBar': { 'baz': 'blam', } } assert self.mock_ta.mock_calls == [ call.update_limits({'SvcBar': self.mock_svc2}) ] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.check_thresholds() ] def test_check_thresholds_no_ta(self): self.mock_svc1.check_thresholds.return_value = { 'foo': 'bar', 'baz': 'blam', } self.mock_svc2.check_thresholds.return_value = {} self.cls.use_ta = False res = self.cls.check_thresholds(use_ta=False) assert res == { 'SvcFoo': { 'foo': 'bar', 'baz': 'blam', } } assert self.mock_ta.mock_calls == [] assert self.mock_svc1.mock_calls == [ call.check_thresholds() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call.check_thresholds() ]
class TestAwsLimitChecker(object): def setup(self): self.mock_ver_info = Mock(release='1.2.3', url='http://myurl', commit='abcd', tag='mytag', version_str='1.2.3@mytag') self.mock_svc1 = Mock(spec_set=_AwsService) self.mock_svc2 = Mock(spec_set=ApiServiceSpec) self.mock_foo = Mock(spec_set=_AwsService) self.mock_bar = Mock(spec_set=_AwsService) self.mock_ta = Mock(spec_set=TrustedAdvisor) self.mock_foo.return_value = self.mock_svc1 self.mock_bar.return_value = self.mock_svc2 self.svcs = {'SvcFoo': self.mock_foo, 'SvcBar': self.mock_bar} with patch.dict('%s._services' % pbm, values=self.svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, ServiceQuotasClient=DEFAULT, autospec=True, ) as mocks: self.mock_logger = mocks['logger'] self.mock_version = mocks['_get_version_info'] self.mock_ta_constr = mocks['TrustedAdvisor'] self.mock_glv = mocks['_get_latest_version'] self.mock_quotas = mocks['ServiceQuotasClient'] mocks['TrustedAdvisor'].return_value = self.mock_ta mocks['_get_latest_version'].return_value = None self.mock_version.return_value = self.mock_ver_info self.cls = AwsLimitChecker(check_version=False) def test_init(self): # dict should be of _AwsService instances services = {'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2} assert self.cls.services == services # _AwsService instances should exist, but have no other calls assert self.mock_foo.mock_calls == [ call(80, 99, {'region_name': None}, self.mock_quotas.return_value) ] assert self.mock_bar.mock_calls == [ call(80, 99, {'region_name': None}, self.mock_quotas.return_value) ] assert self.mock_ta_constr.mock_calls == [ call(services, {'region_name': None}, ta_api_region='us-east-1', ta_refresh_mode=None, ta_refresh_timeout=None) ] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [] assert self.cls.ta == self.mock_ta assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info assert self.mock_glv.mock_calls == [] assert self.mock_logger.mock_calls == [ call.debug('Connecting to region %s', None) ] assert self.cls.role_partition == 'aws' assert self.mock_quotas.mock_calls == [call({'region_name': None})] def test_init_AGPL_message(self, capsys): # get rid of the class self.cls = None # clear out/err out, err = capsys.readouterr() # run setup again self.setup() # check out/err out, err = capsys.readouterr() assert out == '' assert (err) == ( "awslimitchecker 1.2.3@mytag is AGPL-licensed free software; " "all users have a right to the full source code of " "this version. See <http://myurl>\n") def test_check_version_old(self): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, autospec=True, ) as mocks: mocks['_get_version_info'].return_value = self.mock_ver_info mocks['_get_latest_version'].return_value = '3.4.5' AwsLimitChecker() assert mocks['_get_latest_version'].mock_calls == [call()] assert mocks['logger'].mock_calls == [ call.warning( 'You are running awslimitchecker %s, but the latest version' ' is %s; please consider upgrading.', '1.2.3', '3.4.5'), call.debug('Connecting to region %s', None) ] def test_check_version_not_old(self): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, autospec=True, ) as mocks: mocks['_get_version_info'].return_value = self.mock_ver_info mocks['_get_latest_version'].return_value = None AwsLimitChecker() assert mocks['_get_latest_version'].mock_calls == [call()] assert mocks['logger'].mock_calls == [ call.debug('Connecting to region %s', None) ] def test_init_thresholds(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, ServiceQuotasClient=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mock_ta_constr = mocks['TrustedAdvisor'] mocks['TrustedAdvisor'].return_value = mock_ta mocks['_get_latest_version'].return_value = None cls = AwsLimitChecker( warning_threshold=5, critical_threshold=22, ) # dict should be of _AwsService instances services = {'SvcFoo': mock_svc1, 'SvcBar': mock_svc2} assert cls.services == services # _AwsService instances should exist, but have no other calls assert mock_foo.mock_calls == [ call(5, 22, {'region_name': None}, mocks['ServiceQuotasClient'].return_value) ] assert mock_bar.mock_calls == [ call(5, 22, {'region_name': None}, mocks['ServiceQuotasClient'].return_value) ] assert mock_ta_constr.mock_calls == [ call(services, {'region_name': None}, ta_api_region='us-east-1', ta_refresh_mode=None, ta_refresh_timeout=None) ] assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info def test_init_region_profile_role_partition_ta_region(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch('%s.boto3' % pbm) as mock_boto3: with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, autospec=True, ) as mocks: mock_boto3.Session.return_value._session = Mock() mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mocks['TrustedAdvisor'].return_value = mock_ta mocks['_get_latest_version'].return_value = None with patch('%s._boto_conn_kwargs' % pb, new_callable=PropertyMock) as m_bck: m_bck.return_value = {'region_name': 'rName'} cls = AwsLimitChecker(region='regionX', profile_name='foo', role_partition='rpName', ta_api_region='taRegion') # dict should be of _AwsService instances services = {'SvcFoo': mock_svc1, 'SvcBar': mock_svc2} assert cls.profile_name == 'foo' assert cls.region == 'regionX' assert cls.services == services assert cls.role_partition == 'rpName' assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info assert mocks['TrustedAdvisor'].mock_calls == [ call(services, {'region_name': 'rName'}, ta_api_region='taRegion', ta_refresh_mode=None, ta_refresh_timeout=None) ] def test_init_sts(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch('%s.boto3' % pbm) as mock_boto: mock_boto.client.return_value.assume_role.return_value = { 'Credentials': { 'AccessKeyId': 'akid', 'SecretAccessKey': 'sk', 'SessionToken': 'stoken', 'Expiration': '0' }, 'AssumedRoleUser': { 'AssumedRoleId': 'arid', 'Arn': 'arn' } } with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mocks['TrustedAdvisor'].return_value = mock_ta mocks['_get_latest_version'].return_value = None cls = AwsLimitChecker(account_id='123456789012', account_role='myrole', region='myregion') # dict should be of _AwsService instances services = {'SvcFoo': mock_svc1, 'SvcBar': mock_svc2} assert cls.services == services assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info assert mock_boto.mock_calls == [ call.client('sts', region_name='myregion'), call.client().assume_role( RoleArn='arn:aws:iam::123456789012:role/myrole', RoleSessionName='awslimitchecker') ] def test_init_sts_external_id_ta_refresh(self): mock_svc1 = Mock(spec_set=_AwsService) mock_svc2 = Mock(spec_set=_AwsService) mock_foo = Mock(spec_set=_AwsService) mock_bar = Mock(spec_set=_AwsService) mock_ta = Mock(spec_set=TrustedAdvisor) mock_foo.return_value = mock_svc1 mock_bar.return_value = mock_svc2 svcs = {'SvcFoo': mock_foo, 'SvcBar': mock_bar} with patch('%s.boto3' % pbm) as mock_boto: mock_boto.client.return_value.assume_role.return_value = { 'Credentials': { 'AccessKeyId': 'akid', 'SecretAccessKey': 'sk', 'SessionToken': 'stoken', 'Expiration': '0' }, 'AssumedRoleUser': { 'AssumedRoleId': 'arid', 'Arn': 'arn' } } with patch.dict('%s._services' % pbm, values=svcs, clear=True): with patch.multiple( 'awslimitchecker.checker', logger=DEFAULT, _get_version_info=DEFAULT, TrustedAdvisor=DEFAULT, _get_latest_version=DEFAULT, autospec=True, ) as mocks: mock_version = mocks['_get_version_info'] mock_version.return_value = self.mock_ver_info mocks['TrustedAdvisor'].return_value = mock_ta mocks['_get_latest_version'].return_value = None cls = AwsLimitChecker(account_id='123456789012', account_role='myrole', region='myregion', external_id='myextid', mfa_serial_number=123, mfa_token=456, ta_refresh_mode=123, ta_refresh_timeout=456, role_partition='mypart') # dict should be of _AwsService instances services = {'SvcFoo': mock_svc1, 'SvcBar': mock_svc2} assert cls.services == services assert mock_svc1.mock_calls == [] assert mock_svc2.mock_calls == [] assert self.mock_version.mock_calls == [call()] assert self.cls.vinfo == self.mock_ver_info assert mock_boto.mock_calls == [ call.client('sts', region_name='myregion'), call.client().assume_role( ExternalId='myextid', RoleArn='arn:mypart:iam::123456789012:role/myrole', RoleSessionName='awslimitchecker', SerialNumber=123, TokenCode=456) ] def test_boto3_connection_kwargs(self): cls = AwsLimitChecker() with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [] assert mock_logger.mock_calls == [ call.debug('Connecting to region %s', None) ] assert mock_sess.mock_calls == [] assert res == {'region_name': None} def test_boto3_connection_kwargs_profile(self): m_creds = Mock() type(m_creds).access_key = 'ak' type(m_creds).secret_key = 'sk' type(m_creds).token = 'tkn' mock_session = Mock() m_sess = Mock() m_sess.get_credentials.return_value = m_creds type(mock_session)._session = m_sess with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: with patch.dict('%s._services' % pbm, {}, clear=True): mock_sess.return_value = mock_session cls = AwsLimitChecker(profile_name='myprof') mock_get_sts.reset_mock() mock_logger.reset_mock() mock_sess.reset_mock() res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [] assert mock_logger.mock_calls == [ call.debug('Using credentials profile: %s', 'myprof') ] assert mock_sess.mock_calls == [call(profile_name='myprof')] assert res == { 'region_name': None, 'aws_access_key_id': 'ak', 'aws_secret_access_key': 'sk', 'aws_session_token': 'tkn' } def test_boto3_connection_kwargs_region(self): with patch('%s.boto3' % pbm): cls = AwsLimitChecker(region='myregion') with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [] assert mock_logger.mock_calls == [ call.debug('Connecting to region %s', 'myregion') ] assert mock_sess.mock_calls == [] assert res == {'region_name': 'myregion'} def test_boto3_connection_kwargs_sts(self): mock_creds = Mock() type(mock_creds).access_key = 'sts_ak' type(mock_creds).secret_key = 'sts_sk' type(mock_creds).session_token = 'sts_token' with patch('%s._get_sts_token' % pb) as mock_get_sts: with patch('%s.logger' % pbm) as mock_logger: with patch('%s.boto3.Session' % pbm) as mock_sess: with patch.dict('%s._services' % pbm, {}, clear=True): cls = AwsLimitChecker(account_id='123', account_role='myrole', region='myregion') mock_get_sts.return_value = mock_creds mock_get_sts.reset_mock() mock_logger.reset_mock() mock_sess.reset_mock() res = cls._boto_conn_kwargs assert mock_get_sts.mock_calls == [call()] assert mock_logger.mock_calls == [ call.debug( "Connecting for account %s role '%s' with STS " "(region: %s)", '123', 'myrole', 'myregion') ] assert mock_sess.mock_calls == [] assert res == { 'region_name': 'myregion', 'aws_access_key_id': 'sts_ak', 'aws_secret_access_key': 'sts_sk', 'aws_session_token': 'sts_token' } def test_get_version(self): with patch('%s._get_version_info' % pbm, spec_set=_get_version_info) as mock_version: self.cls.vinfo = self.mock_ver_info res = self.cls.get_version() assert res == '1.2.3@mytag' assert mock_version.mock_calls == [] def test_get_project_url(self): with patch('%s._get_version_info' % pbm, spec_set=_get_version_info) as mock_version: self.cls.vinfo = self.mock_ver_info res = self.cls.get_project_url() assert res == 'http://myurl' assert mock_version.mock_calls == [] def test_remove_services_none(self): self.cls.remove_services() assert self.cls.services == { 'SvcFoo': self.mock_svc1, 'SvcBar': self.mock_svc2 } def test_remove_services_one(self): self.cls.remove_services(['SvcFoo']) assert self.cls.services == {'SvcBar': self.mock_svc2} def test_remove_services_all(self): self.cls.remove_services(['SvcFoo', 'SvcBar']) assert self.cls.services == {} def test_get_service_names(self): res = self.cls.get_service_names() assert res == ['SvcBar', 'SvcFoo'] def test_get_limits(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits() assert res == limits assert self.mock_ta.mock_calls == [call.update_limits()] assert self.mock_svc1.mock_calls == [ call._update_service_quotas(), call.get_limits() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call._update_service_quotas(), call.get_limits() ] def test_get_limits_no_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(use_ta=False) assert res == limits assert self.mock_ta.mock_calls == [] assert self.mock_svc1.mock_calls == [ call._update_service_quotas(), call.get_limits() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call._update_service_quotas(), call.get_limits() ] def test_get_limits_service(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(service=['SvcFoo']) assert res == {'SvcFoo': limits['SvcFoo']} assert self.mock_ta.mock_calls == [call.update_limits()] assert self.mock_svc1.mock_calls == [ call._update_service_quotas(), call.get_limits() ] assert self.mock_svc2.mock_calls == [] def test_get_limits_service_with_api(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] res = self.cls.get_limits(service=['SvcBar']) assert res == {'SvcBar': limits['SvcBar']} assert self.mock_ta.mock_calls == [call.update_limits()] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call._update_service_quotas(), call.get_limits() ] def test_find_usage(self): self.cls.find_usage() assert self.mock_svc1.mock_calls == [ call._update_service_quotas(), call.find_usage() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call._update_service_quotas(), call.find_usage() ] assert self.mock_ta.mock_calls == [call.update_limits()] def test_find_usage_no_ta(self): self.cls.find_usage(use_ta=False) assert self.mock_svc1.mock_calls == [ call._update_service_quotas(), call.find_usage() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call._update_service_quotas(), call.find_usage() ] assert self.mock_ta.mock_calls == [] def test_find_usage_service(self): self.cls.find_usage(service=['SvcFoo']) assert self.mock_svc1.mock_calls == [ call._update_service_quotas(), call.find_usage() ] assert self.mock_svc2.mock_calls == [] assert self.mock_ta.mock_calls == [call.update_limits()] def test_find_usage_service_with_api(self): self.cls.find_usage(service=['SvcBar']) assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call._update_service_quotas(), call.find_usage() ] assert self.mock_ta.mock_calls == [call.update_limits()] def test_set_threshold_overrides(self): limits = sample_limits() limits['SvcFoo']['zz3'] = AwsLimit( 'zz3', self.mock_svc1, 1, 2, 3, ) self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'barlimit1': { 'warning': { 'percent': 10, 'count': 12 }, 'critical': { 'percent': 14, 'count': 16 } }, 'bar limit2': { 'critical': { 'count': 15, } }, 'zz3': { 'warning': { 'count': 41 }, 'critical': { 'percent': 52 } } }, 'SvcFoo': { 'foo limit3': { 'warning': { 'percent': 91 }, } }, } self.cls.set_threshold_overrides(overrides) assert self.mock_svc1.mock_calls == [ call.set_threshold_override( 'foo limit3', warn_percent=91, ) ] assert self.mock_svc2.mock_calls == [ call.set_threshold_override('bar limit2', crit_count=15), call.set_threshold_override('barlimit1', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16), call.set_threshold_override('zz3', warn_count=41, crit_percent=52), ] def test_set_limit_overrides(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'barlimit1': 100, }, 'SvcFoo': { 'foo limit3': 99, }, } self.cls.set_limit_overrides(overrides) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 99, override_ta=True) ] assert self.mock_svc2.mock_calls == [ call.set_limit_override('barlimit1', 100, override_ta=True) ] def test_set_limit_overrides_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.mock_svc2.get_limits.return_value = limits['SvcBar'] overrides = { 'SvcBar': { 'bar limit2': 100, }, 'SvcFoo': { 'foo limit3': 3, }, } self.cls.set_limit_overrides(overrides, override_ta=False) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 3, override_ta=False) ] assert self.mock_svc2.mock_calls == [ call.set_limit_override('bar limit2', 100, override_ta=False) ] def test_set_limit_override(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_limit_override('SvcFoo', 'foo limit3', 99) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 99, override_ta=True) ] def test_set_limit_override_ta(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_limit_override('SvcFoo', 'foo limit3', 99, override_ta=False) assert self.mock_svc1.mock_calls == [ call.set_limit_override('foo limit3', 99, override_ta=False) ] def test_set_threshold_override(self): limits = sample_limits() self.mock_svc1.get_limits.return_value = limits['SvcFoo'] self.cls.set_threshold_override('SvcFoo', 'foo limit3', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16) assert self.mock_svc1.mock_calls == [ call.set_threshold_override('foo limit3', warn_percent=10, warn_count=12, crit_percent=14, crit_count=16) ] def test_get_required_iam_policy(self): expected = { 'Version': '2012-10-17', 'Statement': [{ 'Effect': 'Allow', 'Resource': '*', 'Action': [ 'cloudwatch:GetMetricData', 'ec2:bar', 'ec2:foo', 'foo:perm1', 'foo:perm2', 'servicequotas:ListServiceQuotas', 'sts:GetCallerIdentity', 'support:DescribeTrustedAdvisorCheckRefreshStatuses', 'support:DescribeTrustedAdvisorCheckResult', 'support:DescribeTrustedAdvisorCheckSummaries', 'support:DescribeTrustedAdvisorChecks', 'support:RefreshTrustedAdvisorCheck', 'trustedadvisor:Describe*', 'trustedadvisor:RefreshCheck' ], }], } self.mock_svc1.required_iam_permissions.return_value = [ 'ec2:foo', 'ec2:bar', 'foo:perm1' ] self.mock_svc2.required_iam_permissions.return_value = [ 'foo:perm1', 'foo:perm2', ] res = self.cls.get_required_iam_policy() assert res == expected assert self.mock_svc1.mock_calls == [call.required_iam_permissions()] assert self.mock_svc2.mock_calls == [call.required_iam_permissions()] def test_check_thresholds(self): self.mock_svc1.check_thresholds.return_value = { 'foo': 'bar', 'baz': 'blam', } self.mock_svc2.check_thresholds.return_value = {} res = self.cls.check_thresholds() assert res == { 'SvcFoo': { 'foo': 'bar', 'baz': 'blam', } } assert self.mock_ta.mock_calls == [ call.update_limits(), ] assert self.mock_svc1.mock_calls == [ call._update_service_quotas(), call.check_thresholds() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call._update_service_quotas(), call.check_thresholds() ] def test_check_thresholds_service(self): self.mock_svc1.check_thresholds.return_value = {'foo': 'bar'} self.mock_svc2.check_thresholds.return_value = {'baz': 'blam'} res = self.cls.check_thresholds(service=['SvcFoo']) assert res == { 'SvcFoo': { 'foo': 'bar', } } assert self.mock_ta.mock_calls == [call.update_limits()] assert self.mock_svc1.mock_calls == [ call._update_service_quotas(), call.check_thresholds() ] assert self.mock_svc2.mock_calls == [] def test_check_thresholds_service_api(self): self.mock_svc1.check_thresholds.return_value = {'foo': 'bar'} self.mock_svc2.check_thresholds.return_value = {'baz': 'blam'} res = self.cls.check_thresholds(service=['SvcBar']) assert res == { 'SvcBar': { 'baz': 'blam', } } assert self.mock_ta.mock_calls == [call.update_limits()] assert self.mock_svc1.mock_calls == [] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call._update_service_quotas(), call.check_thresholds() ] def test_check_thresholds_no_ta(self): self.mock_svc1.check_thresholds.return_value = { 'foo': 'bar', 'baz': 'blam', } self.mock_svc2.check_thresholds.return_value = {} self.cls.use_ta = False res = self.cls.check_thresholds(use_ta=False) assert res == { 'SvcFoo': { 'foo': 'bar', 'baz': 'blam', } } assert self.mock_ta.mock_calls == [] assert self.mock_svc1.mock_calls == [ call._update_service_quotas(), call.check_thresholds() ] assert self.mock_svc2.mock_calls == [ call._update_limits_from_api(), call._update_service_quotas(), call.check_thresholds() ] def test_region_name(self): mock_client = Mock(_client_config=Mock(region_name='rname')) with patch('%s._boto_conn_kwargs' % pb, new_callable=PropertyMock) as mock_bck: mock_bck.return_value = {'foo': 'bar'} with patch('%s.boto3.client' % pbm) as m_client: m_client.return_value = mock_client res = self.cls.region_name assert res == 'rname'
def test_verify_limits(self, checker_args, creds_type, service_name, use_ta, expect_api_source, allow_endpoint_error): """ This essentially replicates what's done when awslimitchecker is called from the command line with ``-l``. This replicates some of the internal logic of :py:class:`~awslimitchecker.runner.Runner`. The main purpose is: 1. to allow passing in an existing :py:class:`awslimitchecker.checker.Checker` instance for testing the various authentication options, and, 2. to verify that at least some limits are found This method is largely a duplication of :py:meth:`~awslimitchecker.runner.Runner.list_limits`. :param checker_args: dict of kwargs to pass to :py:class:`awslimitchecker.checker.Checker` constructor :type checker_args: dict :param creds_type: Type of credentials to use; 'normal', 'sts', or 'sts_mfa' :type creds_type: str :param service_name: the Service name to test limits for; if None, check for all. :type service_name: str :param use_ta: whether or not to use TrustedAdvisor :type use_ta: bool :param expect_api_source: whether or not to expect a limit with an API source :type expect_api_source: bool :param allow_endpoint_error: passed on to :py:meth:`~.support.LogRecordHelper.unexpected_logs` :type allow_endpoint_error: bool """ # destroy boto3's session, so it creates a new one boto3.DEFAULT_SESSION = None # set the env vars to the creds we want if creds_type == 'normal': creds = self.normal_creds() elif creds_type == 'sts': creds = self.sts_creds() elif creds_type == 'sts_mfa': creds = self.sts_mfa_creds() checker_args['mfa_serial_number'] = creds[2] else: raise RuntimeError("unknown creds type: '%s'" % creds_type) os.environ['AWS_ACCESS_KEY_ID'] = creds[0] os.environ['AWS_SECRET_ACCESS_KEY'] = creds[1] # this has to be generated inside the method, not in the method that # yields it if 'mfa_token' in checker_args: checker_args['mfa_token'] = self.totp_code(creds[3]) # pytest-capturelog looked good, but won't work with our yielded # test functions, per https://github.com/pytest-dev/pytest/issues/227 with LogCapture() as l: checker = AwsLimitChecker(**checker_args) limits = checker.get_limits(use_ta=use_ta, service=service_name) logs = LogRecordHelper(l) have_api_source = False data = {} for svc in sorted(limits.keys()): for lim in sorted(limits[svc].keys()): src_str = '' if limits[svc][lim].get_limit_source() == SOURCE_API: have_api_source = True src_str = ' (API)' if limits[svc][lim].get_limit_source() == SOURCE_TA: src_str = ' (TA)' data["{s}/{l}".format(s=svc, l=lim)] = '{v}{t}'.format( v=limits[svc][lim].get_limit(), t=src_str) # check that we connected to the right region logs.verify_region(checker_args.get('region', REGION)) # this is the normal Runner output print(dict2cols(data)) if expect_api_source: assert have_api_source is True # ensure we didn't log anything at WARN or above, except possibly # a TrustedAdvisor subscription required message records = logs.unexpected_logs( allow_endpoint_error=allow_endpoint_error) assert len(records) == 0, "awslimitchecker emitted unexpected log " \ "messages at WARN or higher: \n%s" % "\n".join(records) polls = logs.num_ta_polls assert polls == 0, "awslimitchecker should have polled Trusted " \ "Advisor ZERO times, but polled %s times" % polls
def get_iam_policy(self): """Return the current IAM policy as a json-serialized string""" checker = AwsLimitChecker() policy = checker.get_required_iam_policy() return json.dumps(policy, sort_keys=True, indent=2)
def test_verify_limits(self, checker_args, creds_type, service_name, use_ta, expect_api_source, allow_endpoint_error): """ This essentially replicates what's done when awslimitchecker is called from the command line with ``-l``. This replicates some of the internal logic of :py:class:`~awslimitchecker.runner.Runner`. The main purpose is: 1. to allow passing in an existing :py:class:`awslimitchecker.checker.Checker` instance for testing the various authentication options, and, 2. to verify that at least some limits are found This method is largely a duplication of :py:meth:`~awslimitchecker.runner.Runner.list_limits`. :param checker_args: dict of kwargs to pass to :py:class:`awslimitchecker.checker.Checker` constructor :type checker_args: dict :param creds_type: Type of credentials to use; 'normal', 'sts', or 'sts_mfa' :type creds_type: str :param service_name: the Service name to test limits for; if None, check for all. :type service_name: str :param use_ta: whether or not to use TrustedAdvisor :type use_ta: bool :param expect_api_source: whether or not to expect a limit with an API source :type expect_api_source: bool :param allow_endpoint_error: passed on to :py:meth:`~.support.LogRecordHelper.unexpected_logs` :type allow_endpoint_error: bool """ # destroy boto3's session, so it creates a new one boto3.DEFAULT_SESSION = None # set the env vars to the creds we want if creds_type == 'normal': creds = self.normal_creds() elif creds_type == 'sts': creds = self.sts_creds() elif creds_type == 'sts_mfa': creds = self.sts_mfa_creds() checker_args['mfa_serial_number'] = creds[2] else: raise RuntimeError("unknown creds type: '%s'" % creds_type) os.environ['AWS_ACCESS_KEY_ID'] = creds[0] os.environ['AWS_SECRET_ACCESS_KEY'] = creds[1] # this has to be generated inside the method, not in the method that # yields it if 'mfa_token' in checker_args: checker_args['mfa_token'] = self.totp_code(creds[3]) # pytest-capturelog looked good, but won't work with our yielded # test functions, per https://github.com/pytest-dev/pytest/issues/227 with LogCapture() as l: checker = AwsLimitChecker(**checker_args) limits = checker.get_limits(use_ta=use_ta, service=service_name) logs = LogRecordHelper(l) have_api_source = False data = {} for svc in sorted(limits.keys()): for lim in sorted(limits[svc].keys()): src_str = '' if limits[svc][lim].get_limit_source() == SOURCE_API: have_api_source = True src_str = ' (API)' if limits[svc][lim].get_limit_source() == SOURCE_TA: src_str = ' (TA)' data["{s}/{l}".format(s=svc, l=lim)] = '{v}{t}'.format( v=limits[svc][lim].get_limit(), t=src_str) # check that we connected to the right region logs.verify_region(checker_args.get('region', REGION)) # this is the normal Runner output print(dict2cols(data)) if expect_api_source: assert have_api_source is True # ensure we didn't log anything at WARN or above, except possibly # a TrustedAdvisor subscription required message records = logs.unexpected_logs( allow_endpoint_error=allow_endpoint_error ) assert len(records) == 0, "awslimitchecker emitted unexpected log " \ "messages at WARN or higher: \n%s" % "\n".join(records) polls = logs.num_ta_polls assert polls == 1, "awslimitchecker should have polled Trusted " \ "Advisor once, but polled %s times" % polls