|
1 | 1 | # Devtools Testutils |
2 | 2 |
|
3 | 3 | ## Objects in this package for use with Azure Testing |
| 4 | + |
4 | 5 | * [`AzureMgmtPreparer`][azure_mgmt_preparer]: Base class for Management-plane resource preparers |
5 | 6 | * [`is_live`][is_live]: Helper method for determining whether a test run is in live or playback mode |
6 | 7 | * [`get_region_override`][get_region_override]: Helper method for determining resource region |
@@ -28,10 +29,16 @@ This loader is meant to be paired with the PowerShell test resource management c |
28 | 29 | resource management. |
29 | 30 |
|
30 | 31 | The `EnvironmentVariableLoader` accepts a positional `directory` argument and arbitrary keyword-only arguments: |
31 | | -- `directory` is the name of your package's service as it appears in the Python repository; i.e. `service` in `azure-sdk-for-python/sdk/service/azure-service-package`. |
32 | | - - For example, for `azure-keyvault-keys`, the value of `directory` is `keyvault`. |
33 | | -- For each environment variable you want to provide to tests, pass in a keyword argument with the pattern `environment_variable_name="sanitized-value"`. |
34 | | - - For example, to fetch the value of `STORAGE_ENDPOINT` and sanitize this value in recordings as `fake-endpoint`, provide `storage_endpoint="fake-endpoint"` to the `EnvironmentVariableLoader` constructor. |
| 32 | + |
| 33 | +* `directory` is the name of your package's service as it appears in the Python repository; i.e. `service` in `azure-sdk-for-python/sdk/service/azure-service-package`. |
| 34 | + * For example, for `azure-keyvault-keys`, the value of `directory` is `keyvault`. |
| 35 | +* For each environment variable you want to provide to tests, pass in a keyword argument with the pattern `environment_variable_name="sanitized-value"`. |
| 36 | + * For example, to fetch the value of `STORAGE_ENDPOINT` and sanitize this value in recordings as `fake-endpoint`, provide `storage_endpoint="fake-endpoint"` to the `EnvironmentVariableLoader` constructor. |
| 37 | + |
| 38 | +Additionally, the loader accepts an `EnvironmentVariableOptions` object via the `options` kwarg. This should be used |
| 39 | +whenever fetching sensitive environment variables like connection strings or account keys. See |
| 40 | +[Hide secret environment variables in test logs](#hide-secret-environment-variables-in-test-logs) for details and usage |
| 41 | +examples. |
35 | 42 |
|
36 | 43 | Decorated test methods will have the values of environment variables passed to them as keyword arguments, and these |
37 | 44 | values will automatically have sanitizers registered with the test proxy. More specifically, the true values of |
@@ -71,16 +78,67 @@ variables in recordings. |
71 | 78 | > `{SERVICE}_TENANT_ID`, `{SERVICE}_CLIENT_ID`, and `{SERVICE}_CLIENT_SECRET` for a service principal when using this |
72 | 79 | > class. |
73 | 80 |
|
| 81 | +### Hide secret environment variables in test logs |
| 82 | + |
| 83 | +Pytest will log local test variables, including test method parameters, whenever a test fails. This can lead to secret |
| 84 | +leak warnings in pipelines even if test resources are transient or mocked. To avoid leaks, you should shield sensitive |
| 85 | +variables with the `options` keyword argument. |
| 86 | + |
| 87 | +`options` accepts an [EnvironmentVariableOptions][options] instance, with which you can provide a case insensitive list |
| 88 | +of environment variables that should be hidden in tests. [For example:][options_use] |
| 89 | + |
| 90 | +```python |
| 91 | +DataLakePreparer = functools.partial( |
| 92 | + EnvironmentVariableLoader, "storage", |
| 93 | + datalake_storage_account_name="storagename", |
| 94 | + datalake_storage_account_key=STORAGE_ACCOUNT_FAKE_KEY, |
| 95 | + storage_data_lake_soft_delete_account_name="storagesoftdelname", |
| 96 | + storage_data_lake_soft_delete_account_key=STORAGE_ACCOUNT_FAKE_KEY, |
| 97 | + options=EnvironmentVariableOptions( |
| 98 | + hide_secrets=["datalake_storage_account_key", "storage_data_lake_soft_delete_account_key"] |
| 99 | + ), |
| 100 | +) |
| 101 | +``` |
| 102 | + |
| 103 | +The loader will raise an error if a variable specified in `hide_secrets` doesn't match any of the variables requested |
| 104 | +for loading. |
| 105 | + |
| 106 | +Hidden variables will be passed to tests as an [EnvironmentVariable][environmentvariable] instance instead of a plain |
| 107 | +string; the variable's value should be fetched with the `.secret` string attribute. **It's important to avoid assigning |
| 108 | +the secret to a local variable.** As mentioned in the start of this section, Pytest logs the values of local variables |
| 109 | +upon test failure. Instead, `<variable>.secret` should be accessed only when the value is used; e.g. |
| 110 | +[when providing the secret to a credential constructor][environmentvariable_use]. |
| 111 | + |
| 112 | +```python |
| 113 | +class TestDatalakeCpk(StorageRecordedTestCase): |
| 114 | + def _setup(self, account_name: str, account_key: EnvironmentVariable): |
| 115 | + url = self.account_url(account_name, 'dfs') |
| 116 | + self.dsc = DataLakeServiceClient(url, credential=account_key.secret) |
| 117 | + ... |
| 118 | + |
| 119 | + # Note that DataLakePreparer passes account name as a plain string, but hides account key in an EnvironmentVariable |
| 120 | + @DataLakePreparer() |
| 121 | + @recorded_by_proxy |
| 122 | + def test_create_directory_cpk( |
| 123 | + self, datalake_storage_account_name: str, datalake_storage_account_key: EnvironmentVariable |
| 124 | + ): |
| 125 | + self._setup(datalake_storage_account_name, datalake_storage_account_key) |
| 126 | + ... |
| 127 | +``` |
74 | 128 |
|
75 | 129 | <!-- LINKS --> |
76 | 130 | [azure_mgmt_preparer]: https://github.com/Azure/azure-sdk-for-python/blob/4df650d2ce4c292942009ed648cae21eb9c2121d/eng/tools/azure-sdk-tools/devtools_testutils/mgmt_testcase.py#L42 |
77 | 131 | [credscan]: https://aka.ms/credscan |
78 | 132 | [credscan_guide]: https://github.com/Azure/azure-sdk-for-python/blob/18611efee7ecf4e591d59b61ba3762d6bdd86304/doc/dev/credscan_process.md |
79 | 133 | [env_loader]: https://github.com/Azure/azure-sdk-for-python/blob/main/eng/tools/azure-sdk-tools/devtools_testutils/envvariable_loader.py |
| 134 | +[environmentvariable]: https://github.com/Azure/azure-sdk-for-python/blob/b6b227edbe318ee79a6a987d063e9823608f3c0a/eng/tools/azure-sdk-tools/devtools_testutils/envvariable_loader.py#L209 |
| 135 | +[environmentvariable_use]: https://github.com/Azure/azure-sdk-for-python/blob/b6b227edbe318ee79a6a987d063e9823608f3c0a/sdk/storage/azure-storage-file-datalake/tests/test_cpk.py#L27 |
80 | 136 | [fake_credentials]: https://github.com/Azure/azure-sdk-for-python/blob/main/eng/tools/azure-sdk-tools/devtools_testutils/fake_credentials.py |
81 | 137 | [fake_credentials_async]: https://github.com/Azure/azure-sdk-for-python/blob/main/eng/tools/azure-sdk-tools/devtools_testutils/fake_credentials_async.py |
82 | 138 | [get_region_override]: https://github.com/Azure/azure-sdk-for-python/blob/4df650d2ce4c292942009ed648cae21eb9c2121d/eng/tools/azure-sdk-tools/devtools_testutils/azure_testcase.py#L52 |
83 | 139 | [is_live]: https://github.com/Azure/azure-sdk-for-python/blob/4df650d2ce4c292942009ed648cae21eb9c2121d/eng/tools/azure-sdk-tools/devtools_testutils/azure_testcase.py#L42 |
| 140 | +[options]: https://github.com/Azure/azure-sdk-for-python/blob/b6b227edbe318ee79a6a987d063e9823608f3c0a/eng/tools/azure-sdk-tools/devtools_testutils/envvariable_loader.py#L20 |
| 141 | +[options_use]: https://github.com/Azure/azure-sdk-for-python/blob/b6b227edbe318ee79a6a987d063e9823608f3c0a/sdk/storage/azure-storage-file-datalake/tests/settings/testcase.py#L45 |
84 | 142 | [retry_counter]: https://github.com/Azure/azure-sdk-for-python/blob/4df650d2ce4c292942009ed648cae21eb9c2121d/eng/tools/azure-sdk-tools/devtools_testutils/helpers.py#L119 |
85 | 143 | [response_callback]: https://github.com/Azure/azure-sdk-for-python/blob/4df650d2ce4c292942009ed648cae21eb9c2121d/eng/tools/azure-sdk-tools/devtools_testutils/helpers.py#L127 |
86 | 144 | [test_resources]: https://github.com/Azure/azure-sdk-for-python/tree/main/eng/common/TestResources#readme |
0 commit comments