Skip to content

Commit 82c2a2a

Browse files
mccoypscbedd
andauthored
Add asset management script (Azure#27676)
Co-authored-by: scbedd <[email protected]>
1 parent a5ba93c commit 82c2a2a

File tree

2 files changed

+161
-5
lines changed

2 files changed

+161
-5
lines changed

doc/dev/tests.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -394,20 +394,26 @@ sdk/{service}/{package}/assets.json;2Km2Z8755;python/{service}/{package}_<10-cha
394394
```
395395
The recording directory in this case is `2Km2Z8755`, the string between the two semicolons.
396396

397-
After verifying that your recording updates look correct, you can use the `manage_recordings.py` script from
398-
`azure-sdk-for-python/scripts` to push these recordings to the `azure-sdk-assets` repo. For example, from the root of
399-
the `azure-sdk-for-python` repo:
397+
After verifying that your recording updates look correct, you can use the [`manage_recordings.py`][manage_recordings]
398+
script from `azure-sdk-for-python/scripts` to push these recordings to the `azure-sdk-assets` repo. This script accepts
399+
a verb and a **relative** path to your package's `assets.json` file. For example, from the root of the
400+
`azure-sdk-for-python` repo:
400401
```
401402
python scripts/manage_recordings.py push sdk/{service}/{package}/assets.json
402403
```
403404

404-
**NOTE:** The `manage_recordings.py` script is still under development at the time of writing. To push updated
405-
recordings in the meantime, refer to https://github.com/Azure/azure-sdk-tools/blob/main/tools/test-proxy/documentation/asset-sync/README.md#pushing-new-recordings.
405+
The verbs that can be provided to this script are "push", "restore", and "reset":
406+
- **push**: pushes recording updates to a new assets repo tag and updates the tag pointer in `assets.json`.
407+
- **restore**: fetches recordings from the assets repo, based on the tag pointer in `assets.json`.
408+
- **reset**: discards any pending changes to recordings, based on the tag pointer in `assets.json`.
406409

407410
After pushing your recordings, the `assets.json` file for your package will be updated to point to a new `Tag` that
408411
contains the updates. Include this `assets.json` update in any pull request to update the recordings pointer in the
409412
upstream repo.
410413

414+
More details about the recording management commands and management script are documented in
415+
[`manage_recordings.py`][manage_recordings].
416+
411417
### Sanitize secrets
412418

413419
The `.json` files created from running tests in live mode can include authorization details, account names, shared
@@ -708,6 +714,7 @@ Tests that use the Shared Access Signature (SAS) to authenticate a client should
708714
[kv_test_resources_outputs]: https://github.com/Azure/azure-sdk-for-python/blob/fbdb860630bcc13c1e355828231161849a9bd5a4/sdk/keyvault/test-resources.json#L255
709715
[kv_test_resources_resources]: https://github.com/Azure/azure-sdk-for-python/blob/fbdb860630bcc13c1e355828231161849a9bd5a4/sdk/keyvault/test-resources.json#L116
710716

717+
[manage_recordings]: https://github.com/Azure/azure-sdk-for-python/blob/main/scripts/manage_recordings.py
711718
[mgmt_settings_fake]: https://github.com/Azure/azure-sdk-for-python/blob/main/tools/azure-sdk-tools/devtools_testutils/mgmt_settings_fake.py
712719

713720
[packaging]: https://github.com/Azure/azure-sdk-for-python/blob/main/doc/dev/packaging.md

scripts/manage_recordings.py

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# -------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the MIT License. See License.txt in the project root for
4+
# license information.
5+
# --------------------------------------------------------------------------
6+
7+
import argparse
8+
import os
9+
import shlex
10+
import sys
11+
12+
try:
13+
from dotenv import load_dotenv
14+
15+
load_dotenv()
16+
except:
17+
pass
18+
19+
import subprocess
20+
21+
22+
# This file contains a script for managing test recordings in the azure-sdk-assets repository with Docker.
23+
#
24+
# INSTRUCTIONS FOR USE:
25+
#
26+
# - Set GIT_TOKEN, GIT_COMMIT_OWNER, and GIT_COMMIT_EMAIL environment variables to authenticate git requests.
27+
# These can be set in-process or added to a .env file at the root of or directory above your local copy of the
28+
# azure-sdk-for-python repository.
29+
# - Set your working directory to be inside your local copy of the azure-sdk-for-python repository.
30+
# - Run the following command:
31+
#
32+
# `python {path to script}/manage_recordings.py {verb} {relative path to package's assets.json file}`
33+
#
34+
# For example, with the root of the azure-sdk-for-python repo as the working directory, you can push modified
35+
# azure-keyvault-keys recordings to the assets repo with:
36+
#
37+
# `python scripts/manage_recordings.py push sdk/keyvault/azure-keyvault-keys/assets.json`
38+
#
39+
# - In addition to "push", you can also use the "restore" or "reset" verbs in the same command format.
40+
#
41+
# * push: pushes recording updates to a new assets repo tag and updates the tag pointer in `assets.json`.
42+
# * restore: fetches recordings from the assets repo, based on the tag pointer in `assets.json`.
43+
# * reset: discards any pending changes to recordings, based on the tag pointer in `assets.json`.
44+
#
45+
# For more information about how recording asset synchronization, please refer to
46+
# https://github.com/Azure/azure-sdk-tools/blob/main/tools/test-proxy/documentation/asset-sync/README.md.
47+
48+
# Load environment variables from user's .env file
49+
50+
CONTAINER_NAME = "azsdkengsys.azurecr.io/engsys/test-proxy"
51+
GIT_TOKEN = os.getenv("GIT_TOKEN", "")
52+
GIT_OWNER = os.getenv("GIT_COMMIT_OWNER", "")
53+
GIT_EMAIL = os.getenv("GIT_COMMIT_EMAIL", "")
54+
55+
56+
# ----- HELPERS ----- #
57+
58+
59+
discovered_roots = []
60+
61+
62+
def ascend_to_root(start_dir_or_file: str) -> str:
63+
"""
64+
Given a path, ascend until encountering a folder with a `.git` folder present within it. Return that directory.
65+
66+
:param str start_dir_or_file: The starting directory or file. Either is acceptable.
67+
"""
68+
if os.path.isfile(start_dir_or_file):
69+
current_dir = os.path.dirname(start_dir_or_file)
70+
else:
71+
current_dir = start_dir_or_file
72+
73+
while current_dir is not None and not (os.path.dirname(current_dir) == current_dir):
74+
possible_root = os.path.join(current_dir, ".git")
75+
76+
# we need the git check to prevent ascending out of the repo
77+
if os.path.exists(possible_root):
78+
if current_dir not in discovered_roots:
79+
discovered_roots.append(current_dir)
80+
return current_dir
81+
else:
82+
current_dir = os.path.dirname(current_dir)
83+
84+
raise Exception(f'Requested target "{start_dir_or_file}" does not exist within a git repo.')
85+
86+
87+
def delete_container() -> None:
88+
"""Delete container if it remained"""
89+
proc = subprocess.Popen(
90+
shlex.split(f"docker rm -f {CONTAINER_NAME}"),
91+
stdout=subprocess.PIPE,
92+
stderr=subprocess.PIPE,
93+
stdin=subprocess.DEVNULL,
94+
)
95+
output, stderr = proc.communicate(timeout=10)
96+
return None
97+
98+
99+
def get_image_tag(repo_root: str) -> str:
100+
"""Gets the test proxy Docker image tag from the target_version.txt file in /eng/common/testproxy"""
101+
version_file_location = os.path.relpath("eng/common/testproxy/target_version.txt")
102+
version_file_location_from_root = os.path.abspath(os.path.join(repo_root, version_file_location))
103+
104+
with open(version_file_location_from_root, "r") as f:
105+
image_tag = f.read().strip()
106+
107+
return image_tag
108+
109+
110+
# ----- CORE LOGIC ----- #
111+
112+
113+
if not (GIT_TOKEN and GIT_OWNER and GIT_EMAIL):
114+
raise ValueError(
115+
"GIT_TOKEN, GIT_COMMIT_OWNER, and GIT_COMMIT_EMAIL environment variables must be set, "
116+
"either in-process or in a .env file"
117+
)
118+
119+
# Prepare command arguments
120+
parser = argparse.ArgumentParser(description="Script for managing recording assets with Docker.")
121+
parser.add_argument("verb", help='The action verb for managing recordings: "restore", "push", or "reset".')
122+
parser.add_argument(
123+
"path",
124+
default="assets.json",
125+
help='The *relative* path to your package\'s `assets.json` file. Default is "assets.json".',
126+
)
127+
args = parser.parse_args()
128+
129+
if args.verb and args.path:
130+
131+
current_directory = os.getcwd()
132+
repo_root = ascend_to_root(current_directory)
133+
image_tag = get_image_tag(repo_root)
134+
135+
root_path = os.path.abspath(repo_root)
136+
cwd_relpath = os.path.relpath(current_directory, root_path)
137+
assets_path = os.path.join(cwd_relpath, args.path).replace("\\", "/")
138+
139+
delete_container() # Delete any lingering container so a new one can be created with necessary environment variables
140+
141+
subprocess.run(
142+
shlex.split(
143+
f'docker run --rm -v "{repo_root}:/srv/testproxy" '
144+
f'-e "GIT_TOKEN={GIT_TOKEN}" -e "GIT_COMMIT_OWNER={GIT_OWNER}" -e "GIT_COMMIT_EMAIL={GIT_OWNER}" '
145+
f"{CONTAINER_NAME}:{image_tag} test-proxy {args.verb.lower()} -a {assets_path}"
146+
),
147+
stdout=sys.stdout,
148+
stderr=sys.stderr,
149+
)

0 commit comments

Comments
 (0)