diff --git a/src/aks-preview/HISTORY.rst b/src/aks-preview/HISTORY.rst index 649067381d2..60c1a2d7d95 100644 --- a/src/aks-preview/HISTORY.rst +++ b/src/aks-preview/HISTORY.rst @@ -12,6 +12,11 @@ To release a new version, please select a new version number (usually plus 1 to Pending +++++++ +0.5.72 +++++++ + +* First public release for `az aks draft` + 0.5.71 ++++++ diff --git a/src/aks-preview/azext_aks_preview/_help.py b/src/aks-preview/azext_aks_preview/_help.py index bf7637f9258..93f36656fb1 100644 --- a/src/aks-preview/azext_aks_preview/_help.py +++ b/src/aks-preview/azext_aks_preview/_help.py @@ -1704,3 +1704,192 @@ type: command short-summary: Delete a nodepool snapshot. """ + +helps['aks draft'] = """ + type: group + short-summary: Commands to build deployment files in a project directory and deploy to an AKS cluster. +""" + +helps['aks draft create'] = """ + type: command + short-summary: Generate a Dockerfile and the minimum required Kubernetes deployment files (helm, kustomize, manifests) for your project directory. + parameters: + - name: --destination + type: string + short-summary: Specify the path to the project directory (default is .). + - name: --app + type: string + short-summary: Specify the name of the helm release. + - name: --language + type: string + short-summary: Specify the language used to create the Kubernetes deployment. + - name: --create-config + type: string + short-summary: Specify the path to the configuration file. + - name: --dockerfile-only + type: bool + short-summary: Only generate Dockerfile for the Kubernetes deployment. + - name: --deployment-only + type: bool + short-summary: Only generate deployment files (helm, kustomize, manifests) for the Kubernetes deployment. + - name: --path + type: string + short-summary: Automatically download and use the Draft binary at the specified location. + examples: + - name: Prompt to generate a Dockerfile and deployment files in the current directory. + text: az aks draft create + - name: Generate only the Dockerfile in the current directory. + text: az aks draft create --dockerfile-only=true + - name: Generate only the deployment files in the current directory. + text: az aks draft create --deployment-only=true + - name: Generate a Dockerfile and an deployment file in a Java project with an app name at a specific project directory. + text: az aks draft create --language=java --app=some_app --destination=/projects/some_project +""" + +helps['aks draft setup-gh'] = """ + type: command + short-summary: Set up Github OIDC for your application + parameters: + - name: --app + type: string + short-summary: Specify the Azure Active Directory applicaton name. + - name: --subscription-id + type: string + short-summary: Specify the Azure subscription ID. + - name: --resource-group + type: string + short-summary: Specify the name of the Azure resource group. + - name: --provider + type: string + short-summary: Specify the cloud provider (default is azure). + - name: --gh-repo + type: string + short-summary: Specify the the github repository (organization/repo_name). + - name: --path + type: string + short-summary: Automatically download and use the Draft binary at the specified location. + examples: + - name: Prompt to setup the Github OIDC for a repository. + text: az aks draft setup-gh + - name: Setup the github OIDC on Azure for a specific repository. + text: az aks draft setup-gh --provider=azure --gh-repo=some_organization/some_repo + - name: Setup the github OIDC on Azure with subscription ID and resource group. + text: az aks draft setup-gh --provider=azure --subscription-id=some_subscription --resource-group=some_rg + - name: Setup the github OIDC with an application name on Azure with subscription ID and resource group for a specific repository. + text: az aks draft setup-gh --app=some_app --provider=azure --subscription-id=some_subscription --resource-group=some_rg --gh-repo=some_organization/some_repo +""" + +helps['aks draft generate-workflow'] = """ + type: command + short-summary: Generate a Github workflow for automatic build and deploy to AKS + long-summary: Before running this command, Make sure you have set up Github OIDC for your application. + You also need to create a resource group, a container registry and a Kubernetes cluster on Azure and + link the three resources using `az aks update -n -g --attach-acr `. + parameters: + - name: --resource-group + type: string + short-summary: Specify the name of the Azure resource group. + - name: --destination + type: string + short-summary: Specify the path to the project directory (default is .). + - name: --cluster-name + type: string + short-summary: Specify the AKS cluster name. + - name: --registry-name + type: string + short-summary: Specify the path to the project directory. + - name: --container-name + type: string + short-summary: Specify the name of the container image. + - name: --branch + type: string + short-summary: Specify the Github branch to automatically deploy from. + - name: --path + type: string + short-summary: Automatically download and use the Draft binary at the specified location. + examples: + - name: Prompt to generate a Github workflow in the current directory. + text: az aks draft generate-workflow + - name: Prompt to generate a Github workflow in a specific project directory. + text: az aks draft generate-workflow --destination=/projects/some_project + - name: Generate a Github workflow with a resource group, an AKS cluster name, a container registry name in a specific project directory. + text: az aks draft generate-workflow --resource-group=some_rg --cluster-name=some_cluster --registry-name=some_registry --destination=/projects/some_project + - name: Generate a Github workflow that deploys from the main branch with a resource group, an AKS cluster name, a container registry name, and a container image name in a specific project directory. + text: az aks draft generate-workflow --branch=main --resource-group=some_rg --cluster-name=some_cluster --registry-name=some_registry --container-name=some_image --destination=/projects/some_project +""" + +helps['aks draft up'] = """ + type: command + short-summary: Set up Github OIDC and generate a Github workflow for automatic build and deploy to AKS + long-summary: This command combines `az aks draft setup-gh` and `az aks draft generate-workflow`. + Before running this command, create a resource group, a container registry and a Kubernetes cluster on Azure and + link the three resources using `az aks update -n -g --attach-acr `. + parameters: + - name: --app + type: string + short-summary: Specify the name of the application. + - name: --subscription-id + type: string + short-summary: Specify the Azure subscription ID. + - name: --resource-group + type: string + short-summary: Specify the name of the Azure resource group. + - name: --provider + type: string + short-summary: Specify the cloud provider (default is azure). + - name: --gh-repo + type: string + short-summary: Specify the the github repository (organization/repo_name). + - name: --cluster-name + type: string + short-summary: Specify the AKS cluster name. + - name: --registry-name + type: string + short-summary: Specify the path to the project directory. + - name: --container-name + type: string + short-summary: Specify the name of the container image. + - name: --destination + type: string + short-summary: Specify the path to the project directory (default is .). + - name: --branch + type: string + short-summary: Specify the Github branch to automatically deploy from. + - name: --path + type: string + short-summary: Automatically download and use the Draft binary at the specified location. + examples: + - name: Prompt to setup the Github OIDC then generate a Github workflow in the current directory. + text: az aks draft up + - name: Prompt to setup the Github OIDC then generate a Github workflow in a specific project directory. + text: az aks draft up --destination=/projects/some_project + - name: Prompt to setup the Github OIDC for a specific repository then generate a Github workflow in a specific project directory. + text: az aks draft up --gh-repo=some_organization/some_repo --destination=/projects/some_project +""" + +helps['aks draft update'] = """ + type: command + short-summary: Update your application to be internet accessible. + long-summary: This command automatically updates your yaml files as necessary so that your + application will be able to receive external requests. + parameters: + - name: --host + type: string + short-summary: Specify the host of the ingress resource. + - name: --certificate + type: string + short-summary: Specify the URI of the Keyvault certificate to present. + - name: --destination + type: string + short-summary: Specify the path to the project directory (default is .). + - name: --path + type: string + short-summary: Automatically download and use the Draft binary at the specified location. + examples: + - name: Prompt to update the application to be internet accessible. + text: az aks draft update + - name: Prompt to update the application to be internet accessible in a specific project directory. + text: az aks draft update --destination=/projects/some_project + - name: Update the application to be internet accessible with a host of the ingress resource and a Keyvault certificate in a specific project directory. + text: az aks draft update --host=some_host --certificate=some_certificate --destination=/projects/some_project +""" diff --git a/src/aks-preview/azext_aks_preview/aks_draft/commands.py b/src/aks-preview/azext_aks_preview/aks_draft/commands.py new file mode 100644 index 00000000000..015127a2db9 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/aks_draft/commands.py @@ -0,0 +1,294 @@ +# pylint: disable=too-many-lines +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import json +from posixpath import dirname +from typing import Dict, List, Optional, Tuple +import subprocess +import requests +import os +import platform +from pathlib import Path +from knack.prompting import prompt_y_n +import logging + + +# `az aks draft create` function +def aks_draft_cmd_create(destination: str, + app_name: str, + language: str, + create_config: str, + dockerfile_only: str, + deployment_only: str, + download_path: str) -> None: + file_path, arguments = _pre_run(download_path, + destination=destination, + app_name=app_name, + language=language, + create_config=create_config, + dockerfile_only=dockerfile_only, + deployment_only=deployment_only) + run_successful = _run(file_path, 'create', arguments) + if run_successful: + _run_finish() + else: + raise ValueError('`az aks draft create` was NOT executed successfully') + + +# `az aks draft setup-gh` function +def aks_draft_cmd_setup_gh(app: str, + subscription_id: str, + resource_group: str, + provider: str, + gh_repo: str, + download_path: str) -> None: + file_path, arguments = _pre_run(download_path, + app=app, + subscription_id=subscription_id, + resource_group=resource_group, + provider=provider, + gh_repo=gh_repo) + run_successful = _run(file_path, 'setup-gh', arguments) + if run_successful: + _run_finish() + else: + raise ValueError('`az aks draft setup-gh` was NOT executed successfully') + + +# `az aks draft generate-workflow` function +def aks_draft_cmd_generate_workflow(cluster_name: str, + registry_name: str, + container_name: str, + resource_group: str, + destination: str, + branch: str, + download_path: str) -> None: + file_path, arguments = _pre_run(download_path, + cluster_name=cluster_name, + registry_name=registry_name, + container_name=container_name, + resource_group=resource_group, + destination=destination, + branch=branch) + run_successful = _run(file_path, 'generate-workflow', arguments) + if run_successful: + _run_finish() + else: + raise ValueError('`az aks draft generate-workflow` was NOT executed successfully') + + +# `az aks draft up` function +def aks_draft_cmd_up(app: str, + subscription_id: str, + resource_group: str, + provider: str, + gh_repo: str, + cluster_name: str, + registry_name: str, + container_name: str, + destination: str, + branch: str, + download_path: str) -> None: + file_path = _binary_pre_check(download_path) + if not file_path: + raise ValueError('Binary check was NOT executed successfully') + + setup_gh_args = _build_args(app=app, + subscription_id=subscription_id, + resource_group=resource_group, + provider=provider, + gh_repo=gh_repo) + + run_successful = _run(file_path, 'setup-gh', setup_gh_args) + if not run_successful: + raise ValueError('`az aks draft setup-gh` was NOT executed successfully') + + generate_workflow_args = _build_args(cluster_name=cluster_name, + registry_name=registry_name, + container_name=container_name, + resource_group=resource_group, + destination=destination, + branch=branch) + run_successful = _run(file_path, 'generate-workflow', generate_workflow_args) + if run_successful: + _run_finish() + else: + raise ValueError('`az aks draft generate-workflow` was NOT executed successfully') + + +# `az aks draft update` function +def aks_draft_cmd_update(host: str, certificate: str, destination: str, download_path: str) -> None: + file_path, arguments = _pre_run(download_path, host=host, certificate=certificate, destination=destination) + run_successful = _run(file_path, 'update', arguments) + if run_successful: + _run_finish() + else: + raise ValueError('`az aks draft update` was NOT executed successfully') + + +# Returns binary file path and arguments +def _pre_run(download_path: str, **kwargs) -> Tuple[str, List[str]]: + file_path = _binary_pre_check(download_path) + if not file_path: + raise ValueError('Binary check was NOT executed successfully') + arguments = _build_args(kwargs) + return file_path, arguments + + +# Executes the Draft command +# Returns True if the process executed sucessfully, False otherwise +def _run(binary_path: str, command: str, arguments: List[str]) -> bool: + if binary_path is None: + raise ValueError('The given Binary path was null or empty') + + logging.info(f'Running `az aks draft {command}`') + cmd = [binary_path, command] + arguments + process = subprocess.Popen(cmd) + exit_code = process.wait() + return exit_code == 0 + + +# Function for clean up logic +def _run_finish(): + # Clean up logic can go here if needed + logging.info('Finished running Draft command') + + +def _build_args(args_dict: Dict[str, str] = None, **kwargs) -> List[str]: + if not args_dict: + args_dict = kwargs + args_list = [] + for key, val in args_dict.items(): + arg = key.replace('_', '-') + if val: + args_list.append(f'--{arg}={val}') + return args_list + + +# Returns the path to Draft binary. None if missing the required binary +def _binary_pre_check(download_path: str) -> Optional[str]: + # if user specifies a download path, download the draft binary to this location and use it as a path + if download_path: + return _download_binary(download_path) + + logging.info('The Draft binary check is in progress...') + draft_binary_path = _get_existing_path() + + if draft_binary_path: # found binary + if _is_latest_version(draft_binary_path): # no need to update + logging.info('Your local version of Draft is up to date.') + else: # prompt the user to update + msg = 'We have detected a newer version of Draft. Would you like to download it?' + response = prompt_y_n(msg, default='n') + if response: + return _download_binary() + return draft_binary_path + else: # prompt the user to download binary + # If users says no, we error out and tell them that this requires the binary + msg = 'The required binary was not found. Would you like us to download the required binary for you?' + + if not prompt_y_n(msg, default='n'): + raise ValueError('`az aks draft` requires the missing dependency') + + return _download_binary() + + +# Returns the latest version str of Draft on Github +def _get_latest_version() -> str: + response = requests.get('https://api.github.com/repos/Azure/draft/releases/latest') + response_json = json.loads(response.text) + return response_json.get('tag_name') + + +# Returns True if the local binary is the latest version, False otherwise +def _is_latest_version(binary_path: str) -> bool: + latest_version = _get_latest_version() + process = subprocess.Popen([binary_path, 'version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + if stderr.decode(): + return False + # return string of result is "version: v0.0.x" + current_version = stdout.decode().split('\n')[0].strip().split()[-1] + return latest_version == current_version + + +# Returns the filename for the current os and architecture +# Returns None if the current system is not supported in Draft +def _get_filename() -> Optional[str]: + operating_system = platform.system().lower() + architecture = platform.machine().lower() + + if architecture == 'x86_64': + architecture = 'amd64' + if architecture not in ['arm64', 'amd64']: + logging.error('Cannot find a suitable download for the current system architecture. Draft only supports AMD64 and ARM64.') + return None + + return f'draft-{operating_system}-{architecture}' + + +# Returns path to existing draft binary, None otherwise +def _get_existing_path() -> Optional[str]: + logging.info('Checking if Draft binary exists locally...') + + filename = _get_filename() + if not filename: + return None + + paths = _get_potential_paths() + if not paths: + logging.error('List of potential Draft paths is empty') + return None + + for path in paths: + binary_file_path = path + '/' + filename + if os.path.exists(binary_file_path): + logging.info('Existing binary found at: ' + binary_file_path) + return binary_file_path + return None + + +# Returns a list of potential draftV2 binary paths +def _get_potential_paths() -> List[str]: + paths = os.environ['PATH'].split(':') + # the download location of _download_binary() + default_dir = str(Path.home()) + '/' + '.aksdraft' + paths.append(default_dir) + + return paths + + +# Downloads the latest binary to ~/.aksdraft +# Returns path to the binary if sucessful, None otherwise +def _download_binary(download_path: str = '~/.aksdraft') -> Optional[str]: + logging.info('Attempting to download dependency...') + download_path = os.path.expanduser(download_path) + filename = _get_filename() + if not filename: + return None + + url = f'https://github.com/Azure/draft/releases/latest/download/{filename}' + headers = {'Accept': 'application/octet-stream'} + + # Downloading the file by sending the request to the URL + response = requests.get(url, headers=headers) + + if response.ok: + # Directory + if os.path.exists(download_path) is False: + Path(download_path).mkdir(parents=True, exist_ok=True) + logging.info(f'Directory {download_path} was created inside of your HOME directory') + full_path = f'{download_path}/{filename}' + + # Writing the file to the local file system + with open(full_path, 'wb') as output_file: + output_file.write(response.content) + logging.info(f'Download of Draft binary was successful with a status code: {response.status_code}') + os.chmod(full_path, 0o755) + return full_path + + logging.error(f'Download of Draft binary was unsuccessful with a status code: {response.status_code}') + return None diff --git a/src/aks-preview/azext_aks_preview/commands.py b/src/aks-preview/azext_aks_preview/commands.py index f9618d4801e..078a0bdcd6d 100644 --- a/src/aks-preview/azext_aks_preview/commands.py +++ b/src/aks-preview/azext_aks_preview/commands.py @@ -122,6 +122,14 @@ def load_command_table(self, _): g.custom_command('stop', 'aks_agentpool_stop', supports_no_wait=True) g.custom_command('start', 'aks_agentpool_start', supports_no_wait=True) + # AKS draft commands + with self.command_group('aks draft', managed_clusters_sdk, client_factory=cf_managed_clusters) as g: + g.custom_command('create', 'aks_draft_create') + g.custom_command('setup-gh', 'aks_draft_setup_gh') + g.custom_command('generate-workflow', 'aks_draft_generate_workflow') + g.custom_command('up', 'aks_draft_up') + g.custom_command('update', 'aks_draft_update') + # AKS pod identity commands with self.command_group('aks pod-identity', managed_clusters_sdk, client_factory=cf_managed_clusters) as g: g.custom_command('add', 'aks_pod_identity_add') diff --git a/src/aks-preview/azext_aks_preview/custom.py b/src/aks-preview/azext_aks_preview/custom.py index 7995a35e8f3..592ca5ea6ef 100644 --- a/src/aks-preview/azext_aks_preview/custom.py +++ b/src/aks-preview/azext_aks_preview/custom.py @@ -149,6 +149,14 @@ UserAssignedIdentity, ) +from .aks_draft.commands import ( + aks_draft_cmd_create, + aks_draft_cmd_setup_gh, + aks_draft_cmd_generate_workflow, + aks_draft_cmd_up, + aks_draft_cmd_update +) + logger = get_logger(__name__) @@ -2860,6 +2868,55 @@ def _get_http_proxy_config(file_path): return config_object +def aks_draft_create(destination='.', + app=None, + language=None, + create_config=None, + dockerfile_only=None, + deployment_only=None, + path=None): + aks_draft_cmd_create(destination, app, language, create_config, dockerfile_only, deployment_only, path) + + +def aks_draft_setup_gh(app=None, + subscription_id=None, + resource_group=None, + provider="azure", + gh_repo=None, + path=None): + aks_draft_cmd_setup_gh(app, subscription_id, resource_group, provider, gh_repo, path) + + +def aks_draft_generate_workflow(cluster_name=None, + registry_name=None, + container_name=None, + resource_group=None, + destination=None, + branch=None, + path=None): + aks_draft_cmd_generate_workflow(cluster_name, registry_name, container_name, + resource_group, destination, branch, path) + + +def aks_draft_up(app=None, + subscription_id=None, + resource_group=None, + provider="azure", + gh_repo=None, + cluster_name=None, + registry_name=None, + container_name=None, + destination=None, + branch=None, + path=None): + aks_draft_cmd_up(app, subscription_id, resource_group, provider, gh_repo, + cluster_name, registry_name, container_name, destination, branch, path) + + +def aks_draft_update(host=None, certificate=None, destination=None, path=None): + aks_draft_cmd_update(host, certificate, destination, path) + + def aks_pod_identity_add(cmd, client, resource_group_name, cluster_name, identity_name, identity_namespace, identity_resource_id, binding_selector=None, diff --git a/src/aks-preview/azext_aks_preview/tests/latest/aks_draft_config/helm.yaml b/src/aks-preview/azext_aks_preview/tests/latest/aks_draft_config/helm.yaml new file mode 100644 index 00000000000..fd0d2cb0df5 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/aks_draft_config/helm.yaml @@ -0,0 +1,10 @@ +deployType: "Helm" +languageType: "python" +deployVariables: + - name: "PORT" + value: "5000" + - name: "APPNAME" + value: "testapp" +languageVariables: + - name: "PORT" + value: "5000" diff --git a/src/aks-preview/azext_aks_preview/tests/latest/aks_draft_config/kustomize.yaml b/src/aks-preview/azext_aks_preview/tests/latest/aks_draft_config/kustomize.yaml new file mode 100644 index 00000000000..c19ebc3a880 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/aks_draft_config/kustomize.yaml @@ -0,0 +1,10 @@ +deployType: "kustomize" +languageType: "python" +deployVariables: + - name: "PORT" + value: "5000" + - name: "APPNAME" + value: "testapp" +languageVariables: + - name: "PORT" + value: "5000" diff --git a/src/aks-preview/azext_aks_preview/tests/latest/aks_draft_config/manifest.yaml b/src/aks-preview/azext_aks_preview/tests/latest/aks_draft_config/manifest.yaml new file mode 100644 index 00000000000..e6b31863167 --- /dev/null +++ b/src/aks-preview/azext_aks_preview/tests/latest/aks_draft_config/manifest.yaml @@ -0,0 +1,10 @@ +deployType: "manifests" +languageType: "python" +deployVariables: + - name: "PORT" + value: "5000" + - name: "APPNAME" + value: "testapp" +languageVariables: + - name: "PORT" + value: "5000" diff --git a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py index e9b385fafb0..36a93079d63 100644 --- a/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py +++ b/src/aks-preview/azext_aks_preview/tests/latest/test_aks_commands.py @@ -4174,3 +4174,75 @@ def test_aks_create_with_apiserver_vnet_integration(self, resource_group, resour self.cmd(cmd, checks=[ self.is_empty(), ]) + + @live_only() # this test requires live_only because a binary is downloaded + def test_aks_draft_with_helm(self): + import tempfile, os + + script_dir = os.path.dirname(__file__) + create_config = 'aks_draft_config/helm.yaml' + abs_file_path = os.path.join(script_dir, create_config) + + with tempfile.TemporaryDirectory() as tmp_dir: + # test `create` + create_cmd = f'aks draft create --path={tmp_dir} --create-config={abs_file_path} --destination={tmp_dir}' + self.cmd(create_cmd) + assert os.path.isdir(f'{tmp_dir}/charts') and os.path.isfile(f'{tmp_dir}/Dockerfile') + + # test `generate-workflow` + generate_workflow_cmd = f'aks draft generate-workflow --path={tmp_dir} --branch=main --destination={tmp_dir} --cluster-name=someAksCluster --registry-name=someRegistry --resource-group=someResourceGroup --container-name=someContainer' + self.cmd(generate_workflow_cmd) + assert os.path.isfile(f'{tmp_dir}/charts/production.yaml') and os.path.isfile(f'{tmp_dir}/.github/workflows/azure-kubernetes-service-helm.yml') + + # test `update` + update_cmd = f'aks draft update --path={tmp_dir} --destination={tmp_dir} --host=testHost --certificate=testKV' + self.cmd(update_cmd) + assert os.path.isfile(f'{tmp_dir}/charts/production.yaml') + + @live_only() # this test requires live_only because a binary is downloaded + def test_aks_draft_with_kustomize(self): + import tempfile, os + + script_dir = os.path.dirname(__file__) + create_config = 'aks_draft_config/kustomize.yaml' + abs_file_path = os.path.join(script_dir, create_config) + + with tempfile.TemporaryDirectory() as tmp_dir: + # test `create` + create_cmd = f'aks draft create --path={tmp_dir} --create-config={abs_file_path} --destination={tmp_dir}' + self.cmd(create_cmd) + assert os.path.isdir(f'{tmp_dir}/base') and os.path.isdir(f'{tmp_dir}/overlays/production') and os.path.isfile(f'{tmp_dir}/Dockerfile') + + # test `generate-workflow` + generate_workflow_cmd = f'aks draft generate-workflow --path={tmp_dir} --branch=main --destination={tmp_dir} --cluster-name=someAksCluster --registry-name=someRegistry --resource-group=someResourceGroup --container-name=someContainer' + self.cmd(generate_workflow_cmd) + assert os.path.isfile(f'{tmp_dir}/overlays/production/deployment.yaml') and os.path.isfile(f'{tmp_dir}/.github/workflows/azure-kubernetes-service-kustomize.yml') + + # test `update` + update_cmd = f'aks draft update --path={tmp_dir} --destination={tmp_dir} --host=testHost --certificate=testKV' + self.cmd(update_cmd) + assert os.path.isfile(f'{tmp_dir}/overlays/production/service.yaml') + + @live_only() # this test requires live_only because a binary is downloaded + def test_aks_draft_with_manifest(self): + import tempfile, os + + script_dir = os.path.dirname(__file__) + create_config = 'aks_draft_config/manifest.yaml' + abs_file_path = os.path.join(script_dir, create_config) + + with tempfile.TemporaryDirectory() as tmp_dir: + + create_cmd = f'aks draft create --path={tmp_dir} --create-config={abs_file_path} --destination={tmp_dir}' + self.cmd(create_cmd) + assert os.path.isdir(f'{tmp_dir}/manifests') and os.path.isfile(f'{tmp_dir}/Dockerfile') + + # test `generate-workflow` + generate_workflow_cmd = f'aks draft generate-workflow --path={tmp_dir} --branch=main --destination={tmp_dir} --cluster-name=someAksCluster --registry-name=someRegistry --resource-group=someResourceGroup --container-name=someContainer' + self.cmd(generate_workflow_cmd) + assert os.path.isfile(f'{tmp_dir}/.github/workflows/azure-kubernetes-service.yml') + + # test `update` + update_cmd = f'aks draft update --path={tmp_dir} --destination={tmp_dir} --host=testHost --certificate=testKV' + self.cmd(update_cmd) + assert os.path.isfile(f'{tmp_dir}/manifests/service.yaml') \ No newline at end of file