Example #1
0
 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)
Example #2
0
    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)
Example #3
0
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()
Example #6
0
 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)
     ]
Example #7
0
 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
Example #8
0
    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'
        }
Example #9
0
    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'
        }
Example #10
0
    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
Example #12
0
 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
Example #13
0
    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)
Example #14
0
    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()
Example #16
0
 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
Example #17
0
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)
Example #18
0
 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
Example #20
0
 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)
     ]
Example #21
0
 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()
Example #23
0
 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)
     ]
Example #24
0
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)
Example #25
0
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 == []
Example #28
0
 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
Example #31
0
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()
        ]
Example #32
0
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()
        ]
Example #33
0
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:
Example #34
0
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()
        ]
Example #35
0
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'
Example #36
0
    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
Example #37
0
 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