Exception
11 months ago
Configuration.php
11 months ago
ConfigurationInterface.php
11 months ago
ConfigurationProvider.php
11 months ago
QuotaManager.php
11 months ago
RateLimiter.php
11 months ago
RetryHelperTrait.php
11 months ago
ConfigurationProvider.php
223 lines
| 1 | <?php |
| 2 | namespace Aws\Retry; |
| 3 | |
| 4 | use Aws\AbstractConfigurationProvider; |
| 5 | use Aws\CacheInterface; |
| 6 | use Aws\ConfigurationProviderInterface; |
| 7 | use Aws\Retry\Exception\ConfigurationException; |
| 8 | use GuzzleHttp\Promise; |
| 9 | use GuzzleHttp\Promise\PromiseInterface; |
| 10 | |
| 11 | /** |
| 12 | * A configuration provider is a function that returns a promise that is |
| 13 | * fulfilled with a {@see \Aws\Retry\ConfigurationInterface} |
| 14 | * or rejected with an {@see \Aws\Retry\Exception\ConfigurationException}. |
| 15 | * |
| 16 | * <code> |
| 17 | * use Aws\Sts\RegionalEndpoints\ConfigurationProvider; |
| 18 | * $provider = ConfigurationProvider::defaultProvider(); |
| 19 | * // Returns a ConfigurationInterface or throws. |
| 20 | * $config = $provider()->wait(); |
| 21 | * </code> |
| 22 | * |
| 23 | * Configuration providers can be composed to create configuration using |
| 24 | * conditional logic that can create different configurations in different |
| 25 | * environments. You can compose multiple providers into a single provider using |
| 26 | * {@see \Aws\Retry\ConfigurationProvider::chain}. This function |
| 27 | * accepts providers as variadic arguments and returns a new function that will |
| 28 | * invoke each provider until a successful configuration is returned. |
| 29 | * |
| 30 | * <code> |
| 31 | * // First try an INI file at this location. |
| 32 | * $a = ConfigurationProvider::ini(null, '/path/to/file.ini'); |
| 33 | * // Then try an INI file at this location. |
| 34 | * $b = ConfigurationProvider::ini(null, '/path/to/other-file.ini'); |
| 35 | * // Then try loading from environment variables. |
| 36 | * $c = ConfigurationProvider::env(); |
| 37 | * // Combine the three providers together. |
| 38 | * $composed = ConfigurationProvider::chain($a, $b, $c); |
| 39 | * // Returns a promise that is fulfilled with a configuration or throws. |
| 40 | * $promise = $composed(); |
| 41 | * // Wait on the configuration to resolve. |
| 42 | * $config = $promise->wait(); |
| 43 | * </code> |
| 44 | */ |
| 45 | class ConfigurationProvider extends AbstractConfigurationProvider |
| 46 | implements ConfigurationProviderInterface |
| 47 | { |
| 48 | const DEFAULT_MAX_ATTEMPTS = 3; |
| 49 | const DEFAULT_MODE = 'legacy'; |
| 50 | const ENV_MAX_ATTEMPTS = 'AWS_MAX_ATTEMPTS'; |
| 51 | const ENV_MODE = 'AWS_RETRY_MODE'; |
| 52 | const ENV_PROFILE = 'AWS_PROFILE'; |
| 53 | const INI_MAX_ATTEMPTS = 'max_attempts'; |
| 54 | const INI_MODE = 'retry_mode'; |
| 55 | |
| 56 | public static $cacheKey = 'aws_retries_config'; |
| 57 | |
| 58 | protected static $interfaceClass = ConfigurationInterface::class; |
| 59 | protected static $exceptionClass = ConfigurationException::class; |
| 60 | |
| 61 | /** |
| 62 | * Create a default config provider that first checks for environment |
| 63 | * variables, then checks for a specified profile in the environment-defined |
| 64 | * config file location (env variable is 'AWS_CONFIG_FILE', file location |
| 65 | * defaults to ~/.aws/config), then checks for the "default" profile in the |
| 66 | * environment-defined config file location, and failing those uses a default |
| 67 | * fallback set of configuration options. |
| 68 | * |
| 69 | * This provider is automatically wrapped in a memoize function that caches |
| 70 | * previously provided config options. |
| 71 | * |
| 72 | * @param array $config |
| 73 | * |
| 74 | * @return callable |
| 75 | */ |
| 76 | public static function defaultProvider(array $config = []) |
| 77 | { |
| 78 | $configProviders = [self::env()]; |
| 79 | if ( |
| 80 | !isset($config['use_aws_shared_config_files']) |
| 81 | || $config['use_aws_shared_config_files'] != false |
| 82 | ) { |
| 83 | $configProviders[] = self::ini(); |
| 84 | } |
| 85 | $configProviders[] = self::fallback(); |
| 86 | |
| 87 | $memo = self::memoize( |
| 88 | call_user_func_array([ConfigurationProvider::class, 'chain'], $configProviders) |
| 89 | ); |
| 90 | |
| 91 | if (isset($config['retries']) |
| 92 | && $config['retries'] instanceof CacheInterface |
| 93 | ) { |
| 94 | return self::cache($memo, $config['retries'], self::$cacheKey); |
| 95 | } |
| 96 | |
| 97 | return $memo; |
| 98 | } |
| 99 | |
| 100 | /** |
| 101 | * Provider that creates config from environment variables. |
| 102 | * |
| 103 | * @return callable |
| 104 | */ |
| 105 | public static function env() |
| 106 | { |
| 107 | return function () { |
| 108 | // Use config from environment variables, if available |
| 109 | $mode = getenv(self::ENV_MODE); |
| 110 | $maxAttempts = getenv(self::ENV_MAX_ATTEMPTS) |
| 111 | ? getenv(self::ENV_MAX_ATTEMPTS) |
| 112 | : self::DEFAULT_MAX_ATTEMPTS; |
| 113 | if (!empty($mode)) { |
| 114 | return Promise\Create::promiseFor( |
| 115 | new Configuration($mode, $maxAttempts) |
| 116 | ); |
| 117 | } |
| 118 | |
| 119 | return self::reject('Could not find environment variable config' |
| 120 | . ' in ' . self::ENV_MODE); |
| 121 | }; |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * Fallback config options when other sources are not set. |
| 126 | * |
| 127 | * @return callable |
| 128 | */ |
| 129 | public static function fallback() |
| 130 | { |
| 131 | return function () { |
| 132 | return Promise\Create::promiseFor( |
| 133 | new Configuration(self::DEFAULT_MODE, self::DEFAULT_MAX_ATTEMPTS) |
| 134 | ); |
| 135 | }; |
| 136 | } |
| 137 | |
| 138 | /** |
| 139 | * Config provider that creates config using a config file whose location |
| 140 | * is specified by an environment variable 'AWS_CONFIG_FILE', defaulting to |
| 141 | * ~/.aws/config if not specified |
| 142 | * |
| 143 | * @param string|null $profile Profile to use. If not specified will use |
| 144 | * the "default" profile. |
| 145 | * @param string|null $filename If provided, uses a custom filename rather |
| 146 | * than looking in the default directory. |
| 147 | * |
| 148 | * @return callable |
| 149 | */ |
| 150 | public static function ini( |
| 151 | $profile = null, |
| 152 | $filename = null |
| 153 | ) { |
| 154 | $filename = $filename ?: (self::getDefaultConfigFilename()); |
| 155 | $profile = $profile ?: (getenv(self::ENV_PROFILE) ?: 'default'); |
| 156 | |
| 157 | return function () use ($profile, $filename) { |
| 158 | if (!@is_readable($filename)) { |
| 159 | return self::reject("Cannot read configuration from $filename"); |
| 160 | } |
| 161 | $data = \Aws\parse_ini_file($filename, true); |
| 162 | if ($data === false) { |
| 163 | return self::reject("Invalid config file: $filename"); |
| 164 | } |
| 165 | if (!isset($data[$profile])) { |
| 166 | return self::reject("'$profile' not found in config file"); |
| 167 | } |
| 168 | if (!isset($data[$profile][self::INI_MODE])) { |
| 169 | return self::reject("Required retry config values |
| 170 | not present in INI profile '{$profile}' ({$filename})"); |
| 171 | } |
| 172 | |
| 173 | $maxAttempts = isset($data[$profile][self::INI_MAX_ATTEMPTS]) |
| 174 | ? $data[$profile][self::INI_MAX_ATTEMPTS] |
| 175 | : self::DEFAULT_MAX_ATTEMPTS; |
| 176 | |
| 177 | return Promise\Create::promiseFor( |
| 178 | new Configuration( |
| 179 | $data[$profile][self::INI_MODE], |
| 180 | $maxAttempts |
| 181 | ) |
| 182 | ); |
| 183 | }; |
| 184 | } |
| 185 | |
| 186 | /** |
| 187 | * Unwraps a configuration object in whatever valid form it is in, |
| 188 | * always returning a ConfigurationInterface object. |
| 189 | * |
| 190 | * @param mixed $config |
| 191 | * @return ConfigurationInterface |
| 192 | * @throws \InvalidArgumentException |
| 193 | */ |
| 194 | public static function unwrap($config) |
| 195 | { |
| 196 | if (is_callable($config)) { |
| 197 | $config = $config(); |
| 198 | } |
| 199 | if ($config instanceof PromiseInterface) { |
| 200 | $config = $config->wait(); |
| 201 | } |
| 202 | if ($config instanceof ConfigurationInterface) { |
| 203 | return $config; |
| 204 | } |
| 205 | |
| 206 | // An integer value for this config indicates the legacy 'retries' |
| 207 | // config option, which is incremented to translate to max attempts |
| 208 | if (is_int($config)) { |
| 209 | return new Configuration('legacy', $config + 1); |
| 210 | } |
| 211 | |
| 212 | if (is_array($config) && isset($config['mode'])) { |
| 213 | $maxAttempts = isset($config['max_attempts']) |
| 214 | ? $config['max_attempts'] |
| 215 | : self::DEFAULT_MAX_ATTEMPTS; |
| 216 | return new Configuration($config['mode'], $maxAttempts); |
| 217 | } |
| 218 | |
| 219 | throw new \InvalidArgumentException('Not a valid retry configuration' |
| 220 | . ' argument.'); |
| 221 | } |
| 222 | } |
| 223 |