diff --git a/src/index.json b/src/index.json index 1e5a97829f7..3e01b216d59 100644 --- a/src/index.json +++ b/src/index.json @@ -325,9 +325,9 @@ ], "webapp": [ { - "filename": "webapp-0.2.3-py2.py3-none-any.whl", - "sha256Digest": "c6e2c8fff7f3d88f9b7eb77327d67ab525ad9c8b8b27b3b004b565fac391c241", - "downloadUrl": "https://github.com/panchagnula/azure-cli-extensions/raw/sisirap-extensions-whl/dist/webapp-0.2.3-py2.py3-none-any.whl", + "filename": "webapp-0.2.4-py2.py3-none-any.whl", + "sha256Digest": "28736466d2602516394d3ed3cd784f318013be7624db8f3fe09012927e7e9de7", + "downloadUrl": "https://github.com/panchagnula/azure-cli-extensions/raw/sisirap-extensions-whl/dist/webapp-0.2.4-py2.py3-none-any.whl", "metadata": { "azext.isPreview": true, "azext.minCliCoreVersion": "2.0.24", @@ -366,7 +366,7 @@ "metadata_version": "2.0", "name": "webapp", "summary": "An Azure CLI Extension to manage appservice resources", - "version": "0.2.3" + "version": "0.2.4" } } ], diff --git a/src/webapp/azext_webapp/_constants.py b/src/webapp/azext_webapp/_constants.py index f218f175fee..01f9d36ed4d 100644 --- a/src/webapp/azext_webapp/_constants.py +++ b/src/webapp/azext_webapp/_constants.py @@ -5,9 +5,14 @@ NODE_VERSION_DEFAULT = "8.9" NETCORE_VERSION_DEFAULT = "2.0" +DOTNET_VERSION_DEFAULT = "4.7" +NETCORE_RUNTIME_NAME = "dotnetcore" +DOTNET_RUNTIME_NAME = "aspnet" +JAVA_RUNTIME_NAME = "java" +OS_DEFAULT = "Windows" +STATIC_RUNTIME_NAME = "static" # not an oficial supported runtime but used for CLI logic # TODO: Remove this once we have the api returning the versions NODE_VERSIONS = ['4.4', '4.5', '6.2', '6.6', '6.9', '6.11', '8.0', '8.1'] NETCORE_VERSIONS = ['1.0', '1.1', '2.0'] +DOTNET_VERSIONS = ['3.5', '4.7'] NODE_RUNTIME_NAME = "node" -NETCORE_RUNTIME_NAME = "dotnetcore" -OS_DEFAULT = "Windows" diff --git a/src/webapp/azext_webapp/create_util.py b/src/webapp/azext_webapp/create_util.py index 0978e3a2942..85b60d3ae4d 100644 --- a/src/webapp/azext_webapp/create_util.py +++ b/src/webapp/azext_webapp/create_util.py @@ -13,7 +13,12 @@ NODE_VERSION_DEFAULT, NODE_VERSIONS, NETCORE_RUNTIME_NAME, - NODE_RUNTIME_NAME) + NODE_RUNTIME_NAME, + DOTNET_RUNTIME_NAME, + DOTNET_VERSION_DEFAULT, + DOTNET_VERSIONS, + JAVA_RUNTIME_NAME, + STATIC_RUNTIME_NAME) def _resource_client_factory(cli_ctx, **_): @@ -57,9 +62,16 @@ def get_runtime_version_details(file_path, lang_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() == STATIC_RUNTIME_NAME: + version_detected = "-" + version_to_create = "-" return {'detected': version_detected, 'to_create': version_to_create} @@ -107,22 +119,65 @@ def check_app_exists(cmd, rg_name, app_name): def get_lang_from_content(src_path): import glob # NODE: package.json should exist in the application root dir - # NETCORE: *.csproj 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_netcore_glob = glob.glob("*.csproj") + package_netlang_glob = glob.glob("**/*.csproj", recursive=True) + runtime_java_file = glob.glob("**/*.war", 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'] = 'S1' - elif package_netcore_glob: - package_netcore_file = os.path.join(src_path, package_netcore_glob[0]) - runtime_details_dict['language'] = NETCORE_RUNTIME_NAME + 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 runtime_java_file: + runtime_details_dict['language'] = JAVA_RUNTIME_NAME + runtime_details_dict['file_loc'] = runtime_java_file + runtime_details_dict['default_sku'] = 'S1' + 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 + else: + return DOTNET_RUNTIME_NAME + + +def parse_dotnet_version(file_path): + from xml.dom import minidom + import re + xmldoc = minidom.parse(file_path) + framework_ver = xmldoc.getElementsByTagName('TargetFrameworkVersion') + version_detected = ['4.7'] + 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] + return version_detected + + def parse_netcore_version(file_path): import xml.etree.ElementTree as ET import re @@ -157,6 +212,15 @@ def detect_netcore_version_tocreate(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 diff --git a/src/webapp/azext_webapp/custom.py b/src/webapp/azext_webapp/custom.py index 1f3397c8f9e..804048204fd 100644 --- a/src/webapp/azext_webapp/custom.py +++ b/src/webapp/azext_webapp/custom.py @@ -19,7 +19,10 @@ _get_scm_url, get_sku_name, list_publish_profiles, - get_site_configs) + get_site_configs, + config_diagnostics) + +from azure.cli.command_modules.appservice._appservice_utils import _generic_site_operation from .create_util import ( zip_contents_from_dir, @@ -33,7 +36,7 @@ web_client_factory ) -from ._constants import (NODE_RUNTIME_NAME, OS_DEFAULT) +from ._constants import (NODE_RUNTIME_NAME, OS_DEFAULT, JAVA_RUNTIME_NAME, STATIC_RUNTIME_NAME) logger = get_logger(__name__) @@ -64,12 +67,14 @@ def create_deploy_webapp(cmd, name, location=None, dryrun=False): else: sku = lang_details.get("default_sku") language = lang_details.get("language") - os_val = "Linux" if language.lower() == NODE_RUNTIME_NAME else OS_DEFAULT + is_java = language.lower() == JAVA_RUNTIME_NAME + is_skip_build = is_java or language.lower() == STATIC_RUNTIME_NAME + os_val = "Linux" if language.lower() == NODE_RUNTIME_NAME or is_java 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) + runtime_version = "{}|{}".format(language, version_used_create) if version_used_create != "-" else version_used_create if location is None: locs = client.list_geo_regions(sku, True) @@ -92,7 +97,7 @@ def create_deploy_webapp(cmd, name, location=None, dryrun=False): # 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_supports_os(cmd, default_rg, location, is_linux): + if default_rg and check_resource_group_exists(cmd, default_rg) and check_resource_group_supports_os(cmd, default_rg, location, is_linux): rg_name = default_rg rg_mssg = "[Using default Resource group]" else: @@ -100,7 +105,6 @@ def create_deploy_webapp(cmd, name, location=None, dryrun=False): src_path = "{} {}".format(src_dir.replace("\\", "\\\\"), str_no_contents_warn) rg_str = "{} {}".format(rg_name, rg_mssg) - dry_run_str = r""" { "name" : "%s", "serverfarm" : "%s", @@ -143,43 +147,48 @@ def create_deploy_webapp(cmd, name, location=None, dryrun=False): # create the app if not check_app_exists(cmd, rg_name, name): logger.warning("Creating app '%s' ....", name) - app_created = create_webapp(cmd, rg_name, name, asp, runtime_version if is_linux else None) - # update create_json to include the app_url - url = app_created.enabled_host_names[0] # picks the custom domain URL incase a domain is assigned - url = 'https://' + url + create_webapp(cmd, rg_name, name, asp, runtime_version if is_linux else None) logger.warning("Webapp creation complete") else: logger.warning("App '%s' already exists", name) + # update create_json to include the app_url + url = _get_app_url(cmd, rg_name, name) # picks the custom domain URL incase a domain is assigned if do_deployment: - # 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 not is_skip_build: + # 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 requests # work around until the timeout limits issue for linux is investigated & fixed user_name, password = _get_site_credential(cmd.cli_ctx, rg_name, name) scm_url = _get_scm_url(cmd, rg_name, name) + import urllib3 authorization = urllib3.util.make_headers(basic_auth='{0}:{1}'.format(user_name, password)) requests.get(scm_url + '/api/settings', headers=authorization) - logger.warning("Creating zip with contents of dir %s ...", src_dir) - # zip contents & deploy - zip_file_path = zip_contents_from_dir(src_dir, language) + if is_java: + zip_file_path = src_path + '\\\\' + lang_details.get('file_loc')[0] + else: + 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("Deploying and building contents to app." - "This operation can take some time to finish...") + logger.warning("Deploying %s contents to app." + "This operation can take some time to finish...", '' if is_skip_build else 'and building') 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 + if not is_java: + # 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 'NODE' or 'DOTNETCORE' package detected, skipping zip and deploy process") + logger.warning("No known package (Node, ASP.NET, .NETCORE, Java or Static Html) found skipping zip and deploy process") create_json.update({'app_url': url}) logger.warning("All done.") return create_json @@ -247,3 +256,8 @@ def create_tunnel(cmd, resource_group_name, name, port, slot=None): break logger.warning('Tunnel is ready! Creating on port %s', port) tunnel_server.start_server() + + +def _get_app_url(cmd, rg_name, app_name): + site = _generic_site_operation(cmd.cli_ctx, rg_name, app_name, 'get') + return "https://" + site.enabled_host_names[0] diff --git a/src/webapp/setup.py b/src/webapp/setup.py index 78727e9507d..e7477e7a86b 100644 --- a/src/webapp/setup.py +++ b/src/webapp/setup.py @@ -8,7 +8,7 @@ from codecs import open from setuptools import setup, find_packages -VERSION = "0.2.3" +VERSION = "0.2.4" CLASSIFIERS = [ 'Development Status :: 4 - Beta',