AWS_ENDPOINT_URL環境変数およびプロファイル設定で使うサービス名について
しばたです。
前の記事でAWS各サービスのエンドポイント設定を上書きするAWS_ENDPOINT_URL
環境変数と共有設定ファイル(~/.aws/config
)の設定が増えたことを解説しました。
この機能では、例えばAWS_ENDPOINT_URL_S3
の様にAWS_ENDPOINT_URL_"サービス名"
の形式で特定サービス専用のエンドポイント設定が可能となっています。
既にAWSには200以上にわたる大量のサービスがあり、単純にサービス名といってもどの様な名称を指定すべきか困ってしまいます。
というわけで指定可能なサービス名がどの様なルールになっているのか調査してみました。
botocoreの実装
この機能はbotocoreに実装されており、具体的にはConfiguredEndpointProvider
クラスで対象となる環境変数および共有設定ファイルの設定を探索しています。
- ConfiguredEndpointProvider (v1.31.0時点)
探索順序は
- サービス毎の環境変数 (
AWS_ENDPOINT_URL_"サービス名"
) - グローバルの環境変数 (
AWS_ENDPOINT_URL
) - サービス毎のファイル設定 (
~/.aws/config
のservices
セクション) - グローバルのファイル設定 (
~/.aws/config
のendpoint_url
キー)
となっていました。
環境変数の名前
探索する環境変数の名前は_get_service_env_var_name
メソッドで取得しており、以下の様な実装になっています。
def _get_service_env_var_name(self):
transformed_service_id_env = self._transformed_service_id.upper()
return f'AWS_ENDPOINT_URL_{transformed_service_id_env}'
ここに出てくる_transformed_service_id
は_get_snake_case_service_id
メソッドからその値を取得しており、
self._transformed_service_id = self._get_snake_case_service_id(
self._client_name
)
_get_snake_case_service_id
メソッドの定義はこんな感じになっています。
def _get_snake_case_service_id(self, client_name):
# Get the service ID without loading the service data file, accounting
# for any aliases and standardizing the names with hyphens.
client_name = utils.SERVICE_NAME_ALIASES.get(client_name, client_name)
hyphenized_service_id = (
utils.CLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDES.get(
client_name, client_name
)
)
return hyphenized_service_id.replace('-', '_')
実装をざっくり説明すると、このメソッドで返される値はAWSのサービス名(boto3やAWS CLIで使うサービス名)をベースにし、特定のサービスに関しては一定の変換をかけています。
変換用のDictonaryとしてSERVICE_NAME_ALIASES
やCLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDES
がグローバルに定義されています。
- utils.py (v1.31.0時点)
SERVICE_NAME_ALIASES
の内容はこんな感じ。
SERVICE_NAME_ALIASES = {'runtime.sagemaker': 'sagemaker-runtime'}
詳細は把握できていないのですが、どうやらSageMakerはSDKで使うサービス名が一度変わっている様でそのためのエイリアスのようです。現時点ではsagemaker-runtime
を使う様なのであまり気にしないで良いでしょう。
続けてCLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDES
の定義はこんな感じです。
CLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDES = {
# Actual service name we use -> Allowed computed service name.
'alexaforbusiness': 'alexa-for-business',
'apigateway': 'api-gateway',
'application-autoscaling': 'application-auto-scaling',
'appmesh': 'app-mesh',
'autoscaling': 'auto-scaling',
'autoscaling-plans': 'auto-scaling-plans',
'ce': 'cost-explorer',
'cloudhsmv2': 'cloudhsm-v2',
'cloudsearchdomain': 'cloudsearch-domain',
'cognito-idp': 'cognito-identity-provider',
'config': 'config-service',
'cur': 'cost-and-usage-report-service',
'datapipeline': 'data-pipeline',
'directconnect': 'direct-connect',
'devicefarm': 'device-farm',
'discovery': 'application-discovery-service',
'dms': 'database-migration-service',
'ds': 'directory-service',
'dynamodbstreams': 'dynamodb-streams',
'elasticbeanstalk': 'elastic-beanstalk',
'elastictranscoder': 'elastic-transcoder',
'elb': 'elastic-load-balancing',
'elbv2': 'elastic-load-balancing-v2',
'es': 'elasticsearch-service',
'events': 'eventbridge',
'globalaccelerator': 'global-accelerator',
'iot-data': 'iot-data-plane',
'iot-jobs-data': 'iot-jobs-data-plane',
'iot1click-devices': 'iot-1click-devices-service',
'iot1click-projects': 'iot-1click-projects',
'iotevents-data': 'iot-events-data',
'iotevents': 'iot-events',
'iotwireless': 'iot-wireless',
'kinesisanalytics': 'kinesis-analytics',
'kinesisanalyticsv2': 'kinesis-analytics-v2',
'kinesisvideo': 'kinesis-video',
'lex-models': 'lex-model-building-service',
'lexv2-models': 'lex-models-v2',
'lex-runtime': 'lex-runtime-service',
'lexv2-runtime': 'lex-runtime-v2',
'logs': 'cloudwatch-logs',
'machinelearning': 'machine-learning',
'marketplacecommerceanalytics': 'marketplace-commerce-analytics',
'marketplace-entitlement': 'marketplace-entitlement-service',
'meteringmarketplace': 'marketplace-metering',
'mgh': 'migration-hub',
'sms-voice': 'pinpoint-sms-voice',
'resourcegroupstaggingapi': 'resource-groups-tagging-api',
'route53': 'route-53',
'route53domains': 'route-53-domains',
's3control': 's3-control',
'sdb': 'simpledb',
'secretsmanager': 'secrets-manager',
'serverlessrepo': 'serverlessapplicationrepository',
'servicecatalog': 'service-catalog',
'servicecatalog-appregistry': 'service-catalog-appregistry',
'stepfunctions': 'sfn',
'storagegateway': 'storage-gateway',
}
こっちが今回のメインで、ここで列挙されているサービスについては独自の変換をした後の名前を使う様になっています。
ほとんどが長い名称をハイフン付きの形にするパターンですが、
elbv2
→elastic-load-balancing-v2
stepfunctions
→sfn
serverlessrepo
→serverlessapplicationrepository
の様に略称から長い名称に変換するパターン、逆に略称にするパターン、さらにハイフン無しの名称にしてしまうパターンがあったりと、きれいな統一には至っていない感じです。
(何故こうなったのかは謎です...どうして...)
最終的に環境変数で使うサービス名としてはハイフンをアンダースコアに変換し大文字にしたものを採用しています。
たとえば先述の3サービスであれば、
elbv2
→AWS_ENDPOINT_URL_ELASTIC_LOAD_BALANCING_V2
stepfunctions
→AWS_ENDPOINT_URL_SFN
serverlessrepo
→AWS_ENDPOINT_URL_SERVERLESSAPPLICATIONREPOSITORY
という環境変数になります。
CLIENT_NAME_TO_HYPHENIZED_SERVICE_ID_OVERRIDES
の変換対象になっていないサービスの場合は、boto3のサービス名をそのままアンダースコア形式+大文字にしたものを使えばOKです。
簡単な例を挙げると、
s3
→AWS_ENDPOINT_URL_S3
ec2
→AWS_ENDPOINT_URL_EC2
license-manager
→AWS_ENDPOINT_URL_LICENSE_MANAGER
といった感じになります。
共有設定ファイルのキー名
次に共有設定ファイルの場合は_get_endpoint_url_config_service
メソッドで設定値を取得しています。
メソッドの定義は以下の通りで環境変数の時と同様に_transformed_service_id
の値をベースとし、こちらはアンダースコア形式+小文字にしたキー名を探索する様になっています。
def _get_endpoint_url_config_service(self):
snakecase_service_id = self._transformed_service_id.lower()
return (
self._get_services_config()
.get(snakecase_service_id, {})
.get('endpoint_url')
)
このため、前節で例示したサービスの場合
elbv2
→elastic_load_balancing_v2
stepfunctions
→sfn
serverlessrepo
→serverlessapplicationrepository
s3
→s3
ec2
→ec2
license-manager
→license_manager
というキー名を指定する形になります。
[services custom-endpoint]
elastic_load_balancing_v2 =
endpoint_url = http://localhost:4567
[profile dev]
services = custom-endpoint
最後に
簡単ですが以上となります。
独特の変換をしているサービスがあるため「いずれはAWSのドキュメントでも一覧化してくれるのでは?」と期待しています。
困ったときは本記事の内容をベースに最新のbotocoreのソースを探索すると良いでしょう。
追記
本記事を公開した後で気が付いたのですが、一覧、ありました...
昨日は無かったような気がするのですが恐らくは単純に見逃してただけでしょう。