Skip to content

Commit

Permalink
Rename PowershellPreparer to EnvironmentVariablesLoader (Azure#22565)
Browse files Browse the repository at this point in the history
* Rename PowershellPreparer to EnvironmentVariablesLoader

* Singular

* cSpell

* Broken links

* Update cspell.json
  • Loading branch information
lmazuel authored Jan 28, 2022
1 parent b567a3f commit 21bb947
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 28 deletions.
4 changes: 3 additions & 1 deletion .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
"deserialization",
"dotenv",
"eastus",
"engsys",
"fileno",
"fqdns",
"fstat",
Expand Down Expand Up @@ -189,6 +190,7 @@
"upserted",
"urandom",
"urlunparse",
"vcrpy",
"verifysdist",
"verifywhl",
"vmimage",
Expand All @@ -209,4 +211,4 @@
}
],
"allowCompoundWords": true
}
}
10 changes: 5 additions & 5 deletions doc/dev/test_proxy_migration_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,15 +214,15 @@ live pipeline testing, requests are made directly to the service instead of goin

Fetching environment variables, passing them directly to tests, and sanitizing their real values can be done all at once
by using the `devtools_testutils`
[PowerShellPreparer](https://github.com/Azure/azure-sdk-for-python/blob/main/tools/azure-sdk-tools/devtools_testutils/powershell_preparer.py).
[EnvironmentVariableLoader](https://github.com/Azure/azure-sdk-for-python/blob/main/tools/azure-sdk-tools/devtools_testutils/envvariable_loader.py).

The name "PowerShellPreparer" comes from its intended use with the PowerShell test resource management commands that are
This loader is nice paired with the PowerShell test resource management commands that are
documented in [/eng/common/TestResources][test_resources]. It's recommended that all test suites use these scripts for
live test resource management.

For an example of using the PowerShellPreparer with the test proxy, you can refer to the Tables SDK. The CosmosPreparer
For an example of using the EnvironmentVariableLoader with the test proxy, you can refer to the Tables SDK. The CosmosPreparer
and TablesPreparer defined in this [preparers.py][tables_preparers] file each define an instance of the
PowerShellPreparer, which are used to fetch environment variables for Cosmos and Tables, respectively. These preparers
EnvironmentVariableLoader, which are used to fetch environment variables for Cosmos and Tables, respectively. These preparers
can be used to decorate test methods directly; for example:

```python
Expand Down Expand Up @@ -270,7 +270,7 @@ class TestExample(AzureRecordedTestCase):
if self.is_live:
table_name = "random-value"
variables = {"table_name": table_name}

# use variables["table_name"] when using the table name throughout the test
...

Expand Down
24 changes: 12 additions & 12 deletions doc/dev/tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,33 +151,33 @@ live-mode: true

## Create Live Test Resources
The Azure Python SDK library has two ways of providing live resources to our tests:
* Using an ArmTemplate and the PowerShellPreparer (we will demonstrate this one)
* [PowerShell preparer implementation](https://github.com/Azure/azure-sdk-for-python/blob/main/tools/azure-sdk-tools/devtools_testutils/powershell_preparer.py)
* Using an ArmTemplate and the EnvironmentVariableLoader (we will demonstrate this one)
* [EnvironmentVariableLoader implementation](https://github.com/Azure/azure-sdk-for-python/blob/main/tools/azure-sdk-tools/devtools_testutils/envvariable_loader.py)
* [In line use](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/schemaregistry/azure-schemaregistry/tests/test_schema_registry.py#L30-L39) for the schemaregistry library
* Using an individualized preparer such as the storage preparer
* [Storage preparer implementation](https://github.com/Azure/azure-sdk-for-python/blob/main/tools/azure-sdk-tools/devtools_testutils/storage_testcase.py)
* [In line use](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/storage/azure-storage-blob/tests/test_blob_client.py#L49-L61) for the blob client

If your library has a management plane library, you can build a preparer specific to your service using the storage preparer as an example. It is recommended that you use a PowerShellPreparer for new libraries and those without management plane libraries. The `PowerShellPreparer` uses the `New-TestResources.ps1` script to deploy resources using an ARM Template. This script and information about running it can be found in the [`eng/common/TestResources`](https://github.com/Azure/azure-sdk-for-python/tree/main/eng/common/TestResources#live-test-resource-management) directory. For more information about the engineering systems in Azure SDK, check out their [wiki][engsys_wiki]
If your library has a management plane library, you can build a preparer specific to your service using the storage preparer as an example. It is recommended that you use a EnvironmentVariableLoader for new libraries and those without management plane libraries. The `EnvironmentVariableLoader` is compatible with the `New-TestResources.ps1` script to deploy resources using an ARM Template. This script and information about running it can be found in the [`eng/common/TestResources`](https://github.com/Azure/azure-sdk-for-python/tree/main/eng/common/TestResources#live-test-resource-management) directory. For more information about the engineering systems in Azure SDK, check out their [wiki][engsys_wiki]

1. Create an Azure Resource Management Template for your specific service and the configuration you need. This can be done in the portal by creating the resources and at the very last step (Review + Create) clicking "Download a template for automation". Save this template to a `test-resources.json` file under the directory that contains your library (`sdk/<my-library>/test-resources.json`).
2. Use the [`New-TestResources.ps1`](https://github.com/Azure/azure-sdk-for-python/tree/main/eng/common/TestResources#on-the-desktop) script to deploy those resources.
3. Set the environment variables returned from step 2 in your current shell or add them to your `.env` file at the root of the repo to save these secrets. If you choose the latter method, you will have to make sure all the key-value pairs are in the format `<key_name>=<value>`, rather than the `${env:<key_name>} = '<value>'` formatting used in PowerShell. The names of the environment variables should be in all capital letters, snake case, and be prefixed with the library name. Ie. `TABLES_PRIMARY_KEY`, `FORMRECOGNIZER_ACCOUNT_URL`, `EVENTHUBS_SECRET_KEY`. If the name of the service is more than one word, like Form Recognizer, don't include an underscore between the words. Use `FORMRECOGNIZER_ACCOUNT_URL`, not `FORM_RECOGNIZER_ACCOUNT_URL`.
4. Create a partial implementation of the PowerShellPreparer to pass in your specific environment variables. An example implementation is shown below for schemaregistry
4. Create a partial implementation of the EnvironmentVariableLoader to pass in your specific environment variables. An example implementation is shown below for schemaregistry

```python
import functools
from devtools_testutils import PowerShellPreparer
from devtools_testutils import EnvironmentVariableLoader

MyServicePreparer = functools.partial(
PowerShellPreparer, "<my_service_directory>",
EnvironmentVariableLoader, "<my_service_directory>",
schemaregistry_endpoint="fake_resource.servicebus.windows.net/",
schemaregistry_group="fakegroup"
)
```

The parameters for the `functools.partial` method are:
* The `PowerShellPreparer` class
* The `EnvironmentVariableLoader` class
* The library folder that holds your code (ie. `sdk/schemaregistry`). This value is used to search your environment variables for the appropriate values.
* The remaining arguments are key-value kwargs, with the keys being the environment variables needed for the tests, and the value being a fake value for replacing the actual value in the recordings. The fake value in this implementation will replace the real value in the recording to make sure the secret keys are not committed to the recordings. These values should closely resemble the values because they are used in playback mode and will need to pass any client side validation. The fake value should also be a unique value to the other key-value pairs.

Expand All @@ -189,12 +189,12 @@ In the `tests` directory create a file with the naming pattern `test_<what_you_a
import functools
import pytest

from devtools_testutils import AzureTestCase, PowerShellPreparer
from devtools_testutils import AzureTestCase, EnvironmentVariableLoader

from azure.schemaregistry import SchemaRegistryClient

SchemaRegistryPreparer = functools.partial(
PowerShellPreparer, 'schemaregistry',
EnvironmentVariableLoader, 'schemaregistry',
schemaregistry_endpoint="fake_resource.servicebus.windows.net/",
schemaregistry_group="fakegroup"
)
Expand All @@ -219,9 +219,9 @@ class TestSchemaRegistry(AzureTestCase):

There's a lot going on in the example so we'll take this piece by piece:

* Import everything you will need in your tests as normal, add to your imports the line `from devtools_testutils import AzureTestCase, PowerShellPreparer`. These two objects give our tests a lot of the desired powers.
* Import everything you will need in your tests as normal, add to your imports the line `from devtools_testutils import AzureTestCase, EnvironmentVariableLoader`. These two objects give our tests a lot of the desired powers.
* `AzureTestCase`: the test class should inherit from this object (`class TestSchemaRegistry(AzureTestCase)`), doing so sets up the recording infrastructure and the client creation methods.
* `PowerShellPreparer`: this preparer serves two purposes.
* `EnvironmentVariableLoader`: this loader serves two purposes.
* First, it will provide the live keys we need to test our library against live resources.
* Second, it will keep those same live keys out of our recordings to make sure that we are not leaking our secrets into the recordings.
* At the top of your test class you should include any helper methods you will need. Most libraries will have a client creation method to eliminate repetitive code.
Expand Down Expand Up @@ -269,7 +269,7 @@ Your update should run smooth and have green dots representing passing tests. No

### Purging Secrets

The `yaml` files created from running tests in live mode store the request and response interactions between the library and the service and this can include authorization, account names, shared access signatures, and other secrets. The recordings are included in our public GitHub repository, making it important for us to remove any secrets from these recordings before committing them to the repository. There are two easy ways to remove secrets. The first is the `PowerShellPreparer` implementation, discussed above. This method will automatically purge the keys with the provided fake values. The second way is to use the `self.scrubber.register_name_pair(key, fake_key)` method (This method is a function of the base `AzureTestCase` class), which is used when a secret is dynamically created during a test. For example, [Tables](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/tables/azure-data-tables/tests/_shared/cosmos_testcase.py#L86-L89) uses this method to replace storage account names with standard names.
The `yaml` files created from running tests in live mode store the request and response interactions between the library and the service and this can include authorization, account names, shared access signatures, and other secrets. The recordings are included in our public GitHub repository, making it important for us to remove any secrets from these recordings before committing them to the repository. There are two easy ways to remove secrets. The first is the `EnvironmentVariableLoader` implementation, discussed above. This method will automatically purge the keys with the provided fake values. The second way is to use the `self.scrubber.register_name_pair(key, fake_key)` method (This method is a function of the base `AzureTestCase` class), which is used when a secret is dynamically created during a test. For example, [Tables](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/tables/azure-data-tables/tests/_shared/cosmos_testcase.py#L86-L89) uses this method to replace storage account names with standard names.

#### Special Case: Shared Access Signature

Expand Down
4 changes: 2 additions & 2 deletions tools/azure-sdk-tools/devtools_testutils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* [`StorageAccountPreparer`][storage_account_preparer]:
* [`CachedStorageAccountPreparer`][cached_storage_account_preparer]:
* [`KeyVaultPreparer`][kv_preparer]:
* [`PowerShellPreparer`][powershell_preparer]: Abstract preparer for delivering secrets from environment variables to individual tests
* [`EnvironmentVariableLoader`][env_loader]: Abstract preparer for delivering secrets from environment variables to individual tests
* [`RetryCounter`][retry_counter]: Object for counting retries on a request.
* [`ResponseCallback`][response_callback]: Object for mocking response callbacks.
* [`FakeCredential`][fake_credential]: Fake credential used for authenticating in playback mode.
Expand All @@ -34,7 +34,7 @@
[storage_account_preparer]: https://github.com/Azure/azure-sdk-for-python/blob/520ea7175e10a971eae9d3e6cd0735efd80447b1/tools/azure-sdk-tools/devtools_testutils/storage_testcase.py#L29
[cached_storage_account_preparer]: https://github.com/Azure/azure-sdk-for-python/blob/master/tools/azure-sdk-tools/devtools_testutils/storage_testcase.py#L140
[kv_preparer]: https://github.com/Azure/azure-sdk-for-python/blob/520ea7175e10a971eae9d3e6cd0735efd80447b1/tools/azure-sdk-tools/devtools_testutils/keyvault_preparer.py#L49
[powershell_preparer]: https://github.com/Azure/azure-sdk-for-python/blob/520ea7175e10a971eae9d3e6cd0735efd80447b1/tools/azure-sdk-tools/devtools_testutils/powershell_preparer.py#L14
[env_loader]: https://github.com/Azure/azure-sdk-for-python/blob/main/tools/azure-sdk-tools/devtools_testutils/envvariable_loader.py#L15
[retry_counter]: https://github.com/Azure/azure-sdk-for-python/blob/ab7e7f1a7b2a6d7255abdc77a40e2d6a86c9de0a/tools/azure-sdk-tools/devtools_testutils/helpers.py#L6
[response_callback]: https://github.com/Azure/azure-sdk-for-python/blob/ab7e7f1a7b2a6d7255abdc77a40e2d6a86c9de0a/tools/azure-sdk-tools/devtools_testutils/helpers.py#L14
[fake_credential]: https://github.com/Azure/azure-sdk-for-python/blob/65ffc49fbdd0f4f83e68eb5c8e0c6d293f0569cd/tools/azure-sdk-tools/devtools_testutils/fake_credential.py
5 changes: 4 additions & 1 deletion tools/azure-sdk-tools/devtools_testutils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
CachedStorageAccountPreparer,
)
from .keyvault_preparer import KeyVaultPreparer
from .powershell_preparer import PowerShellPreparer
# cSpell:disable
from .envvariable_loader import EnvironmentVariableLoader
PowerShellPreparer = EnvironmentVariableLoader # Backward compat
from .proxy_docker_startup import start_test_proxy, stop_test_proxy, test_proxy
from .proxy_testcase import recorded_by_proxy
from .sanitizers import (
Expand Down Expand Up @@ -60,6 +62,7 @@
"RandomNameResourceGroupPreparer",
"CachedResourceGroupPreparer",
"PowerShellPreparer",
"EnvironmentVariableLoader",
"recorded_by_proxy",
"test_proxy",
"set_bodiless_matcher",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from .sanitizers import add_general_regex_sanitizer


class PowerShellPreparer(AzureMgmtPreparer):
class EnvironmentVariableLoader(AzureMgmtPreparer):
def __init__(
self,
directory,
Expand All @@ -24,7 +24,7 @@ def __init__(
preparers=None,
**kwargs
):
super(PowerShellPreparer, self).__init__(
super(EnvironmentVariableLoader, self).__init__(
name_prefix,
24,
disable_recording=disable_recording,
Expand Down Expand Up @@ -81,13 +81,13 @@ def create_resource(self, name, **kwargs):
logger = logging.getLogger()
logger.info(
"This test class instance has no scrubber and a sanitizer could not be registered "
"with the test proxy, so the PowerShellPreparer will not scrub the value of {} in "
"with the test proxy, so the EnvironmentVariableLoader will not scrub the value of {} in "
"recordings.".format(key)
)
else:
template = 'To pass a live ID you must provide the scrubbed value for recordings to \
prevent secrets from being written to files. {} was not given. For example: \
@PowerShellPreparer("schemaregistry", schemaregistry_endpoint="fake_endpoint.servicebus.windows.net")'
@EnvironmentVariableLoader("schemaregistry", schemaregistry_endpoint="fake_endpoint.servicebus.windows.net")'
raise AzureTestError(template.format(key))
except KeyError as key_error:
if not self._backup_preparers:
Expand All @@ -96,9 +96,9 @@ def create_resource(self, name, **kwargs):
self.real_values = {}
create_kwargs = {}
for preparer in self._backup_preparers:
resource_name, vals = preparer._prepare_create_resource(self.test_class_instance, **create_kwargs)
# vals = preparer.create_resource(name, **create_kwargs)
self.real_values.update(vals)
resource_name, values = preparer._prepare_create_resource(self.test_class_instance, **create_kwargs)
# values = preparer.create_resource(name, **create_kwargs)
self.real_values.update(values)
if "resource_group" in self.real_values.keys():
create_kwargs["resource_group"] = self.real_values["resource_group"]

Expand Down

0 comments on commit 21bb947

Please sign in to comment.