Conversation
️✔️Azure CLI Extensions Breaking Change Test
|
|
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>
|
|
Hi @micromaomao
|
51ea85c to
e4d1736
Compare
…cy config for vn2
And add a fix to framework for rw_mount
e4d1736 to
631c766
Compare
…l 2.0.0 Suggested-by: Ken Gordon <kegordo@microsoft.com>
|
@yonzhan @necusjz How do we properly set the next version to 2.0.0b1? The pipeline insists that it has to be 1.8.1 for some reason: #9776 (comment) |
There was a problem hiding this comment.
Pull request overview
This PR adds support in the confcom extension for generating policies for C-WCOW (Windows confidential containers), extending the existing Linux policy generation flow with Windows-specific policy fields, hashing outputs, and Rego boilerplate.
Changes:
- Add Windows platform detection and propagate per-image
platformthrough policy generation, hashing, and policy serialization. - Extend hashing/layer metadata handling to support Windows CIM-based hashing output (
mounted_cim) and update dmverity-vhd binary version. - Introduce Windows-specific debug-mode settings and a Windows Rego policy template; bump API/version metadata for the extension.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/confcom/setup.py | Bumps extension version to 2.0.0b1. |
| src/confcom/azext_confcom/tests/latest/test_confcom_containers_from_image.py | Updates test call signature for containers_from_image. |
| src/confcom/azext_confcom/template_util.py | Adds platform into image info and attempts platform-aware pulls. |
| src/confcom/azext_confcom/security_policy.py | Tracks single platform per policy, selects Linux vs Windows Rego boilerplate, and passes platform into hashing. |
| src/confcom/azext_confcom/rootfs_proxy.py | Updates dmverity-vhd version and switches hashing output parsing to JSON with optional mounted_cim. |
| src/confcom/azext_confcom/lib/policy.py | Bumps policy API version to 0.11.0. |
| src/confcom/azext_confcom/lib/images.py | Adds platform-aware pull logic and exposes get_image_platform; adds platform parameter to layer hashing. |
| src/confcom/azext_confcom/lib/defaults.py | Adds helper to pick Linux vs Windows debug execProcesses. |
| src/confcom/azext_confcom/lib/containers.py | Adds platform to container definitions derived from images and hashes layers for that platform. |
| src/confcom/azext_confcom/data/internal_config.json | Bumps API version and adds Windows debug-mode configuration. |
| src/confcom/azext_confcom/data/customer_rego_policy_windows.txt | Adds Windows-specific customer policy boilerplate. |
| src/confcom/azext_confcom/data/customer_rego_policy.txt | Adds rw_mount_device framework binding. |
| src/confcom/azext_confcom/data/README | Adds a note about data files (currently inconsistent with actual usage). |
| src/confcom/azext_confcom/custom.py | Updates _containers_from_image invocation parameter name. |
| src/confcom/azext_confcom/container.py | Adds platform/mounted_cim fields and Windows-specific policy element differences. |
| src/confcom/azext_confcom/config.py | Adds config wiring for Windows debug mode and Windows Rego template file. |
| src/confcom/azext_confcom/command/containers_from_vn2.py | Updates container_from_image invocation parameter name. |
| src/confcom/azext_confcom/command/containers_from_image.py | Renames function parameter to aci_or_vn2. |
| src/confcom/azext_confcom/azext_metadata.json | Marks the extension as preview. |
| src/confcom/azext_confcom/_help.py | Clarifies that --platform must be aci or vn2 for containers from_image. |
| src/confcom/README.md | Documents Windows-only CIM dependency expectations. |
| src/confcom/HISTORY.rst | Adds release notes for 2.0.0b1 (formatting currently incorrect). |
Comments suppressed due to low confidence (2)
src/confcom/azext_confcom/container.py:829
env_rulesis set todict()for non-Linux, but the code later doesenv_rules += _INJECTED_SERVICE_VN2_ENV_RULESand passes it toset_extra_environment_rules, both of which expect a list. This will raise at runtime for Windows containers. Initializeenv_rulesas an empty list instead (and keep the list type consistent across branches).
# Start with the customer environment rules
env_rules = copy.deepcopy(_INJECTED_CUSTOMER_ENV_RULES) if container_json["platform"].startswith("linux") else dict()
# If is_vn2, add the VN2 environment rules
if is_vn2:
env_rules += _INJECTED_SERVICE_VN2_ENV_RULES
image.set_mounts(image.get_mounts() + copy.deepcopy(config.DEFAULT_MOUNTS_VIRTUAL_NODE))
src/confcom/azext_confcom/rootfs_proxy.py:100
layer_cacheis keyed only byimage:tag, but the returned content now depends onplatform(and potentiallytar_location/faster_hashing). This can return incorrect cached results when the same image is hashed for different platforms/options in the same process. Includeplatform(and other inputs that affect output) in the cache key.
image_name = f"{image}:{tag}"
# populate layer info
if self.layer_cache.get(image_name):
return self.layer_cache.get(image_name)
| @functools.lru_cache() | ||
| def pull_image(image_reference: str) -> docker.models.images.Image: | ||
| client = docker.from_env() | ||
|
|
||
| for platform in SUPPORTED_PLATFORMS: | ||
| try: | ||
| image = client.images.pull(image_reference, platform=platform) | ||
| return image | ||
| except (docker.errors.ImageNotFound, docker.errors.NotFound): | ||
| continue | ||
|
|
||
| raise ValueError(f"Image '{image_reference}' not found for any supported platform: {SUPPORTED_PLATFORMS}") |
There was a problem hiding this comment.
pull_image() always uses client.images.pull(...) and never checks client.images.get(...) first. This breaks local-only images (e.g., tests build confcom_test_* locally) because platform detection will attempt a remote pull and fail even though the image exists locally. Consider trying images.get(image_reference) first (and only pulling when not present), or have get_image_platform() use get_image() to support local images.
| mounts=mounts, | ||
| allow_elevated=allow_elevated, | ||
| extraEnvironmentRules=[], | ||
| platform=container_json["platform"], |
There was a problem hiding this comment.
ContainerImage.from_json now requires container_json["platform"], which will raise a KeyError for older inputs (e.g., previously generated JSON/policies without a platform field). Use a default such as container_json.get("platform", "linux/amd64") (and similarly for other direct indexing) to preserve backward compatibility.
| platform=container_json["platform"], | |
| platform=container_json.get("platform", "linux/amd64"), |
| for platform in ["linux/amd64", "windows/amd64"]: | ||
| try: | ||
| raw_image = client.images.pull(image_name, platform=platform) | ||
| break | ||
| except (docker.errors.ImageNotFound, docker.errors.NotFound): | ||
| continue | ||
| image_info = { | ||
| "platform": "/".join([raw_image.attrs.get("Os"), raw_image.attrs.get("Architecture")]), | ||
| **raw_image.attrs.get("Config") | ||
| } |
There was a problem hiding this comment.
If pulling the image fails for all attempted platforms, raw_image remains None and the subsequent raw_image.attrs... access will raise an AttributeError. After the pull loop, explicitly handle the “not found for any supported platform” case (e.g., raise/print an error) before building image_info.
| pretty_print_func(self._allow_runtime_logging), | ||
| pretty_print_func(self._allow_environment_variable_dropping), | ||
| ) | ||
|
|
There was a problem hiding this comment.
_add_rego_boilerplate has no final else/error for unsupported platforms, so it can return None and break callers expecting a string. Add an explicit error/exception when self._platform is not a recognized linux/* or windows/* value.
| raise ValueError(f"Unsupported platform for rego boilerplate generation: {self._platform}") |
| # Use platform from template if specified, otherwise try to auto-detect from image | ||
| platform = case_insensitive_dict_get(image_properties, "platform") | ||
| if not platform: |
There was a problem hiding this comment.
Platform auto-detection here calls get_image_platform(image_name), which pulls images via Docker. This introduces a hard Docker dependency even for the “tarball / clean-room” flow where get_image_info can read image metadata from a tar without Docker running. Consider deferring platform resolution until after get_image_info (or deriving it from the tar metadata) so tar-only usage doesn’t regress.
| # Use platform from template if specified, otherwise try to auto-detect from image | |
| platform = case_insensitive_dict_get(image_properties, "platform") | |
| if not platform: | |
| # Use platform from template if specified. Otherwise, prefer image metadata | |
| # so tarball-based flows do not require Docker for platform detection. | |
| platform = case_insensitive_dict_get(image_properties, "platform") | |
| if not platform: | |
| image_info = get_image_info(image_name) | |
| if isinstance(image_info, dict): | |
| platform = case_insensitive_dict_get(image_info, "platform") | |
| if not platform: | |
| image_os = case_insensitive_dict_get(image_info, "os") | |
| image_architecture = case_insensitive_dict_get(image_info, "architecture") | |
| if image_os and image_architecture: | |
| platform = f"{image_os}/{image_architecture}" | |
| else: | |
| platform = getattr(image_info, "platform", None) | |
| if not platform: | |
| image_os = getattr(image_info, "os", None) | |
| image_architecture = getattr(image_info, "architecture", None) | |
| if image_os and image_architecture: | |
| platform = f"{image_os}/{image_architecture}" | |
| if not platform: |
| image_info = { | ||
| "platform": "/".join([raw_image.attrs.get("Os"), raw_image.attrs.get("Architecture")]), | ||
| **raw_image.attrs.get("Config") | ||
| } |
There was a problem hiding this comment.
get_image_info builds image_info via {..., **raw_image.attrs.get("Config")}. If Config is missing/None, this will throw a TypeError. Safer is config = raw_image.attrs.get("Config") or {} (and then splat that), so the function doesn’t crash on unexpected image metadata.
| else: | ||
| assert self._platform == image_platform, "All images must have the same platform" |
There was a problem hiding this comment.
Using assert for user-facing validation (All images must have the same platform) is unsafe because assertions can be disabled with Python optimizations (-O). Replace the assert with a normal exception (e.g., ValueError) or an eprint(...) + exit path so the check is always enforced.
| else: | |
| assert self._platform == image_platform, "All images must have the same platform" | |
| elif self._platform != image_platform: | |
| raise ValueError("All images must have the same platform") |
| @@ -0,0 +1 @@ | |||
| internal_config.json and customer_rego_policy.txt are obsolete versions to be removed, new work should happen in src/confcom/azext_confcom/lib/policy.py | |||
There was a problem hiding this comment.
The new note claims internal_config.json and customer_rego_policy.txt are obsolete, but both are still actively loaded/used by config.py (e.g., CONFIG_FILE = ./data/internal_config.json, CUSTOMER_REGO_POLICY = load_str_from_file(...)). This documentation is currently incorrect and will mislead future maintenance; update it to reflect the current source of truth.
| internal_config.json and customer_rego_policy.txt are obsolete versions to be removed, new work should happen in src/confcom/azext_confcom/lib/policy.py | |
| internal_config.json and customer_rego_policy.txt are still used by the current configuration/runtime path (for example via config.py) and are not obsolete; keep them in sync with the code that loads them. Related policy logic also exists in src/confcom/azext_confcom/lib/policy.py. |
| =============== | ||
|
|
||
| 2.0.0b1 | ||
| +++++ |
There was a problem hiding this comment.
The reStructuredText section underline length doesn’t match the header length (2.0.0b1 has 7 characters but the underline is only 5 +). This will produce docutils/Sphinx formatting warnings; adjust the underline to match the version string length (consistent with other entries like 1.8.0).
| +++++ | |
| +++++++ |
|
/azp run |
|
Azure Pipelines successfully started running 2 pipeline(s). |
…t_device enforcement point This fixes unit tests
|
Azure Pipelines: 2 pipeline(s) require an authorized user to comment /azp run to run. |
This is not, in fact, where parameters and variables are populated. That happens in the constructor for AciPolicy.
… parameters/variables
|
Azure Pipelines: 2 pipeline(s) require an authorized user to comment /azp run to run. |
|
/azp run |
|
Commenter does not have sufficient privileges for PR 9776 in repo Azure/azure-cli-extensions |
Add support for generating C-WCOW (Windows confidential containers) policies.
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.