AwsLimitChecker provides the full and only public interface to this
project; it’s used by the awslimitchecker command line script (entry point to runner)
and should be the only portion directly used by external code.

Each AWS Service is represented by a subclass of the _AwsService abstract base
class; these Service Classes are responsible for knowing which limits exist for the service they represent, what the
default values for these limits are, querying current limits from the service’s API (if supported),
and how to check the current usage via the AWS API (boto3). When the
Service Classes are instantiated, they build a dict of all of their limits, correlating a string key (the “limit name”)
with an AwsLimit object. The Service Class constructors must not make any network
connections; connections are created lazily as needed and stored as a class attribute. This allows us to inspect the
services, limits and default limit values without ever connecting to AWS (this is also used to generate the
Supported Limits documentation automatically).

All calls to boto3 client (“low-level”) methods that return a dict response that can
include ‘NextToken’ or another pagination marker, should be called through
paginate_dict() with the appropriate parameters.

When AwsLimitChecker is instantiated, it imports services
which in turn creates instances of all awslimitchecker.services.* classes and adds them to a dict mapping the
string Service Name to the Service Class instance. These instances are used for all interaction with the services.

So, once an instance of AwsLimitChecker is created, we should have instant access
to the services and limits without any connection to AWS. This is utilized by the --list-services and
--list-defaults options for the command line client.

update_limits() polls Trusted Advisor data from the Support API via
_poll(); this will retrieve the limits for all “flaggedResources” items in the
ServiceLimits Trusted Advisor check result for the current AWS account. It then calls
_update_services(), passing in the Trusted Advisor check results and the
dict of _AwsService objects it was called with (from AwsLimitChecker).

_update_services() iterates over the Services in the Trusted Advisor check result
and attempts to find a matching _AwsService (by string service name) in the dict passed
in from AwsLimitChecker. If a match is found, it iterates over all limits for that service
in the TA result and attempts to call the Service‘s _set_ta_limit() method.
If a matching Service is not found, or if _set_ta_limit raises a ValueError (matching Limit not found
for that Service), an error is logged.

When AwsLimitChecker initializes
TrustedAdvisor, it passes in the
self.services dictionary of all services and limits. At initialization time,
TrustedAdvisor iterates all services
and limits, and builds a new dictionary mapping the limit objects by the return
values of their ta_service_name()
and ta_limit_name() properties. This
allows limits to override the Trusted Advisor service and limit name that their
data comes from. In the default case, their service and limit names will be used
as they are set in the awslimitchecker code, and limits which have matching
Trusted Advisor data will be automatically populated.

In the TrustedAdvisor class’s
_poll() method,
_get_refreshed_check_result() is used to retrieve the
check result data from Trusted Advisor. This method also implements the check
refresh logic. See the comments in the source code for the specific logic. There
are three methods of refreshing checks (refresh modes), which are controlled
by the ta_refresh_mode parameter to TrustedAdvisor:

If ta_refresh_mode is the string “wait”, the check will be refreshed and
awslimitchecker will poll for the refresh result every 30 seconds, waiting
for the refresh to complete (or until ta_refresh_timeout seconds have elapsed).
This is exposed via the CLI as the --ta-refresh-wait option.

If ta_refresh_mode is an integer, it will operate like the “wait” mode above,
but only if the current result data for the check is more than ta_refresh_mode
seconds old. This is exposed via the CLI as the --ta-refresh-older option.

If ta_refresh_mode is the string “trigger”, the check will be refreshed and
the program will continue on immediately, without waiting for the refresh to
complete; this will almost certainly result in stale check results in the current
run. However, this may be useful if you desire to keep awslimitchecker runs
short, and run it on a regular schedule (i.e. if you run awslimitchecker
every 6 hours, and are OK with Trusted Advisor check data being 6 hours old).
This is exposed via the CLI as the --ta-refresh-trigger option.

Additionally, TrustedAdvisor has a
ta_refresh_timeout parameter. If this is set to a non-None value (an integer),
refreshes of the check will time out after that number of seconds. If a timeout
occurs, a message will be logged at error level, but the program will continue
running (most likely using the old result data). This parameter is exposed via
the CLI as the --ta-refresh-timeout option.

Important: It may take 30 to 60 minutes for the Service Limits check to
refresh on large accounts. Please be aware of this when enabling the refresh
options.

Using the check refresh options will require the trustedadvisor:RefreshCheck
IAM permission.

For use via Python, these same parameters (ta_refresh_mode and ta_refresh_timeout)
are exposed as parameters on the
AwsLimitChecker constructor.

Some services provide API calls to retrieve at least some of the current limits, such as the DescribeAccountAttributes
API calls for RDS
and EC2. Services that
support such calls should make them in a _update_limits_from_api() method, which will be automatically called from
get_limits(). The _update_limits_from_api() method should make the API call, and then
update all relevant limits via the AwsLimit class’s _set_api_limit() method.