Skip to content

feat: add Essentials SKU support to IoT Operations create - #964

Merged
Ketki Naik (ketkimnaik) merged 7 commits into
devfrom
users/ketkinaik/aio-essentials-sku-it
Oct 12, 2026
Merged

Ketki Naik (ketkimnaik) merged 7 commits into
devfrom
users/ketkinaik/aio-essentials-sku-it

Conversation

@ketkimnaik

@ketkimnaik Ketki Naik (ketkimnaik) commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • add create-only --sku {Essentials,Standard} support
  • force opcua.mode=Disabled for Essentials and warn on conflicting OPC UA input
  • pass SKU through the generated deployment template parameters without changing omission behavior
  • validate SKU through show/list integration assertions and add an Essentials integration scenario
  • sanitize truncated integration namespace names

Testing

Live-instance testing:
image

This Azure-owned branch is intended to run the integration-test workflows.

ketkinaik2611 and others added 6 commits September 29, 2026 14:22
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep omitted SKU absent even when regenerated templates contain their own SKU expression, and forward SKU when the template exposes a matching parameter.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Strip trailing hyphens after limiting ADR namespace names so longer scenarios such as Essentials remain valid.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@ketkimnaik Ketki Naik (ketkimnaik) changed the title Add Essentials SKU support to IoT Operations create feat: add Essentials SKU support to IoT Operations create Oct 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

SKU plumbing, compatibility enforcement, omission behavior, and integration coverage are consistent and complete.

0 open findings

What changed in this PR

Adds create-time Essentials/Standard SKU selection for IoT Operations while enforcing Essentials compatibility constraints.

Changes:

  • Adds --sku and propagates it into deployment parameters.
  • Forces OPC UA disabled for Essentials with conflict warnings.
  • Adds unit/integration coverage and namespace sanitization.
File Description
azext_edge/​edge/​commands_edge.py Applies Essentials behavior and forwards SKU.
azext_edge/​edge/​params.py Defines the create-only --sku argument.
azext_edge/​edge/​_help.py Adds an Essentials creation example.
azext_edge/​edge/​providers/​orchestration/​common.py Defines supported SKU values.
azext_edge/​edge/​providers/​orchestration/​targets.py Adds SKU to deployment parameters.
azext_edge/​tests/​edge/​orchestration/​test_work_unit.py Tests Essentials feature enforcement.
azext_edge/​tests/​edge/​orchestration/​test_targets_unit.py Tests SKU template propagation and omission.
azext_edge/​tests/​edge/​init/​int/​test_init_int.py Verifies SKU through show/list integration assertions.
.github/​test-scenarios.yml Adds an Essentials integration scenario.
.github/​actions/​setup-cluster-resources/​action.yml Removes trailing hyphens from truncated namespace names.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@zzcodework

Copy link
Copy Markdown

Azure SRE Agent - automated review

Reviewing full diff (head e173c4a3)

10 files, +122/-3. Adds az iot ops create --sku {Essentials,Standard}, forces opcua.mode=Disabled for Essentials, plumbs the value into the instance blueprint's sku parameter, and adds an essentials-sku CI scenario. No blocking defects found.

Blocking

None.

Suggestions

  1. The Essentials ↔ OPC UA constraint is enforced on create only — az iot ops update can undo it.
    azext_edge/edge/commands_edge.py applies _apply_essentials_sku inside create_instance, but Instances.update / Instances._update (azext_edge/edge/providers/orchestration/resources/instances.py) never read instance["sku"]. So on an instance created with --sku Essentials, az iot ops update --feature opcua.mode=Stable sets enable_opcua = True, resolves a connector version, writes features.opcua.mode = Stable, and backfills the default OPC UA connector template — with no warning and no error. Because the --sku help text states the SKU "cannot be changed after creation", the instance is then permanently in a state the SKU is documented not to support. A guard in update (reject or warn when the live sku.name == "Essentials" and the desired opcua.mode is not Disabled) would close the loop. Raising this as a suggestion rather than a blocker because it is a pre-existing surface this PR does not regress — but this PR is what creates the reachable state.

  2. No test covers the command-level wiring of --sku.
    test_apply_essentials_sku (test_work_unit.py) exercises the helper in isolation and test_init_targets_instance_sku (test_targets_unit.py) exercises InitTargets → template, but nothing asserts that create_instance(sku="Essentials", instance_features=["opcua.mode=Stable"]) actually reaches WorkManager.execute_ops_init with the rewritten feature list and sku="Essentials". The if sku == IoTOperationsSku.ESSENTIALS.value: branch in commands_edge.py is the one line connecting the two tested halves, and it is currently only covered by the int scenario. test_work_unit.py already mocks WorkManager, so this is cheap to add.

