From e3640e4e5723645886f982f402a22a0d5ed98114 Mon Sep 17 00:00:00 2001 From: Colby Williams Date: Fri, 14 Oct 2022 12:13:09 -0500 Subject: [PATCH] add output for repo build and bake yaml export --- README.md | 2 +- bake/HISTORY.rst | 5 ++ bake/azext_bake/_help.py | 71 +++++++++++++++--- bake/azext_bake/_params.py | 74 +++++++++++-------- bake/azext_bake/_validators.py | 20 +++++ bake/azext_bake/commands.py | 10 +++ bake/azext_bake/custom.py | 59 +++++++++++++-- .../templates/builder/builder.bicep | 1 + bake/setup.py | 2 +- builder/Dockerfile | 2 +- 10 files changed, 198 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 1f0e686..7807764 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Microsoft Azure CLI Custom Image Helper 'az' Extension adds useful "utilities" f To install the Azure CLI Custom Image Helper extension, simply run the following command: ```sh -az extension add --source https://github.com/colbylwilliams/az-bake/releases/latest/download/bake-0.0.20-py3-none-any.whl -y +az extension add --source https://github.com/colbylwilliams/az-bake/releases/latest/download/bake-0.0.21-py3-none-any.whl -y ``` ### Update diff --git a/bake/HISTORY.rst b/bake/HISTORY.rst index 90cdf32..68e09b4 100644 --- a/bake/HISTORY.rst +++ b/bake/HISTORY.rst @@ -3,6 +3,11 @@ Release History =============== +0.0.21 +++++++ ++ Add bake yaml commands +* Add output to bake repo to track packer + 0.0.20 ++++++ + Try VS images diff --git a/bake/azext_bake/_help.py b/bake/azext_bake/_help.py index 10e7ede..3fc380a 100644 --- a/bake/azext_bake/_help.py +++ b/bake/azext_bake/_help.py @@ -34,16 +34,69 @@ short-summary: Create and manage sandboxes. """ -# helps['bake sandbox create'] = """ -# type: command -# short-summary: Create a sandbox. -# examples: -# - name: Create a sandbox. -# text: az bake sandbox create -n mySandbox -l westus2 -p myPrefix -# - name: Create a sandbox with a custom templates.json file. -# text: az bake sandbox create -n mySandbox -l westus2 -p myPrefix --templates-url -# """ +helps['bake sandbox create'] = """ +type: command +short-summary: Create a sandbox. +examples: + - name: Create a sandbox. + text: az bake sandbox create -sb mySandbox -n myPrefix +""" + +helps['bake sandbox validate'] = """ +type: command +short-summary: Validate a sandbox. +examples: + - name: Validate a sandbox. + text: az bake sandbox validate -sb mySandbox +""" + +helps['bake repo'] = """ +type: group +short-summary: Configure, validate, and bake images in a repo. +""" + +helps['bake repo'] = """ +type: command +short-summary: Bake all images in a repo. +examples: + - name: Build all the images in a repo. + text: az bake repo -r . +""" + +helps['bake repo validate'] = """ +type: command +short-summary: Validate a repo. +examples: + - name: Validate a repo. + text: az bake repo validate -r . +""" + + +helps['bake yaml'] = """ +type: group +short-summary: Export and validate bake.yaml files. +""" + +helps['bake yaml export'] = """ +type: command +short-summary: Export a bake.yaml file. +examples: + - name: Export a bake.yaml file. + text: az bake yaml export -sb MySandbox -g /My/Gallery/Resource/ID +""" + +helps['bake yaml validate'] = """ +type: command +short-summary: Validate a bake.yaml file. +examples: + - name: Validate a bake.yaml file. + text: az bake yaml validate -f ./bake.yaml +""" +helps['bake _builder'] = """ +type: group +short-summary: Used by the builder container to execute packer. +""" # ---------------- # az bake repo diff --git a/bake/azext_bake/_params.py b/bake/azext_bake/_params.py index efb8591..16aff85 100644 --- a/bake/azext_bake/_params.py +++ b/bake/azext_bake/_params.py @@ -3,6 +3,7 @@ # Licensed under the MIT License. # ------------------------------------ +from argcomplete.completers import DirectoriesCompleter, FilesCompleter from azure.cli.core.commands.parameters import ( file_type, get_location_type, get_resource_group_completion_list, tags_type) @@ -12,7 +13,8 @@ from ._validators import (bake_source_version_validator, gallery_resource_id_validator, image_names_validator, repository_path_validator, - sandbox_resource_group_name_validator) + sandbox_resource_group_name_validator, + yaml_out_validator) # get_resource_group_completion_list,) @@ -27,48 +29,32 @@ def load_arguments(self, _): # ) sandbox_resource_group_name_type = CLIArgumentType( - options_list=['--sandbox', '-sb', '-g'], - completer=get_resource_group_completion_list, - # id_part='resource_group', - help="Name of the sandbox resource group. You can configure the default using `az configure --defaults bake-sandbox=`", - configured_default='bake-sandbox', - validator=sandbox_resource_group_name_validator, + options_list=['--sandbox', '-sb', '-g'], configured_default='bake-sandbox', + completer=get_resource_group_completion_list, validator=sandbox_resource_group_name_validator, + help='Name of the sandbox resource group. You can configure the default using `az configure --defaults bake-sandbox=`' ) gallery_resource_id_type = CLIArgumentType( - options_list=['--gallery', '-r'], - completer=get_resource_group_completion_list, - # id_part='name', - help="Resource Id of a Azure Compute Gallery. You can configure the default using `az configure --defaults bake-gallery=`", - configured_default='bake-gallery', - validator=gallery_resource_id_validator, + options_list=['--gallery', '-r'], configured_default='bake-gallery', + completer=get_resource_group_completion_list, validator=gallery_resource_id_validator, + help='Resource Id of a Azure Compute Gallery. You can configure the default using `az configure --defaults bake-gallery=`' ) + # yaml_outfile_type validator also validates yaml_outdir_type and yaml_stdout_type + yaml_outfile_type = CLIArgumentType(options_list=['--outfile'], completer=FilesCompleter(), validator=yaml_out_validator, help='When set, saves the output as the specified file path.') + yaml_outdir_type = CLIArgumentType(options_list=['--outdir'], completer=DirectoriesCompleter(), help='When set, saves the output at the specified directory.') + yaml_stdout_type = CLIArgumentType(options_list=['--stdout'], action='store_true', help='When set, prints all output to stdout instead of corresponding files.') + with self.argument_context('bake upgrade') as c: c.argument('version', options_list=['--version', '-v'], help='Version (tag). Default: latest stable.', validator=bake_source_version_validator, completer=get_version_completion_list) c.argument('prerelease', options_list=['--pre'], action='store_true', help='Update to the latest template prerelease version.') - for scope in ['bake image', 'bake repo', 'bake _builder', 'bake sandbox validate']: - with self.argument_context(scope) as c: - c.ignore('sandbox') - c.ignore('gallery') - - for scope in ['bake image', 'bake _builder']: - with self.argument_context(scope) as c: - c.ignore('image') - - for scope in ['bake image', 'bake sandbox']: - with self.argument_context(scope) as c: - c.argument('sandbox_resource_group_name', sandbox_resource_group_name_type) - - for scope in ['bake image', 'bake sandbox validate']: - with self.argument_context(scope) as c: - c.argument('gallery_resource_id', gallery_resource_id_type) - # sandbox create uses a command level validator, param validators will be ignored with self.argument_context('bake sandbox create') as c: + c.argument('sandbox_resource_group_name', sandbox_resource_group_name_type) + c.argument('gallery_resource_id', gallery_resource_id_type) c.argument('location', get_location_type(self.cli_ctx)) c.argument('tags', tags_type) @@ -110,6 +96,12 @@ def load_arguments(self, _): c.argument('templates_url', arg_group='Advanced', help='URL to custom templates.json file.') c.argument('template_file', arg_group='Advanced', type=file_type, help='Path to custom sandbox arm/bicep template.') + with self.argument_context('bake sandbox validate') as c: + c.argument('sandbox_resource_group_name', sandbox_resource_group_name_type) + c.argument('gallery_resource_id', gallery_resource_id_type) + c.ignore('sandbox') + c.ignore('gallery') + # bake repo uses a command level validator, param validators will be ignored with self.argument_context('bake repo') as c: c.argument('repository_path', options_list=['--repo-path', '-r'], type=file_type, @@ -121,16 +113,36 @@ def load_arguments(self, _): c.argument('repository_token', options_list=['--repo-token'], arg_group='Repo', help='Repository token.') c.argument('repository_revision', options_list=['--repo-revision'], arg_group='Repo', help='Repository revision.') c.ignore('is_ci') - c.ignore('repo') + c.ignore('sandbox') + c.ignore('gallery') c.ignore('images') + c.ignore('repo') # bake image uses a command level validator, param validators will be ignored with self.argument_context('bake image') as c: + c.argument('sandbox_resource_group_name', sandbox_resource_group_name_type) + c.argument('gallery_resource_id', gallery_resource_id_type) c.argument('image_path', options_list=['--image-path', '-i'], type=file_type, help='Path to image to bake.') c.argument('bake_yaml', options_list=['--bake-yaml', '-b'], type=file_type, help='Path to bake.yaml file.') + c.ignore('sandbox') + c.ignore('gallery') + c.ignore('image') + + with self.argument_context('bake yaml export') as c: + c.argument('sandbox_resource_group_name', sandbox_resource_group_name_type) + c.argument('gallery_resource_id', gallery_resource_id_type) + c.argument('outfile', yaml_outfile_type, default='./bake.yml') + c.argument('outdir', yaml_outdir_type) + c.argument('stdout', yaml_stdout_type) + c.ignore('sandbox') + c.ignore('gallery') + c.ignore('images') with self.argument_context('bake _builder') as c: c.ignore('in_builder') + c.ignore('sandbox') + c.ignore('gallery') + c.ignore('image') c.ignore('repo') c.ignore('storage') c.ignore('suffix') diff --git a/bake/azext_bake/_validators.py b/bake/azext_bake/_validators.py index 41f6208..0fd4921 100644 --- a/bake/azext_bake/_validators.py +++ b/bake/azext_bake/_validators.py @@ -426,6 +426,26 @@ def templates_version_validator(cmd, ns): ns.templates_url = f'https://github.com/colbylwilliams/az-bake/releases/download/{ns.version}/templates.json' +def yaml_out_validator(cmd, ns): + if ns.outfile: + if getattr(ns.outfile, 'is_default', None) is None: + if ns.outdir or ns.stdout: + raise MutuallyExclusiveArgumentError( + 'Only use one of --outdir | --outfile | --stdout', + recommendation='Remove all --outdir, --outfile, and --stdout to output a bake.yaml file ' + 'in the current directory, or only specify --stdout to output to stdout.') + ns.outfile = Path(ns.outfile).resolve() + elif ns.outdir and ns.stdout: + raise MutuallyExclusiveArgumentError( + 'Only use one of --outdir | --stdout', + recommendation='Remove all --outdir and --stdout to output a bake.yaml file ' + 'in the current directory, or only specify --stdout to output to stdout.') + else: + ns.outfile = None + if ns.outdir: + ns.outdir = _validate_dir_path(ns.outdir) + + def _validate_object(path, obj, properties, parent_key=None): key_prefix = f'{parent_key}.' if parent_key else '' diff --git a/bake/azext_bake/commands.py b/bake/azext_bake/commands.py index 486bcba..7469ebb 100644 --- a/bake/azext_bake/commands.py +++ b/bake/azext_bake/commands.py @@ -26,10 +26,20 @@ def load_command_table(self, _): # pylint: disable=too-many-statements with self.command_group('bake repo') as g: g.custom_command('validate', 'bake_repo_validate', validator=process_bake_repo_validate_namespace) + with self.command_group('bake yaml') as g: + g.custom_command('export', 'bake_yaml_export') + g.custom_command('validate', 'bake_yaml_validate') + # with self.command_group('bake image') as g: # g.custom_command('test', 'bake_image_test') with self.command_group('bake _builder') as g: g.custom_command('build', 'bake_builder_build', validator=builder_validator) + # with self.command_group('bake validate') as g: + # g.custom_command('image', 'bake_validate_image') + # g.custom_command('repo', 'bake_validate_repo', validator=process_bake_repo_validate_namespace) + # g.custom_command('sandbox', 'bake_validate_sandbox') + # g.custom_command('yaml', 'bake_validate_yaml') + # with self.command_group('bake') diff --git a/bake/azext_bake/custom.py b/bake/azext_bake/custom.py index c96141d..1e4abe2 100644 --- a/bake/azext_bake/custom.py +++ b/bake/azext_bake/custom.py @@ -7,12 +7,13 @@ import json import os +import yaml from knack.log import get_logger from knack.util import CLIError from ._arm import (create_image_definition, deploy_arm_template_at_resource_group, - ensure_gallery_permissions, get_gallery, + ensure_gallery_permissions, get_arm_output, get_gallery, get_image_definition, image_version_exists) from ._github import (get_github_release, get_release_templates, get_template_url) @@ -77,7 +78,14 @@ def bake_builder_build(cmd, in_builder=False, repo=None, storage=None, sandbox=N save_packer_vars_file(sandbox, gallery, image) - return packer_execute(image) + success = packer_execute(image) + + if not success: + raise CLIError('Packer build failed') + else: + logger.info('Packer build succeeded') + + return success # sandbox, gallery, and images come from validator @@ -142,11 +150,20 @@ def bake_repo(cmd, repository_path, is_ci=False, image_names=None, sandbox=None, deployment, outputs = deploy_arm_template_at_resource_group(cmd, sandbox['resourceGroup'], template_file=template_file, template_uri=template_uri, parameters=[image_params]) - logger.info(f'Finished deploying {image["name"]} builder') + logs = get_arm_output(outputs, 'logs') + portal = get_arm_output(outputs, 'portal') + + logger.warning(f'Finished deploying builder for {image["name"]} but packer is still running.') + logger.warning(f'You can check the progress of the packer build:') + logger.warning(f' - Azure CLI: {logs}') + logger.warning(f' - Azure Portal: {portal}') + logger.warning(f'') + deployments.append(deployment) hook.end(message=' ') - return deployments + + return True def bake_repo_validate(cmd, repository_path, sandbox=None, gallery=None, images=None): @@ -196,9 +213,41 @@ def bake_sandbox_create(cmd, location, sandbox_resource_group_name, name_prefix, template_file=template_file, template_uri=template_uri, parameters=[params]) logger.info(f'Finished deploying sandbox') + logger.warning(f'Successfully deployed sandbox environment') + logger.warning(f'You can configure it as your default sandbox using `az configure --defaults bake-sandbox={sandbox_resource_group_name}`') hook.end(message=' ') - return deployment + return True + + +def bake_yaml_export(cmd, sandbox_resource_group_name, gallery_resource_id, sandbox=None, gallery=None, images=None, + outfile='./bake.yml', outdir=None, stdout=False): + logger.info('Exporting bake.yaml file') + + bake_obj = { + 'version': 1.0, + 'sandbox': sandbox, + 'gallery': gallery + } + if images: + bake_obj['images'] = images + + if stdout: + print(yaml.safe_dump(bake_obj, default_flow_style=False, sort_keys=False)) + elif outfile: + with open(outfile, 'w') as f: + yaml.safe_dump(bake_obj, f, default_flow_style=False, sort_keys=False) + elif outdir: + with open(outdir / 'bake.yml', 'w') as f: + yaml.safe_dump(bake_obj, f, default_flow_style=False, sort_keys=False) + + return + + +def bake_yaml_validate(cmd): + logger.info('Validating bake.yaml file') + return True + # raise CLIError('Not implemented') def bake_sandbox_validate(cmd, sandbox_resource_group_name, gallery_resource_id=None, sandbox=None, gallery=None): diff --git a/bake/azext_bake/templates/builder/builder.bicep b/bake/azext_bake/templates/builder/builder.bicep index 4cde8c4..7bcdd53 100644 --- a/bake/azext_bake/templates/builder/builder.bicep +++ b/bake/azext_bake/templates/builder/builder.bicep @@ -158,3 +158,4 @@ resource group 'Microsoft.ContainerInstance/containerGroups@2021-10-01' = { } output logs string = 'az container logs -g ${resourceGroup().name} -n ${validImageName}' +output portal string = 'https://portal.azure.com/#@${tenant().tenantId}${group.id}/containers' diff --git a/bake/setup.py b/bake/setup.py index 1378c69..4f48343 100644 --- a/bake/setup.py +++ b/bake/setup.py @@ -17,7 +17,7 @@ logger.warn("Wheel is not available, disabling bdist_wheel hook") # Must match a HISTORY.rst entry. -VERSION = '0.0.20' +VERSION = '0.0.21' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers diff --git a/builder/Dockerfile b/builder/Dockerfile index 8e757c0..56cb1ce 100644 --- a/builder/Dockerfile +++ b/builder/Dockerfile @@ -38,7 +38,7 @@ LABEL maintainer="Microsoft" \ RUN apk add --no-cache packer --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community # install az-bake -RUN az extension add --source https://github.com/colbylwilliams/az-bake/releases/latest/download/bake-0.0.20-py3-none-any.whl -y +RUN az extension add --source https://github.com/colbylwilliams/az-bake/releases/latest/download/bake-0.0.21-py3-none-any.whl -y # Terminate container on stop STOPSIGNAL SIGTERM