From 17b5ea408c69c6e75cb3fe14187ca0b22d8501fd Mon Sep 17 00:00:00 2001 From: Sisira Panchagnula Date: Tue, 27 Nov 2018 15:20:17 -0800 Subject: [PATCH 1/8] Adding suppport for up command as preview --- azure-cli2017.pyproj | 6 + .../command_modules/appservice/_constants.py | 19 ++ .../appservice/_create_util.py | 252 ++++++++++++++++++ .../cli/command_modules/appservice/_help.py | 21 ++ .../cli/command_modules/appservice/_params.py | 5 + .../command_modules/appservice/commands.py | 1 + .../cli/command_modules/appservice/custom.py | 212 ++++++++++++++- 7 files changed, 509 insertions(+), 7 deletions(-) create mode 100644 src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py create mode 100644 src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py diff --git a/azure-cli2017.pyproj b/azure-cli2017.pyproj index f1e09e0e224..afd37e34312 100644 --- a/azure-cli2017.pyproj +++ b/azure-cli2017.pyproj @@ -256,6 +256,12 @@ + + Code + + + Code + diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py new file mode 100644 index 00000000000..12a05aeaed9 --- /dev/null +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py @@ -0,0 +1,19 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +NODE_VERSION_DEFAULT = "8.11" +NETCORE_VERSION_DEFAULT = "2.0" +DOTNET_VERSION_DEFAULT = "4.7" +PYTHON_VERSION_DEFAULT = "3.7" +NETCORE_RUNTIME_NAME = "dotnetcore" +DOTNET_RUNTIME_NAME = "aspnet" +NODE_RUNTIME_NAME = "node" +PYTHON_RUNTIME_NAME = "python" +OS_DEFAULT = "Windows" +STATIC_RUNTIME_NAME = "static" # not an oficial supported runtime but used for CLI logic +NODE_VERSIONS = ['4.4', '4.5', '6.2', '6.6', '6.9', '6.11', '8.0', '8.1', '8.9', '8.11'] +NETCORE_VERSIONS = ['1.0', '1.1', '2.0'] +DOTNET_VERSIONS = ['3.5', '4.7'] + diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py new file mode 100644 index 00000000000..5eb921ae174 --- /dev/null +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py @@ -0,0 +1,252 @@ + +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import os +import zipfile +from azure.cli.core.commands.client_factory import get_mgmt_service_client +from azure.mgmt.resource.resources.models import ResourceGroup +from ._constants import ( + NETCORE_VERSION_DEFAULT, + NETCORE_VERSIONS, + NODE_VERSION_DEFAULT, + NODE_VERSIONS, + NETCORE_RUNTIME_NAME, + NODE_RUNTIME_NAME, + DOTNET_RUNTIME_NAME, + DOTNET_VERSION_DEFAULT, + DOTNET_VERSIONS, + STATIC_RUNTIME_NAME, + PYTHON_RUNTIME_NAME, + PYTHON_VERSION_DEFAULT) + + +def _resource_client_factory(cli_ctx, **_): + from azure.cli.core.profiles import ResourceType + return get_mgmt_service_client(cli_ctx, ResourceType.MGMT_RESOURCE_RESOURCES) + + +def web_client_factory(cli_ctx, **_): + from azure.mgmt.web import WebSiteManagementClient + return get_mgmt_service_client(cli_ctx, WebSiteManagementClient) + + +def zip_contents_from_dir(dirPath, lang): + relroot = os.path.abspath(os.path.join(dirPath, os.pardir)) + path_and_file = os.path.splitdrive(dirPath)[1] + file_val = os.path.split(path_and_file)[1] + zip_file_path = relroot + os.path.sep + file_val + ".zip" + abs_src = os.path.abspath(dirPath) + with zipfile.ZipFile("{}".format(zip_file_path), "w", zipfile.ZIP_DEFLATED) as zf: + for dirname, subdirs, files in os.walk(dirPath): + # skip node_modules folder for Node apps, + # since zip_deployment will perfom the build operation + if lang.lower() == NODE_RUNTIME_NAME and 'node_modules' in subdirs: + subdirs.remove('node_modules') + elif lang.lower() == NETCORE_RUNTIME_NAME: + if 'bin' in subdirs: + subdirs.remove('bin') + elif 'obj' in subdirs: + subdirs.remove('obj') + for filename in files: + absname = os.path.abspath(os.path.join(dirname, filename)) + arcname = absname[len(abs_src) + 1:] + zf.write(absname, arcname) + return zip_file_path + + +def get_runtime_version_details(file_path, lang_name): + version_detected = None + version_to_create = None + if lang_name.lower() == NETCORE_RUNTIME_NAME: + # method returns list in DESC, pick the first + version_detected = parse_netcore_version(file_path)[0] + version_to_create = detect_netcore_version_tocreate(version_detected) + elif lang_name.lower() == DOTNET_RUNTIME_NAME: + # method returns list in DESC, pick the first + version_detected = parse_dotnet_version(file_path) + version_to_create = detect_dotnet_version_tocreate(version_detected) + elif lang_name.lower() == NODE_RUNTIME_NAME: + version_detected = parse_node_version(file_path)[0] + version_to_create = detect_node_version_tocreate(version_detected) + elif lang_name.lower() == PYTHON_RUNTIME_NAME: + version_detected = "-" + version_to_create = PYTHON_VERSION_DEFAULT + elif lang_name.lower() == STATIC_RUNTIME_NAME: + version_detected = "-" + version_to_create = "-" + return {'detected': version_detected, 'to_create': version_to_create} + + +def create_resource_group(cmd, rg_name, location): + rcf = _resource_client_factory(cmd.cli_ctx) + rg_params = ResourceGroup(location=location) + return rcf.resource_groups.create_or_update(rg_name, rg_params) + + +def check_resource_group_exists(cmd, rg_name): + rcf = _resource_client_factory(cmd.cli_ctx) + return rcf.resource_groups.check_existence(rg_name) + + +def check_resource_group_supports_os(cmd, rg_name, is_linux): + # get all appservice plans from RG + client = web_client_factory(cmd.cli_ctx) + plans = list(client.app_service_plans.list_by_resource_group(rg_name)) + for item in plans: + # for Linux if an app with reserved==False exists, ASP doesn't support Linux + if is_linux and not item.reserved: + return False + elif not is_linux and item.reserved: + return False + return True + + +def check_if_asp_exists(cmd, rg_name, asp_name, location): + # get all appservice plans from RG + client = web_client_factory(cmd.cli_ctx) + for item in list(client.app_service_plans.list_by_resource_group(rg_name)): + if item.name.lower() == asp_name.lower() and (item.location.replace(" ", "").lower() == location or item.location == location): + return True + return False + + +def check_app_exists(cmd, rg_name, app_name): + client = web_client_factory(cmd.cli_ctx) + for item in list(client.web_apps.list_by_resource_group(rg_name)): + if item.name.lower() == app_name.lower(): + return True + return False + + +# pylint:disable=unexpected-keyword-arg +def get_lang_from_content(src_path): + import glob + # NODE: package.json should exist in the application root dir + # NETCORE & DOTNET: *.csproj should exist in the application dir + # NETCORE: netcoreapp2.0 + # DOTNET: v4.5.2 + runtime_details_dict = dict.fromkeys(['language', 'file_loc', 'default_sku']) + package_json_file = os.path.join(src_path, 'package.json') + package_python_file = glob.glob("**/*.py", recursive=True) + package_netlang_glob = glob.glob("**/*.csproj", recursive=True) + static_html_file = glob.glob("**/*.html", recursive=True) + if os.path.isfile(package_json_file): + runtime_details_dict['language'] = NODE_RUNTIME_NAME + runtime_details_dict['file_loc'] = package_json_file + runtime_details_dict['default_sku'] = 'B1' + elif package_python_file: + runtime_details_dict['language'] = PYTHON_RUNTIME_NAME + runtime_details_dict['file_loc'] = os.path.join(src_path, package_json_file[0]) + runtime_details_dict['default_sku'] = 'B1' + elif package_netlang_glob: + package_netcore_file = os.path.join(src_path, package_netlang_glob[0]) + runtime_lang = detect_dotnet_lang(package_netcore_file) + runtime_details_dict['language'] = runtime_lang + runtime_details_dict['file_loc'] = package_netcore_file + runtime_details_dict['default_sku'] = 'F1' + elif static_html_file: + runtime_details_dict['language'] = STATIC_RUNTIME_NAME + runtime_details_dict['file_loc'] = static_html_file[0] + runtime_details_dict['default_sku'] = 'F1' + return runtime_details_dict + + +def detect_dotnet_lang(csproj_path): + import xml.etree.ElementTree as ET + import re + parsed_file = ET.parse(csproj_path) + root = parsed_file.getroot() + version_lang = '' + for target_ver in root.iter('TargetFramework'): + version_lang = re.sub(r'([^a-zA-Z\s]+?)', '', target_ver.text) + if 'netcore' in version_lang.lower(): + return NETCORE_RUNTIME_NAME + return DOTNET_RUNTIME_NAME + + +def parse_dotnet_version(file_path): + version_detected = ['4.7'] + try: + from xml.dom import minidom + import re + xmldoc = minidom.parse(file_path) + framework_ver = xmldoc.getElementsByTagName('TargetFrameworkVersion') + target_ver = framework_ver[0].firstChild.data + non_decimal = re.compile(r'[^\d.]+') + # reduce the version to '5.7.4' from '5.7' + if target_ver is not None: + # remove the string from the beginning of the version value + c = non_decimal.sub('', target_ver) + version_detected = c[:3] + except: # pylint: disable=bare-except + version_detected = version_detected[0] + return version_detected + + +def parse_netcore_version(file_path): + import xml.etree.ElementTree as ET + import re + version_detected = ['0.0'] + parsed_file = ET.parse(file_path) + root = parsed_file.getroot() + for target_ver in root.iter('TargetFramework'): + version_detected = re.findall(r"\d+\.\d+", target_ver.text) + # incase of multiple versions detected, return list in descending order + version_detected = sorted(version_detected, key=float, reverse=True) + return version_detected + + +def parse_node_version(file_path): + import json + import re + with open(file_path) as data_file: + data = [] + for d in find_key_in_json(json.load(data_file), 'node'): + non_decimal = re.compile(r'[^\d.]+') + # remove the string ~ or > that sometimes exists in version value + c = non_decimal.sub('', d) + # reduce the version to '6.0' from '6.0.0' + data.append(c[:3]) + version_detected = sorted(data, key=float, reverse=True) + return version_detected or ['0.0'] + + +def detect_netcore_version_tocreate(detected_ver): + if detected_ver in NETCORE_VERSIONS: + return detected_ver + return NETCORE_VERSION_DEFAULT + + +def detect_dotnet_version_tocreate(detected_ver): + min_ver = DOTNET_VERSIONS[0] + if detected_ver in DOTNET_VERSIONS: + return detected_ver + elif detected_ver < min_ver: + return min_ver + return DOTNET_VERSION_DEFAULT + + +def detect_node_version_tocreate(detected_ver): + if detected_ver in NODE_VERSIONS: + return detected_ver + # get major version & get the closest version from supported list + major_ver = float(detected_ver.split('.')[0]) + if major_ver < 4: + return NODE_VERSION_DEFAULT + elif major_ver >= 4 and major_ver < 6: + return '4.5' + elif major_ver >= 6 and major_ver < 8: + return '6.9' + return NODE_VERSION_DEFAULT + + +def find_key_in_json(json_data, key): + for k, v in json_data.items(): + if key in k: + yield v + elif isinstance(v, dict): + for id_val in find_key_in_json(v, key): + yield id_val diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_help.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_help.py index 0a76199f1f8..9f610863fed 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_help.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_help.py @@ -635,6 +635,27 @@ az webapp create -g MyResourceGroup -p MyPlan -n MyUniqueAppName --runtime "node|6.2" --deployment-local-git """ +helps['webapp up'] = """ + type: command + short-summary: (Preview) Create and deploy existing local code to the webapp, by running the command from the folder where the code is present. + Supports running the command in preview mode using --dryrun parameter. Current supports includes Node, Python,.NET Core, ASP.NET, + staticHtml. Node, Python apps are created as Linux apps. .Net Core, ASP.NET and static HTML apps are created as Windows apps. + If command is run from an empty folder, an empty windows webapp is created. + examples: + - name: View the details of the app that will be created, without actually running the operation + text: > + az webapp up -n MyUniqueAppName --dryrun + - name: Create a web app with the default configuration, by running the command from the folder where the code to deployed exists. + text: > + az webapp up -n MyUniqueAppName + - name: Create a web app in a sepcific region, by running the command from the folder where the code to deployed exists. + text: > + az webapp up -n MyUniqueAppName -l locationName + - name: Deploy new code to an app that was originally created using the same command + text: > + az webapp up -n MyUniqueAppName -l locationName +""" + helps['webapp update'] = """ type: command short-summary: Update a web app. diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_params.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_params.py index d6c0af5b4ea..515274198bf 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_params.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_params.py @@ -326,6 +326,11 @@ def load_arguments(self, _): c.argument('microsoft_account_client_secret', arg_group='Microsoft', help='AAD V2 Application client secret') c.argument('microsoft_account_oauth_scopes', nargs='+', help="One or more Microsoft authentification scopes (space-delimited).", arg_group='Microsoft') + with self.argument_context('webapp up') as c: + c.argument('name', arg_type=webapp_name_arg_type) + c.argument('sku', arg_type=sku_arg_type) + c.argument('dryrun', help="show summary of the create and deploy operation instead of executing it", default=False, action='store_true') + with self.argument_context('functionapp') as c: c.ignore('app_instance', 'slot') c.argument('name', arg_type=name_arg_type, id_part='name', help='name of the function app') diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/commands.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/commands.py index 2255cd80b11..4bdf45aa913 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/commands.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/commands.py @@ -73,6 +73,7 @@ def load_command_table(self, _): with self.command_group('webapp', webapp_sdk) as g: g.custom_command('create', 'create_webapp', exception_handler=ex_handler_factory()) + g.custom_command('up', 'create_deploy_webapp', exception_handler=ex_handler_factory()) g.custom_command('list', 'list_webapp', table_transformer=transform_web_list_output) g.custom_show_command('show', 'show_webapp', table_transformer=transform_web_output) g.custom_command('delete', 'delete_webapp') diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py index 47120b9096a..6e98faf61af 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py @@ -42,7 +42,10 @@ from ._params import AUTH_TYPES, MULTI_CONTAINER_TYPES from ._client_factory import web_client_factory, ex_handler_factory from ._appservice_utils import _generic_site_operation - +from ._create_util import (zip_contents_from_dir, get_runtime_version_details, create_resource_group, + check_resource_group_exists, check_resource_group_supports_os, check_if_asp_exists, check_app_exists, + get_lang_from_content, web_client_factory) +from ._constants import (NODE_RUNTIME_NAME, OS_DEFAULT, STATIC_RUNTIME_NAME, PYTHON_RUNTIME_NAME) logger = get_logger(__name__) @@ -1357,16 +1360,20 @@ def show_container_cd_url(cmd, resource_group_name, name, slot=None): def view_in_browser(cmd, resource_group_name, name, slot=None, logs=False): + url = _get_url(cmd, resource_group_name, name, slot) + open_page_in_browser(url) + if logs: + get_streaming_log(cmd, resource_group_name, name, provider=None, slot=slot) + + +def _get_url(cmd, resource_group_name, name, slot=None): site = _generic_site_operation(cmd.cli_ctx, resource_group_name, name, 'get', slot) if not site: raise CLIError("'{}' app doesn't exist".format(name)) url = site.enabled_host_names[0] # picks the custom domain URL incase a domain is assigned ssl_host = next((h for h in site.host_name_ssl_states if h.ssl_state != SslState.disabled), None) - url = ('https' if ssl_host else 'http') + '://' + url - open_page_in_browser(url) - if logs: - get_streaming_log(cmd, resource_group_name, name, provider=None, slot=slot) + return ('https' if ssl_host else 'http') + '://' + url # TODO: expose new blob suport @@ -1960,9 +1967,9 @@ def list_locations(cmd, sku, linux_workers_enabled=None): def _check_zip_deployment_status(deployment_status_url, authorization, timeout=None): import requests import time - total_trials = (int(timeout) // 30) if timeout else 10 + total_trials = (int(timeout) // 30) if timeout else 300 for _num_trials in range(total_trials): - time.sleep(30) + time.sleep(2) response = requests.get(deployment_status_url, headers=authorization) res_dict = response.json() if res_dict.get('status', 0) == 5: @@ -2033,3 +2040,194 @@ def get_history_triggered_webjob(cmd, resource_group_name, name, webjob_name, sl if slot: return client.web_apps.list_triggered_web_job_history_slot(resource_group_name, name, webjob_name, slot) return client.web_apps.list_triggered_web_job_history(resource_group_name, name, webjob_name) + + +def create_deploy_webapp(cmd, name, location=None, sku=None, dryrun=False): + import os + client = web_client_factory(cmd.cli_ctx) + # the code to deploy is expected to be the current directory the command is running from + src_dir = os.getcwd() + # if dir is empty, show a message in dry run + do_deployment = False if os.listdir(src_dir) == [] else True + create_new_rg = True + create_new_asp = True + set_build_appSetting = False + + # determine the details for app to be created from src contents + lang_details = get_lang_from_content(src_dir) + # we support E2E create and deploy for Node & dotnetcore, any other stack, set defaults for os & runtime + # and skip deployment + if lang_details['language'] is None: + do_deployment = False + sku = sku | 'F1' + os_val = OS_DEFAULT + detected_version = '-' + runtime_version = '-' + else: + # update SKU to user set value + if sku is None: + sku = lang_details.get("default_sku") + else: + sku = sku + language = lang_details.get("language") + is_skip_build = language.lower() == STATIC_RUNTIME_NAME + os_val = "Linux" if language.lower() == NODE_RUNTIME_NAME \ + or language.lower() == PYTHON_RUNTIME_NAME else OS_DEFAULT + # detect the version + data = get_runtime_version_details(lang_details.get('file_loc'), language) + version_used_create = data.get('to_create') + detected_version = data.get('detected') + runtime_version = "{}|{}".format(language, version_used_create) if \ + version_used_create != "-" else version_used_create + + full_sku = get_sku_name(sku) + + if location is None: + locs = client.list_geo_regions(sku, True) + available_locs = [] + for loc in locs: + available_locs.append(loc.name) + location = available_locs[0] + else: + location = location + # Remove spaces from the location string, incase the GeoRegion string is used + loc_name = location.replace(" ", "").lower() + + is_linux = True if os_val == 'Linux' else False + + asp = "appsvc_asp_{}_{}".format(os_val, loc_name) + rg_name = "appsvc_rg_{}_{}".format(os_val, loc_name) + + str_no_contents_warn = "" + if not do_deployment: + str_no_contents_warn = "[Empty directory, no deployment will be triggered]" + + # Resource group: check if default RG is set + default_rg = cmd.cli_ctx.config.get('defaults', 'group', fallback=None) + + if default_rg and check_resource_group_exists(cmd, default_rg) and check_resource_group_supports_os(cmd, default_rg, is_linux): + create_new_rg = False + elif check_resource_group_exists(cmd, rg_name) and check_resource_group_supports_os(cmd, rg_name, is_linux): + create_new_rg = False + else: + create_new_rg = True + + src_path = "{} {}".format(src_dir.replace("\\", "\\\\"), str_no_contents_warn) + rg_str = "{}".format(rg_name) + dry_run_str = r""" { + "name" : "%s", + "serverfarm" : "%s", + "resourcegroup" : "%s", + "sku": "%s", + "os": "%s", + "location" : "%s", + "src_path" : "%s", + "version_detected": "%s", + "version_to_create": "%s" + } + """ % (name, asp, rg_str, full_sku, os_val, location, src_path, + detected_version, runtime_version) + create_json = json.loads(dry_run_str) + + if dryrun: + logger.warning("Web app will be created with the below configuration,re-run command " + "without the --dryrun flag to create & deploy a new app") + return create_json + + # create RG if the RG doesn't already exist + if create_new_rg: + logger.warning("Creating Resource group '%s' ...", rg_name) + create_resource_group(cmd, rg_name, location) + logger.warning("Resource group creation complete") + else: + logger.warning("Resource group '%s' already exists.", rg_name) + + # create asp ], if are creating a new RG we can skip the checks for if asp exists + if create_new_rg: + logger.warning("Creating App service plan '%s' ...", asp) + sku_def = SkuDescription(tier=full_sku, name=sku, capacity=(1 if is_linux else None)) + plan_def = AppServicePlan(location=loc_name, app_service_plan_name=asp, + sku=sku_def, reserved=(is_linux or None)) + client.app_service_plans.create_or_update(rg_name, asp, plan_def) + logger.warning("App service plan creation complete") + create_new_asp = True + + elif not check_if_asp_exists(cmd, rg_name, asp, location): + logger.warning("Creating App service plan '%s' ...", asp) + sku_def = SkuDescription(tier=full_sku, name=sku, capacity=(1 if is_linux else None)) + plan_def = AppServicePlan(location=loc_name, app_service_plan_name=asp, + sku=sku_def, reserved=(is_linux or None)) + client.app_service_plans.create_or_update(rg_name, asp, plan_def) + create_new_asp = True + logger.warning("App service plan creation complete") + else: + logger.warning("App service plan '%s' already exists.", asp) + create_new_asp = False + + # create the app, skip checks for if app exists if a New RG or New ASP is created + if create_new_rg or create_new_asp: + logger.warning("Creating app '%s' ....", name) + create_webapp(cmd, rg_name, name, asp, runtime_version if is_linux else None) + logger.warning("Webapp creation complete") + set_build_appSetting = True + elif not check_app_exists(cmd, rg_name, name): + logger.warning("Creating app '%s' ....", name) + create_webapp(cmd, rg_name, name, asp, runtime_version if is_linux else None) + logger.warning("Webapp creation complete") + set_build_appSetting = True + else: + logger.warning("App '%s' already exists", name) + if do_deployment: + # setting the appsettings causes a app restart so we avoid if not needed + set_build_appSetting = False + _app_settings = get_app_settings(cmd, rg_name, name) + if all(not d for d in _app_settings): + set_build_appSetting = True + elif '"name": "SCM_DO_BUILD_DURING_DEPLOYMENT", "value": "true"' not in json.dumps(_app_settings[0]): + set_build_appSetting = True + else: + set_build_appSetting = False + + # update create_json to include the app_url + url = _get_url(cmd, rg_name, name) + + if do_deployment: + if not is_skip_build and set_build_appSetting: + # setting to build after deployment + logger.warning("Updating app settings to enable build after deployment") + update_app_settings(cmd, rg_name, name, ["SCM_DO_BUILD_DURING_DEPLOYMENT=true"]) + # work around until the timeout limits issue for linux is investigated & fixed + # wakeup kudu, by making an SCM call + import time + time.sleep(5) + _ping_scm_site(cmd, rg_name, name) + + logger.warning("Creating zip with contents of dir %s ...", src_dir) + # zip contents & deploy + zip_file_path = zip_contents_from_dir(src_dir, language) + + logger.warning("Preparing to deploy %s contents to app.", + '' if is_skip_build else 'and build') + enable_zip_deploy(cmd, rg_name, name, zip_file_path) + # Remove the file afer deployment, handling exception if user removed the file manually + try: + os.remove(zip_file_path) + except OSError: + pass + else: + logger.warning('No known package (Node, ASP.NET, .NETCORE, or Static Html) ' + 'found skipping zip and deploy process') + create_json.update({'app_url': url}) + logger.warning("All done.") + return create_json + + +def _ping_scm_site(cmd, resource_group, name): + # wakeup kudu, by making an SCM call + import requests + # work around until the timeout limits issue for linux is investigated & fixed + user_name, password = _get_site_credential(cmd.cli_ctx, resource_group, name) + scm_url = _get_scm_url(cmd, resource_group, name) + import urllib3 + authorization = urllib3.util.make_headers(basic_auth='{}:{}'.format(user_name, password)) + requests.get(scm_url + '/api/settings', headers=authorization) From c8673533152db54b1dceb4d8bf2afa5f5e9d7826 Mon Sep 17 00:00:00 2001 From: Sisira Panchagnula Date: Tue, 27 Nov 2018 15:23:44 -0800 Subject: [PATCH 2/8] Updating history --- src/command_modules/azure-cli-appservice/HISTORY.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/command_modules/azure-cli-appservice/HISTORY.rst b/src/command_modules/azure-cli-appservice/HISTORY.rst index 1cdf63b481e..43e5e50ef64 100644 --- a/src/command_modules/azure-cli-appservice/HISTORY.rst +++ b/src/command_modules/azure-cli-appservice/HISTORY.rst @@ -4,8 +4,10 @@ Release History =============== 0.2.8 +++++ +* webapp: adding support for az webapp up command (Preview) that helps in creating & deploying contents to app * webapp: fix a bug on container based windows app due to backend change + 0.2.7 +++++ * webapp, functionapp: Zip deployment default timeout to poll for the status increased to 5 mins, also adding a timeout property to customize this value From cc03e09d927b5893a8ee8e51adbb81249f5a7602 Mon Sep 17 00:00:00 2001 From: Sisira Panchagnula Date: Tue, 27 Nov 2018 15:27:01 -0800 Subject: [PATCH 3/8] Pylint fixes --- .../azure/cli/command_modules/appservice/_constants.py | 1 - .../azure/cli/command_modules/appservice/custom.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py index 12a05aeaed9..cbc81fb2745 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_constants.py @@ -16,4 +16,3 @@ NODE_VERSIONS = ['4.4', '4.5', '6.2', '6.6', '6.9', '6.11', '8.0', '8.1', '8.9', '8.11'] NETCORE_VERSIONS = ['1.0', '1.1', '2.0'] DOTNET_VERSIONS = ['3.5', '4.7'] - diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py index 6e98faf61af..861d303fc91 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py @@ -43,8 +43,8 @@ from ._client_factory import web_client_factory, ex_handler_factory from ._appservice_utils import _generic_site_operation from ._create_util import (zip_contents_from_dir, get_runtime_version_details, create_resource_group, - check_resource_group_exists, check_resource_group_supports_os, check_if_asp_exists, check_app_exists, - get_lang_from_content, web_client_factory) + check_resource_group_exists, check_resource_group_supports_os, + check_if_asp_exists, check_app_exists, get_lang_from_content, web_client_factory) from ._constants import (NODE_RUNTIME_NAME, OS_DEFAULT, STATIC_RUNTIME_NAME, PYTHON_RUNTIME_NAME) logger = get_logger(__name__) From 628eb4df603c64eccf374b5fd6f6808736733448 Mon Sep 17 00:00:00 2001 From: Sisira Panchagnula Date: Wed, 28 Nov 2018 14:02:43 -0800 Subject: [PATCH 4/8] Handling Python static check errors PYlint fixes PYlint static check fixes Pylint fixes Raising the default timeout & raising exception if deployment doesn't finish by timeout --- .../appservice/_create_util.py | 50 +++++--- .../cli/command_modules/appservice/custom.py | 108 ++++++------------ 2 files changed, 67 insertions(+), 91 deletions(-) diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py index 5eb921ae174..b5d056f385d 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py @@ -8,19 +8,10 @@ import zipfile from azure.cli.core.commands.client_factory import get_mgmt_service_client from azure.mgmt.resource.resources.models import ResourceGroup -from ._constants import ( - NETCORE_VERSION_DEFAULT, - NETCORE_VERSIONS, - NODE_VERSION_DEFAULT, - NODE_VERSIONS, - NETCORE_RUNTIME_NAME, - NODE_RUNTIME_NAME, - DOTNET_RUNTIME_NAME, - DOTNET_VERSION_DEFAULT, - DOTNET_VERSIONS, - STATIC_RUNTIME_NAME, - PYTHON_RUNTIME_NAME, - PYTHON_VERSION_DEFAULT) +from ._constants import (NETCORE_VERSION_DEFAULT, NETCORE_VERSIONS, NODE_VERSION_DEFAULT, + NODE_VERSIONS, NETCORE_RUNTIME_NAME, NODE_RUNTIME_NAME, DOTNET_RUNTIME_NAME, + DOTNET_VERSION_DEFAULT, DOTNET_VERSIONS, STATIC_RUNTIME_NAME, + PYTHON_RUNTIME_NAME, PYTHON_VERSION_DEFAULT) def _resource_client_factory(cli_ctx, **_): @@ -86,12 +77,12 @@ def create_resource_group(cmd, rg_name, location): return rcf.resource_groups.create_or_update(rg_name, rg_params) -def check_resource_group_exists(cmd, rg_name): +def _check_resource_group_exists(cmd, rg_name): rcf = _resource_client_factory(cmd.cli_ctx) return rcf.resource_groups.check_existence(rg_name) -def check_resource_group_supports_os(cmd, rg_name, is_linux): +def _check_resource_group_supports_os(cmd, rg_name, is_linux): # get all appservice plans from RG client = web_client_factory(cmd.cli_ctx) plans = list(client.app_service_plans.list_by_resource_group(rg_name)) @@ -108,8 +99,10 @@ def check_if_asp_exists(cmd, rg_name, asp_name, location): # get all appservice plans from RG client = web_client_factory(cmd.cli_ctx) for item in list(client.app_service_plans.list_by_resource_group(rg_name)): - if item.name.lower() == asp_name.lower() and (item.location.replace(" ", "").lower() == location or item.location == location): - return True + if (item.name.lower() == asp_name.lower() and + (item.location.replace(" ", "").lower() == location or + item.location == location)): + return True return False @@ -250,3 +243,26 @@ def find_key_in_json(json_data, key): elif isinstance(v, dict): for id_val in find_key_in_json(v, key): yield id_val + + +def set_location(cmd, sku, location): + client = web_client_factory(cmd.cli_ctx) + if location is None: + locs = client.list_geo_regions(sku, True) + available_locs = [] + for loc in locs: + available_locs.append(loc.name) + location = available_locs[0] + else: + location = location + return location.replace(" ", "").lower() + + +def should_create_new_rg(cmd, default_rg, rg_name, is_linux): + if (default_rg and _check_resource_group_exists(cmd, default_rg) and + _check_resource_group_supports_os(cmd, default_rg, is_linux)): + return False + elif (_check_resource_group_exists(cmd, rg_name) and + _check_resource_group_supports_os(cmd, rg_name, is_linux)): + return False + return True diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py index 861d303fc91..f4bbd62b2fa 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py @@ -43,8 +43,8 @@ from ._client_factory import web_client_factory, ex_handler_factory from ._appservice_utils import _generic_site_operation from ._create_util import (zip_contents_from_dir, get_runtime_version_details, create_resource_group, - check_resource_group_exists, check_resource_group_supports_os, - check_if_asp_exists, check_app_exists, get_lang_from_content, web_client_factory) + should_create_new_rg, set_location, check_if_asp_exists, check_app_exists, + get_lang_from_content) from ._constants import (NODE_RUNTIME_NAME, OS_DEFAULT, STATIC_RUNTIME_NAME, PYTHON_RUNTIME_NAME) logger = get_logger(__name__) @@ -1967,7 +1967,7 @@ def list_locations(cmd, sku, linux_workers_enabled=None): def _check_zip_deployment_status(deployment_status_url, authorization, timeout=None): import requests import time - total_trials = (int(timeout) // 30) if timeout else 300 + total_trials = (int(timeout) // 2) if timeout else 450 for _num_trials in range(total_trials): time.sleep(2) response = requests.get(deployment_status_url, headers=authorization) @@ -1981,8 +1981,8 @@ def _check_zip_deployment_status(deployment_status_url, authorization, timeout=N logger.info(res_dict['progress']) # show only in debug mode, customers seem to find this confusing # if the deployment is taking longer than expected if res_dict.get('status', 0) != 4: - logger.warning("""Deployment is taking longer than expected. Please verify status at '%s' - beforing launching the app""", deployment_status_url) + raise ValueError("""Deployment is taking longer than expected. Please verify + status at '%s' beforing launching the app""", deployment_status_url) return res_dict @@ -2042,20 +2042,21 @@ def get_history_triggered_webjob(cmd, resource_group_name, name, webjob_name, sl return client.web_apps.list_triggered_web_job_history(resource_group_name, name, webjob_name) -def create_deploy_webapp(cmd, name, location=None, sku=None, dryrun=False): +def create_deploy_webapp(cmd, name, location=None, sku=None, dryrun=False): # pylint: disable=too-many-statements import os client = web_client_factory(cmd.cli_ctx) # the code to deploy is expected to be the current directory the command is running from src_dir = os.getcwd() # if dir is empty, show a message in dry run do_deployment = False if os.listdir(src_dir) == [] else True - create_new_rg = True - create_new_asp = True - set_build_appSetting = False + _create_new_rg = True + _create_new_asp = True + _create_new_app = True + _set_build_appSetting = False # determine the details for app to be created from src contents lang_details = get_lang_from_content(src_dir) - # we support E2E create and deploy for Node & dotnetcore, any other stack, set defaults for os & runtime + # we support E2E create and deploy for selected stacks, any other stack, set defaults for os & runtime # and skip deployment if lang_details['language'] is None: do_deployment = False @@ -2081,38 +2082,15 @@ def create_deploy_webapp(cmd, name, location=None, sku=None, dryrun=False): version_used_create != "-" else version_used_create full_sku = get_sku_name(sku) - - if location is None: - locs = client.list_geo_regions(sku, True) - available_locs = [] - for loc in locs: - available_locs.append(loc.name) - location = available_locs[0] - else: - location = location - # Remove spaces from the location string, incase the GeoRegion string is used - loc_name = location.replace(" ", "").lower() - + loc_name = set_location(cmd, sku, location) is_linux = True if os_val == 'Linux' else False - asp = "appsvc_asp_{}_{}".format(os_val, loc_name) rg_name = "appsvc_rg_{}_{}".format(os_val, loc_name) - - str_no_contents_warn = "" - if not do_deployment: - str_no_contents_warn = "[Empty directory, no deployment will be triggered]" - # Resource group: check if default RG is set default_rg = cmd.cli_ctx.config.get('defaults', 'group', fallback=None) + _create_new_rg = should_create_new_rg(cmd, default_rg, rg_name, is_linux) - if default_rg and check_resource_group_exists(cmd, default_rg) and check_resource_group_supports_os(cmd, default_rg, is_linux): - create_new_rg = False - elif check_resource_group_exists(cmd, rg_name) and check_resource_group_supports_os(cmd, rg_name, is_linux): - create_new_rg = False - else: - create_new_rg = True - - src_path = "{} {}".format(src_dir.replace("\\", "\\\\"), str_no_contents_warn) + src_path = "{}".format(src_dir.replace("\\", "\\\\")) rg_str = "{}".format(rg_name) dry_run_str = r""" { "name" : "%s", @@ -2135,69 +2113,54 @@ def create_deploy_webapp(cmd, name, location=None, sku=None, dryrun=False): return create_json # create RG if the RG doesn't already exist - if create_new_rg: + if _create_new_rg: logger.warning("Creating Resource group '%s' ...", rg_name) create_resource_group(cmd, rg_name, location) logger.warning("Resource group creation complete") + _create_new_asp = True else: logger.warning("Resource group '%s' already exists.", rg_name) - - # create asp ], if are creating a new RG we can skip the checks for if asp exists - if create_new_rg: - logger.warning("Creating App service plan '%s' ...", asp) - sku_def = SkuDescription(tier=full_sku, name=sku, capacity=(1 if is_linux else None)) - plan_def = AppServicePlan(location=loc_name, app_service_plan_name=asp, - sku=sku_def, reserved=(is_linux or None)) - client.app_service_plans.create_or_update(rg_name, asp, plan_def) - logger.warning("App service plan creation complete") - create_new_asp = True - - elif not check_if_asp_exists(cmd, rg_name, asp, location): + _create_new_asp = check_if_asp_exists(cmd, rg_name, asp, location) + # create new ASP if an existing one cannot be used + if _create_new_asp: logger.warning("Creating App service plan '%s' ...", asp) sku_def = SkuDescription(tier=full_sku, name=sku, capacity=(1 if is_linux else None)) plan_def = AppServicePlan(location=loc_name, app_service_plan_name=asp, sku=sku_def, reserved=(is_linux or None)) client.app_service_plans.create_or_update(rg_name, asp, plan_def) - create_new_asp = True logger.warning("App service plan creation complete") + _create_new_app = True else: logger.warning("App service plan '%s' already exists.", asp) - create_new_asp = False - - # create the app, skip checks for if app exists if a New RG or New ASP is created - if create_new_rg or create_new_asp: + _create_new_asp = False + _create_new_app = check_app_exists(cmd, rg_name, name) + # create the app + if _create_new_app: logger.warning("Creating app '%s' ....", name) create_webapp(cmd, rg_name, name, asp, runtime_version if is_linux else None) logger.warning("Webapp creation complete") - set_build_appSetting = True - elif not check_app_exists(cmd, rg_name, name): - logger.warning("Creating app '%s' ....", name) - create_webapp(cmd, rg_name, name, asp, runtime_version if is_linux else None) - logger.warning("Webapp creation complete") - set_build_appSetting = True + _set_build_appSetting = True else: logger.warning("App '%s' already exists", name) if do_deployment: # setting the appsettings causes a app restart so we avoid if not needed - set_build_appSetting = False _app_settings = get_app_settings(cmd, rg_name, name) if all(not d for d in _app_settings): - set_build_appSetting = True + _set_build_appSetting = True elif '"name": "SCM_DO_BUILD_DURING_DEPLOYMENT", "value": "true"' not in json.dumps(_app_settings[0]): - set_build_appSetting = True + _set_build_appSetting = True else: - set_build_appSetting = False + _set_build_appSetting = False # update create_json to include the app_url url = _get_url(cmd, rg_name, name) - if do_deployment: - if not is_skip_build and set_build_appSetting: - # setting to build after deployment - logger.warning("Updating app settings to enable build after deployment") - update_app_settings(cmd, rg_name, name, ["SCM_DO_BUILD_DURING_DEPLOYMENT=true"]) - # work around until the timeout limits issue for linux is investigated & fixed - # wakeup kudu, by making an SCM call + if do_deployment and not is_skip_build and _set_build_appSetting: + # setting to build after deployment + logger.warning("Updating app settings to enable build after deployment") + update_app_settings(cmd, rg_name, name, ["SCM_DO_BUILD_DURING_DEPLOYMENT=true"]) + # work around until the timeout limits issue for linux is investigated & fixed + # wakeup kudu, by making an SCM call import time time.sleep(5) _ping_scm_site(cmd, rg_name, name) @@ -2214,9 +2177,6 @@ def create_deploy_webapp(cmd, name, location=None, sku=None, dryrun=False): os.remove(zip_file_path) except OSError: pass - else: - logger.warning('No known package (Node, ASP.NET, .NETCORE, or Static Html) ' - 'found skipping zip and deploy process') create_json.update({'app_url': url}) logger.warning("All done.") return create_json From 070f0d21150a104e93d99404b5952b0035e8c7e7 Mon Sep 17 00:00:00 2001 From: Sisira Panchagnula Date: Wed, 28 Nov 2018 18:29:50 -0800 Subject: [PATCH 5/8] Pylint style fixes --- .../azure/cli/command_modules/appservice/_create_util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py index b5d056f385d..e655a9f1862 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py @@ -101,7 +101,7 @@ def check_if_asp_exists(cmd, rg_name, asp_name, location): for item in list(client.app_service_plans.list_by_resource_group(rg_name)): if (item.name.lower() == asp_name.lower() and (item.location.replace(" ", "").lower() == location or - item.location == location)): + item.location == location)): return True return False @@ -260,9 +260,9 @@ def set_location(cmd, sku, location): def should_create_new_rg(cmd, default_rg, rg_name, is_linux): if (default_rg and _check_resource_group_exists(cmd, default_rg) and - _check_resource_group_supports_os(cmd, default_rg, is_linux)): + _check_resource_group_supports_os(cmd, default_rg, is_linux)): return False elif (_check_resource_group_exists(cmd, rg_name) and - _check_resource_group_supports_os(cmd, rg_name, is_linux)): + _check_resource_group_supports_os(cmd, rg_name, is_linux)): return False return True From 1c49040dac6d5a239f719ffd13027a2a05bb25c6 Mon Sep 17 00:00:00 2001 From: Sisira Panchagnula Date: Wed, 28 Nov 2018 21:08:23 -0800 Subject: [PATCH 6/8] Fixing static check errors --- .../cli/command_modules/appservice/_create_util.py | 10 +++++----- .../azure/cli/command_modules/appservice/custom.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py index e655a9f1862..cd4c101c323 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py @@ -100,9 +100,9 @@ def check_if_asp_exists(cmd, rg_name, asp_name, location): client = web_client_factory(cmd.cli_ctx) for item in list(client.app_service_plans.list_by_resource_group(rg_name)): if (item.name.lower() == asp_name.lower() and - (item.location.replace(" ", "").lower() == location or - item.location == location)): - return True + item.location.replace(" ", "").lower() == location or + item.location == location): + return True return False @@ -261,8 +261,8 @@ def set_location(cmd, sku, location): def should_create_new_rg(cmd, default_rg, rg_name, is_linux): if (default_rg and _check_resource_group_exists(cmd, default_rg) and _check_resource_group_supports_os(cmd, default_rg, is_linux)): - return False + return False elif (_check_resource_group_exists(cmd, rg_name) and _check_resource_group_supports_os(cmd, rg_name, is_linux)): - return False + return False return True diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py index f4bbd62b2fa..bfbbc13a97c 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py @@ -1982,7 +1982,7 @@ def _check_zip_deployment_status(deployment_status_url, authorization, timeout=N # if the deployment is taking longer than expected if res_dict.get('status', 0) != 4: raise ValueError("""Deployment is taking longer than expected. Please verify - status at '%s' beforing launching the app""", deployment_status_url) + status at '{}' beforing launching the app""".format(deployment_status_url)) return res_dict From 3b5e05189ed85201f95c1a8df9c753c4b01a1ff2 Mon Sep 17 00:00:00 2001 From: Sisira Panchagnula Date: Wed, 28 Nov 2018 22:06:22 -0800 Subject: [PATCH 7/8] Indentation fix --- .../azure/cli/command_modules/appservice/_create_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py index cd4c101c323..f0563ddc7a9 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/_create_util.py @@ -263,6 +263,6 @@ def should_create_new_rg(cmd, default_rg, rg_name, is_linux): _check_resource_group_supports_os(cmd, default_rg, is_linux)): return False elif (_check_resource_group_exists(cmd, rg_name) and - _check_resource_group_supports_os(cmd, rg_name, is_linux)): + _check_resource_group_supports_os(cmd, rg_name, is_linux)): return False return True From a3bd2f4c478943c31024e194aef3d178eb8d9c09 Mon Sep 17 00:00:00 2001 From: Sisira Panchagnula Date: Thu, 29 Nov 2018 05:52:39 -0800 Subject: [PATCH 8/8] Fixes #7812 --- .../azure/cli/command_modules/appservice/custom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py index bfbbc13a97c..bdf4fe08e1f 100644 --- a/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py +++ b/src/command_modules/azure-cli-appservice/azure/cli/command_modules/appservice/custom.py @@ -1972,7 +1972,7 @@ def _check_zip_deployment_status(deployment_status_url, authorization, timeout=N time.sleep(2) response = requests.get(deployment_status_url, headers=authorization) res_dict = response.json() - if res_dict.get('status', 0) == 5: + if res_dict.get('status', 0) == 3: logger.warning("Zip deployment failed status %s", res_dict['status_text']) break elif res_dict.get('status', 0) == 4: