You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
5.6KB

  1. # -*- coding: utf-8 -*-
  2. import os
  3. import time
  4. from traceback import format_exc
  5. import requests
  6. import json
  7. import threading
  8. from loguru import logger
  9. from .exceptions import ClientError
  10. from .utils import to_unixtime
  11. from .compat import to_unicode
  12. class Credentials(object):
  13. __slots__ = ("access_key_id", 'access_key_secret', 'security_token')
  14. def __init__(self, access_key_id="", access_key_secret="", security_token=""):
  15. self.access_key_id = access_key_id
  16. self.access_key_secret = access_key_secret
  17. self.security_token = security_token
  18. def get_access_key_id(self):
  19. return self.access_key_id
  20. def get_access_key_secret(self):
  21. return self.access_key_secret
  22. def get_security_token(self):
  23. return self.security_token
  24. DEFAULT_ECS_SESSION_TOKEN_DURATION_SECONDS = 3600 * 6
  25. DEFAULT_ECS_SESSION_EXPIRED_FACTOR = 0.85
  26. class EcsRamRoleCredential(Credentials):
  27. def __init__(self,
  28. access_key_id,
  29. access_key_secret,
  30. security_token,
  31. expiration,
  32. duration,
  33. expired_factor=None):
  34. self.access_key_id = access_key_id
  35. self.access_key_secret = access_key_secret
  36. self.security_token = security_token
  37. self.expiration = expiration
  38. self.duration = duration
  39. self.expired_factor = expired_factor or DEFAULT_ECS_SESSION_EXPIRED_FACTOR
  40. def get_access_key_id(self):
  41. return self.access_key_id
  42. def get_access_key_secret(self):
  43. return self.access_key_secret
  44. def get_security_token(self):
  45. return self.security_token
  46. def will_soon_expire(self):
  47. now = int(time.time())
  48. return self.duration * (1.0 - self.expired_factor) > self.expiration - now
  49. class CredentialsProvider(object):
  50. def get_credentials(self):
  51. return
  52. class StaticCredentialsProvider(CredentialsProvider):
  53. __slots__ = "credentials"
  54. def __init__(self, access_key_id="", access_key_secret="", security_token=""):
  55. self.credentials = Credentials(access_key_id, access_key_secret, security_token)
  56. def get_credentials(self):
  57. return self.credentials
  58. class EcsRamRoleCredentialsProvider(CredentialsProvider):
  59. def __init__(self, auth_host, max_retries=3, timeout=10):
  60. self.fetcher = EcsRamRoleCredentialsFetcher(auth_host)
  61. self.max_retries = max_retries
  62. self.timeout = timeout
  63. self.credentials = None
  64. self.__lock = threading.Lock()
  65. def get_credentials(self):
  66. if self.credentials is None or self.credentials.will_soon_expire():
  67. with self.__lock:
  68. if self.credentials is None or self.credentials.will_soon_expire():
  69. try:
  70. self.credentials = self.fetcher.fetch(self.max_retries, self.timeout)
  71. except Exception:
  72. logger.error("Exception: {}", format_exc())
  73. if self.credentials is None:
  74. raise
  75. return self.credentials
  76. class EcsRamRoleCredentialsFetcher(object):
  77. def __init__(self, auth_host):
  78. self.auth_host = auth_host
  79. def fetch(self, retry_times=3, timeout=10):
  80. for i in range(0, retry_times):
  81. try:
  82. response = requests.get(self.auth_host, timeout=timeout)
  83. if response.status_code != 200:
  84. raise ClientError(
  85. "Failed to fetch credentials url, http code:{0}, msg:{1}".format(response.status_code,
  86. response.text))
  87. dic = json.loads(to_unicode(response.content))
  88. code = dic.get('Code')
  89. access_key_id = dic.get('AccessKeyId')
  90. access_key_secret = dic.get('AccessKeySecret')
  91. security_token = dic.get('SecurityToken')
  92. expiration_date = dic.get('Expiration')
  93. last_updated_date = dic.get('LastUpdated')
  94. if code != "Success":
  95. raise ClientError("Get credentials from ECS metadata service error, code: {0}".format(code))
  96. expiration_stamp = to_unixtime(expiration_date, "%Y-%m-%dT%H:%M:%SZ")
  97. duration = DEFAULT_ECS_SESSION_TOKEN_DURATION_SECONDS
  98. if last_updated_date is not None:
  99. last_updated_stamp = to_unixtime(last_updated_date, "%Y-%m-%dT%H:%M:%SZ")
  100. duration = expiration_stamp - last_updated_stamp
  101. return EcsRamRoleCredential(access_key_id, access_key_secret, security_token, expiration_stamp,
  102. duration, DEFAULT_ECS_SESSION_EXPIRED_FACTOR)
  103. except Exception as e:
  104. if i == retry_times - 1:
  105. logger.error("Exception: {}", format_exc())
  106. raise ClientError("Failed to get credentials from ECS metadata service. {0}".format(e))
  107. class EnvironmentVariableCredentialsProvider(CredentialsProvider):
  108. def __init__(self):
  109. self.access_key_id = ""
  110. self.access_key_secret = ""
  111. self.security_token = ""
  112. def get_credentials(self):
  113. access_key_id = os.getenv('OSS_ACCESS_KEY_ID')
  114. access_key_secret = os.getenv('OSS_ACCESS_KEY_SECRET')
  115. security_token = os.getenv('OSS_SESSION_TOKEN')
  116. if not access_key_id:
  117. raise ClientError("Access key id should not be null or empty.")
  118. if not access_key_secret:
  119. raise ClientError("Secret access key should not be null or empty.")
  120. return Credentials(access_key_id, access_key_secret, security_token)