Nits

  1. azext_edge/tests/edge/init/int/test_init_int.py — .get("sku", {}) does not tolerate an explicit null.
    instance_show.get("sku", {}).get("name") (and the same pattern on the az iot ops list result) raises AttributeError: 'NoneType' object has no attribute 'get' if the RP returns "sku": null rather than omitting the key — which is exactly the shape the blueprint's [if(not(equals(parameters('sku'), null())), ...)] expression produces when the parameter is absent. That turns a "SKU was not persisted" regression into an opaque traceback instead of a clean assertion failure. (instance_show.get("sku") or {}).get("name") matches the idiom already used elsewhere in the repo, e.g. (features.get("opcua") or {}).get("mode") in upgrade2.py.

  2. .github/actions/setup-cluster-resources/action.yml — the trailing-hyphen fix covers one of the two truncating lines.
    The new | sed 's/-*$//' is applied to the ADR namespace name (init-adr-ns-${{ inputs.suffix }}), which genuinely needs it with the new essentials-sku scenario name. The sibling init-registry${{ inputs.suffix }} line a few lines above still does a bare cut -c 1-30; with essentials-sku it happens to land mid-word today only because the run number is 4 digits (a 3-digit run number would cut to ...essentials-). Not reachable going forward since run numbers only increase, but applying the same sed to both would make the invariant independent of the run-number width.

Checked and clean

  • --sku is not silently dropped under --use-preview. _handle_apply_targets only forwards a parameter when param in built_in_template_params, so a blueprint missing sku would discard it without error. Both blueprints declare it: template.py and template_preview.py each carry "sku": {"type": "string", "allowedValues": ["Essentials", "Standard"], "nullable": True} plus the matching [if(not(equals(parameters('sku'), null())), createObject('name', parameters('sku')), null())] on aioInstance. These are already on dev — this PR does not need a template change.
  • Plumbing is complete: create_instance → execute_ops_init(sku=..., **kwargs) → InitTargets(subscription_id=..., **kwargs) → self.sku → param_to_target. work.py needs no edit because execute_ops_init forwards **kwargs verbatim. _handle_apply_targets skips None values, which is what makes assert "sku" not in instance_parameters hold for the None case.
  • Case handling is safe. get_enum_type installs an action that normalises the typed value to the declared choice, so --sku essentials arrives as "Essentials" and matches both IoTOperationsSku.ESSENTIALS.value and ARM's allowedValues.
  • The feature filter covers every reachable key. COMPAT_FEAT_KEY_SET == {"opcua.mode"} and parse_feature_kvp_nargs(..., strict=True) rejects anything else, so feature.partition("=")[0] != "opcua.mode" cannot leave a stale OPC UA-related feature behind.
  • upgrade will not re-introduce OPC UA on an Essentials instance. upgrade2._check_opcua_connector_template_needed returns early when opcua_mode == "Disabled", so no default connector template is backfilled.
  • The new az iot ops list assertion will see the field. Instances.list returns the SDK payload unprojected (list_by_resource_group / list_by_subscription).
  • A scenario with no tox_env still runs the assertions. The deploy + test_init_int.py step in int_test.yml is not gated on tox_env; only the scenario-specific pytest step is — same shape as the existing redeploy scenario.
  • No HISTORY.rst entry is required in this repo (CONTRIBUTING.md does not ask for one; HISTORY.rst on dev is a baseline stub).

This is an automated review and may be incomplete.

@ketkimnaik
Ketki Naik (ketkimnaik) merged commit f3cbe62 into dev Oct 12, 2026
55 of 77 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants