Added sync commands#9824
Conversation
|
| rule | cmd_name | rule_message | suggest_message |
|---|---|---|---|
| workload-orchestration sync | cmd workload-orchestration sync added |
|
Hi @guptahars, |
|
Thank you for your contribution! We will review the pull request and get back to you soon. |
|
The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR. Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions). pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>
|
CodeGen Tools Feedback CollectionThank you for using our CodeGen tool. We value your feedback, and we would like to know how we can improve our product. Please take a few minutes to fill our codegen survey |
|
Hi @guptahars Release SuggestionsModule: workload-orchestration
Notes
|
There was a problem hiding this comment.
Pull request overview
Adds a new az workload-orchestration sync command intended to re-sync workload-orchestration targets (and reinstall deployed solution versions) for a given custom location, including special handling for staged solutions.
Changes:
- Introduces
workload-orchestration syncAAZ command that queries targets via Azure Resource Graph and orchestrates per-target sync. - Adds helper implementation to PUT targets, poll provisioning, query installed solution versions, and (re)install solutions (including staged review/publish/install flow).
- Exposes the new command via the workload_orchestration package
__init__.py.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
src/workload-orchestration/azext_workload_orchestration/aaz/latest/workload_orchestration/_sync.py |
New workload-orchestration sync command, ARG target discovery, interactive selection, and summary output. |
src/workload-orchestration/azext_workload_orchestration/aaz/latest/workload_orchestration/_resync_target_helper.py |
Parallel per-target sync orchestration, ARG solution-version discovery, staged-solution config/review/publish/install, retry + polling logic. |
src/workload-orchestration/azext_workload_orchestration/aaz/latest/workload_orchestration/__init__.py |
Exports the newly added sync command module. |
| def url_parameters(self): | ||
| target_id = self._target["id"] | ||
| parts = target_id.split("/") | ||
| return { | ||
| **self.serialize_url_param("subscriptionId", parts[2], required=True), | ||
| **self.serialize_url_param("resourceGroupName", parts[4], required=True), | ||
| **self.serialize_url_param("targetName", parts[8], required=True), | ||
| } |
There was a problem hiding this comment.
This parses the target ARM ID by fixed segment indices (parts[2], parts[4], parts[8]). If the ID format is unexpected, this will raise IndexError and surface a confusing stack trace. Consider using a safer parse (e.g., validate the split length and keys, or use a resource-id parser helper) and raise a CLIInternalError/InvalidArgumentValueError with the problematic ID.
| sv_parts = self._solution_version_id.split("/") | ||
| solution_unique_id = sv_parts[10] | ||
|
|
There was a problem hiding this comment.
This assumes the solution version ARM ID always has the expected /solutions/{uniqueId}/versions/{version} shape and indexes sv_parts[10]. If the ID format changes or differs, this will throw IndexError. Consider validating the split result (or parsing as a resource ID) and emitting a clearer error that includes the offending solutionVersionId.
| sv_parts = self._solution_version_id.split("/") | |
| solution_unique_id = sv_parts[10] | |
| sv_parts = [part for part in self._solution_version_id.split("/") if part] | |
| try: | |
| solutions_index = sv_parts.index("solutions") | |
| versions_index = sv_parts.index("versions", solutions_index + 1) | |
| except ValueError: | |
| raise Exception( | |
| f"Invalid solutionVersionId format. Expected ARM ID containing " | |
| f"'/solutions/{{uniqueId}}/versions/{{version}}', got: {self._solution_version_id}" | |
| ) | |
| if versions_index != solutions_index + 2: | |
| raise Exception( | |
| f"Invalid solutionVersionId format. Expected ARM ID containing " | |
| f"'/solutions/{{uniqueId}}/versions/{{version}}', got: {self._solution_version_id}" | |
| ) | |
| solution_unique_id = sv_parts[solutions_index + 1] |
| def content(self): | ||
| custom_location = self.ctx.args.custom_location.to_serialized_data() | ||
| body = { | ||
| "query": ( | ||
| "Resources" | ||
| " | where type =~ 'Microsoft.Edge/targets'" | ||
| f" | where extendedLocation.name =~ '{custom_location}'" | ||
| " | where properties.provisioningState =~ 'Succeeded'" | ||
| " | project id, name, location, resourceGroup, subscriptionId," | ||
| " extendedLocation, properties, tags" | ||
| ), | ||
| "options": { | ||
| "resultFormat": "objectArray" | ||
| } | ||
| } | ||
| return self.serialize_content(body) |
There was a problem hiding this comment.
The Resource Graph POST body is missing the required "subscriptions" (or "managementGroups") scope. Without it, /providers/Microsoft.ResourceGraph/resources requests typically fail with a 400. Consider adding "subscriptions": [self.ctx.subscription_id] (or a user-provided list) to the body alongside the query/options.
| # Step 2: Determine which targets to sync | ||
| _log_step("[Step 2/3] Determining which targets to sync...") | ||
| selected_targets = self._targets | ||
| from knack.prompting import prompt | ||
| user_input = prompt( | ||
| "\nEnter the numbers of the targets to sync (e.g. 1,3) or press Enter to sync all: " | ||
| ) | ||
| if user_input.strip(): |
There was a problem hiding this comment.
This command always prompts for target selection, which makes it unusable in non-interactive/automation scenarios (CI, scripts, piping). Consider adding a non-interactive selection argument (e.g., --target-ids/--target-names/--all plus optional --yes/--no-prompt) and only prompting when running in an interactive TTY and no explicit selection was provided.
| """ | ||
|
|
||
| _aaz_info = { | ||
| "version": "2025-06-01", |
There was a problem hiding this comment.
_aaz_info is set to version "2025-06-01" while the rest of the extension’s latest commands appear to use "2025-08-01". If this command targets the same API surface as the other latest commands, align the version metadata to avoid confusing version skew (and to match the declared resources/api-versions used by the operations).
| "version": "2025-06-01", | |
| "version": "2025-08-01", |
| @register_command( | ||
| "workload-orchestration sync", | ||
| ) | ||
| class Sync(AAZCommand): | ||
| """Sync workload orchestration resources for a custom location | ||
|
|
||
| :example: Sync resources for a custom location | ||
| az workload-orchestration sync --custom-location /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ExtendedLocation/customLocations/myCustomLocation | ||
| """ |
There was a problem hiding this comment.
New command behavior isn’t covered by the extension’s existing ScenarioTest suite. Since this adds a new top-level command, consider adding at least one scenario test (and recording) that validates the ARG query wiring and the per-target sync flow (or a minimal mocked/recorded path), especially once a non-interactive selection flag exists.
| def content(self): | ||
| body = { | ||
| "query": ( | ||
| "ExtensibilityResources" | ||
| " | where type =~ 'microsoft.edge/targets/solutions/versions'" | ||
| f" | where tolower(id) startswith tolower('{self._target_id}')" | ||
| " | project id, name, location, resourceGroup, subscriptionId," | ||
| " provisioningState = tostring(properties.provisioningState)," | ||
| " state = tostring(properties.state)," | ||
| " specification = properties.specification" | ||
| ), | ||
| "options": { | ||
| "resultFormat": "objectArray" | ||
| } | ||
| } | ||
| return self.serialize_content(body) |
There was a problem hiding this comment.
The Resource Graph POST body is missing the required "subscriptions" (or "managementGroups") scope. Add a scope (e.g., "subscriptions": [ctx.subscription_id]) so the query executes against the intended subscription(s).
This checklist is used to make sure that common guidelines for a pull request are followed.
Related command
General Guidelines
azdev style <YOUR_EXT>locally? (pip install azdevrequired)python scripts/ci/test_index.py -qlocally? (pip install wheel==0.30.0required)For new extensions:
About Extension Publish
There is a pipeline to automatically build, upload and publish extension wheels.
Once your pull request is merged into main branch, a new pull request will be created to update
src/index.jsonautomatically.You only need to update the version information in file setup.py and historical information in file HISTORY.rst in your PR but do not modify
src/index.json.