Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ARM] az ts export: Fix the issue that export template specs with no linked templates failed #18928

Merged
merged 5 commits into from
Jul 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
"program": "${workspaceRoot}/src/azure-cli/azure/cli/__main__.py",
"cwd": "${workspaceRoot}",
"args": [
"--help"
"ts",
"export",
"-s",
"/subscriptions/a1bfa635-f2bf-42f1-86b5-848c674fc321/resourceGroups/Gokul-TestRG4/providers/Microsoft.Resources/templateSpecs/testTS/versions/1",
"--output-folder",
"C:\\Users\\daetienn\\Desktop\\ExportBugOutput2"
],
"console": "integratedTerminal",
"debugOptions": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import re
import json
from knack.util import CLIError
from azure.cli.core.azclierror import BadRequestError
from azure.cli.core.util import read_file_content, shell_safe_json_parse
from azure.cli.command_modules.resource.custom import _remove_comments_from_json
from azure.cli.core.profiles import ResourceType, get_sdk
Expand Down Expand Up @@ -39,7 +40,7 @@ def process_template(template, preserve_order=True, file_path=None):
try:
return shell_safe_json_parse(result, preserve_order)
except CLIError:
# Because the processing of removing comments and compression will lead to misplacement of error location,
# The processing of removing comments and compression will lead to misplacement of error location,
# so the error message should be wrapped.
if file_path:
raise CLIError("Failed to parse '{}', please check whether it is a valid JSON format".format(file_path))
Expand All @@ -63,7 +64,7 @@ def pack(cmd, template_file):
def _pack_artifacts(cmd, template_abs_file_path, context):
"""
Recursively packs the specified template and its referenced artifacts and
adds the artifacts to the current packing context.
adds the artifact(s) to the current packing context.

:param template_abs_file_path: The path to the template spec .json file to pack.
:type template_abs_file_path : str
Expand Down Expand Up @@ -97,9 +98,10 @@ def _pack_artifacts(cmd, template_abs_file_path, context):

if(not os.path.commonpath([getattr(context, 'RootTemplateDirectory')]) ==
os.path.commonpath([getattr(context, 'RootTemplateDirectory'), abs_local_path])):
raise CLIError('Unable to handle the reference to file ' + abs_local_path + 'from ' +
template_abs_file_path + 'because it exists outside of the root template directory of ' +
getattr(context, 'RootTemplateDirectory'))
raise BadRequestError('Unable to handle the reference to file ' + abs_local_path + 'from ' +
template_abs_file_path +
'because it exists outside of the root template directory of ' +
getattr(context, 'RootTemplateDirectory'))

# Convert the template relative path to one that is relative to our root
# directory path, and then if we haven't already processed that template into
Expand Down Expand Up @@ -189,34 +191,39 @@ def unpack(cmd, exported_template, target_dir, template_file_name):
root_template_file_path = os.path.join(target_dir, template_file_name)

# TODO: Directory/file existence checks..
# Go through each artifact ad make sure it's not going to place artifacts
# Iterate through artifacts to ensure no artifact will be placed
# outside of the target directory:

for artifact in getattr(packaged_template, 'Artifacts'):
local_path = os.path.join(target_dir,
_normalize_directory_seperators_for_local_file_system(getattr(artifact, 'path')))
abs_local_path = os.path.abspath(local_path)
if os.path.commonpath([target_dir]) != os.path.commonpath([target_dir, abs_local_path]):
raise CLIError('Unable to unpack linked template ' + getattr(artifact, 'path') +
'because it would create a file outside of the target directory hierarchy of' + target_dir)

# Now that the artifact paths checkout...let's begin by writing our main template
# file and then processing each artifact:
artifacts = getattr(packaged_template, 'Artifacts')
if artifacts is not None:
for artifact in artifacts:
local_path = os.path.join(target_dir,
_normalize_directory_seperators_for_local_file_system(getattr(artifact, 'path')))
abs_local_path = os.path.abspath(local_path)
if os.path.commonpath([target_dir]) != os.path.commonpath([target_dir, abs_local_path]):
raise BadRequestError('Unable to unpack linked template ' + getattr(artifact, 'path') +
'because it would create a file outside of the target directory hierarchy of ' +
target_dir)

# Process each artifact:

LinkedTemplateArtifact = get_sdk(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_TEMPLATESPECS,
'LinkedTemplateArtifact', mod='models')
for artifact in artifacts:
if not isinstance(artifact, LinkedTemplateArtifact):
raise CLIError('Unknown linked template type encountered...')
artifact_path = _normalize_directory_seperators_for_local_file_system(getattr(artifact, 'path'))
abs_local_path = os.path.abspath(os.path.join(target_dir, artifact_path))
if not os.path.exists(os.path.dirname(abs_local_path)):
os.makedirs(os.path.dirname(abs_local_path))
with open(abs_local_path, 'w') as artifact_file:
json.dump(getattr(artifact, 'template'), artifact_file, indent=2)

# Write our main template file

if not os.path.exists(target_dir):
os.makedirs(os.path.dirname(target_dir))
with open(root_template_file_path, 'w') as root_file:
json.dump(getattr(packaged_template, 'RootTemplate'), root_file, indent=2)

LinkedTemplateArtifact = get_sdk(cmd.cli_ctx, ResourceType.MGMT_RESOURCE_TEMPLATESPECS,
'LinkedTemplateArtifact', mod='models')
for artifact in getattr(packaged_template, 'Artifacts'):
if not isinstance(artifact, LinkedTemplateArtifact):
raise CLIError('Unknown linked template type encountered...')
artifact_path = _normalize_directory_seperators_for_local_file_system(getattr(artifact, 'path'))
abs_local_path = os.path.abspath(os.path.join(target_dir, artifact_path))
if not os.path.exists(os.path.dirname(abs_local_path)):
os.makedirs(os.path.dirname(abs_local_path))
with open(abs_local_path, 'w') as artifact_file:
json.dump(getattr(artifact, 'template'), artifact_file, indent=2)
return target_dir
6 changes: 3 additions & 3 deletions src/azure-cli/azure/cli/command_modules/resource/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ def load_arguments(self, _):
deployment_template_file_type = CLIArgumentType(options_list=['--template-file', '-f'], completer=FilesCompleter(), type=file_type,
help="a path to a template file or Bicep file in the file system")
deployment_template_uri_type = CLIArgumentType(options_list=['--template-uri', '-u'], help='a uri to a remote template file')
deployment_template_spec_type = CLIArgumentType(options_list=['--template-spec', '-s'], is_preview=True, min_api='2019-06-01', help="The template spec resource id.")
deployment_query_string_type = CLIArgumentType(options_list=['--query-string', '-q'], is_preview=True, help="The query string (a SAS token) to be used with the template-uri in the case of linked templates.")
deployment_template_spec_type = CLIArgumentType(options_list=['--template-spec', '-s'], min_api='2019-06-01', help="The template spec resource id.")
deployment_query_string_type = CLIArgumentType(options_list=['--query-string', '-q'], help="The query string (a SAS token) to be used with the template-uri in the case of linked templates.")
deployment_parameters_type = CLIArgumentType(options_list=['--parameters', '-p'], action='append', nargs='+', completer=FilesCompleter(), help='the deployment parameters')
filter_type = CLIArgumentType(options_list=['--filter'], is_preview=True,
help='Filter expression using OData notation. You can use --filter "provisioningState eq \'{state}\'" to filter provisioningState. '
Expand Down Expand Up @@ -94,7 +94,7 @@ def load_arguments(self, _):
ts_display_name_type = CLIArgumentType(options_list=['--display-name', '-d'], help='The display name of the template spec')
ts_description_type = CLIArgumentType(options_list=['--description'], help='The description of the parent template spec.')
ts_version_description_type = CLIArgumentType(options_list=['--version-description'], help='The description of the template spec version.')
ui_form_definition_file_type = CLIArgumentType(options_list=['--ui-form-definition'], is_preview=True, completer=FilesCompleter(), type=file_type,
ui_form_definition_file_type = CLIArgumentType(options_list=['--ui-form-definition'], completer=FilesCompleter(), type=file_type,
help="A path to a uiFormDefinition file in the file system")

_PROVIDER_HELP_TEXT = 'the resource namespace, aka \'provider\''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ def load_command_table(self, _):
g.custom_command('show-log', 'get_deployment_script_logs')
g.custom_command('delete', 'delete_deployment_script', confirmation=True)

with self.command_group('ts', resource_templatespecs_sdk, resource_type=ResourceType.MGMT_RESOURCE_TEMPLATESPECS, is_preview=True, min_api='2019-06-01-preview') as g:
with self.command_group('ts', resource_templatespecs_sdk, resource_type=ResourceType.MGMT_RESOURCE_TEMPLATESPECS, min_api='2019-06-01-preview') as g:
g.custom_command('create', 'create_template_spec', validator=process_ts_create_or_update_namespace)
g.custom_command('update', 'update_template_spec', validator=process_ts_create_or_update_namespace, confirmation=True)
g.custom_command('export', 'export_template_spec', validator=_validate_template_spec_out)
Expand Down
4 changes: 2 additions & 2 deletions src/azure-cli/azure/cli/command_modules/resource/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -907,8 +907,8 @@ def _what_if_deploy_arm_template_core(cli_ctx, what_if_poller, no_pretty_print,

try:
if cli_ctx.enable_color:
# Diabling colorama since it will silently strip out the Xterm 256 color codes the What-If formatter
# is using. Unfortuanately, the colors that colorama supports are very limited, which doesn't meet our needs.
# Disabling colorama since it will silently strip out the Xterm 256 color codes the What-If formatter
# is using. Unfortunately, the colors that colorama supports are very limited, which doesn't meet our needs.
from colorama import deinit
deinit()

Expand Down