From b69a217a6c08243608c1a6254f57ecaf65369776 Mon Sep 17 00:00:00 2001 From: Sisira Panchagnula Date: Mon, 30 Apr 2018 11:10:02 -0700 Subject: [PATCH 1/2] SUpport for ASP.NET FW:updating to find DOTNET version Fixing logic for version detected & created for dotnot Support for Java --- src/webapp/azext_webapp/_constants.py | 4 ++ src/webapp/azext_webapp/create_util.py | 68 +++++++++++++++++++++++--- src/webapp/azext_webapp/custom.py | 39 +++++++++------ 3 files changed, 89 insertions(+), 22 deletions(-) diff --git a/src/webapp/azext_webapp/_constants.py b/src/webapp/azext_webapp/_constants.py index f218f175fee..f3f1a1202a4 100644 --- a/src/webapp/azext_webapp/_constants.py +++ b/src/webapp/azext_webapp/_constants.py @@ -5,9 +5,13 @@ NODE_VERSION_DEFAULT = "8.9" NETCORE_VERSION_DEFAULT = "2.0" +DOTNET_VERSION_DEFAULT = "4.7" # 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" +DOTNET_RUNTIME_NAME = "aspnet" +JAVA_RUNTIME_NAME = "java" OS_DEFAULT = "Windows" diff --git a/src/webapp/azext_webapp/create_util.py b/src/webapp/azext_webapp/create_util.py index 0978e3a2942..86ecc4d2bcf 100644 --- a/src/webapp/azext_webapp/create_util.py +++ b/src/webapp/azext_webapp/create_util.py @@ -13,7 +13,11 @@ 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) def _resource_client_factory(cli_ctx, **_): @@ -53,10 +57,15 @@ def zip_contents_from_dir(dirPath, lang): def get_runtime_version_details(file_path, lang_name): version_detected = None version_to_create = None + print(lang_name.lower()) 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) @@ -107,22 +116,60 @@ 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) 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' 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 +204,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..4457ce23d3a 100644 --- a/src/webapp/azext_webapp/custom.py +++ b/src/webapp/azext_webapp/custom.py @@ -33,7 +33,7 @@ web_client_factory ) -from ._constants import (NODE_RUNTIME_NAME, OS_DEFAULT) +from ._constants import (NODE_RUNTIME_NAME, OS_DEFAULT, JAVA_RUNTIME_NAME) logger = get_logger(__name__) @@ -64,7 +64,8 @@ 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 + 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') @@ -100,7 +101,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", @@ -152,32 +152,38 @@ def create_deploy_webapp(cmd, name, location=None, dryrun=False): logger.warning("App '%s' already exists", name) 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_java: + # 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...") 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") create_json.update({'app_url': url}) @@ -247,3 +253,4 @@ 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() + \ No newline at end of file From 05f7e238d03f23a0c209bcb81d0fda3ac867bfdc Mon Sep 17 00:00:00 2001 From: Sisira Panchagnula Date: Thu, 3 May 2018 13:30:29 -0700 Subject: [PATCH 2/2] Support for Static HTML file deployment Check the default_RG exists in the current subscription and minor string updates Updating version Pylint fixes Minor fixes --- src/index.json | 8 +++---- src/webapp/azext_webapp/_constants.py | 9 +++---- src/webapp/azext_webapp/create_util.py | 18 ++++++++++---- src/webapp/azext_webapp/custom.py | 33 ++++++++++++++++---------- src/webapp/setup.py | 2 +- 5 files changed, 43 insertions(+), 27 deletions(-) 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 f3f1a1202a4..01f9d36ed4d 100644 --- a/src/webapp/azext_webapp/_constants.py +++ b/src/webapp/azext_webapp/_constants.py @@ -6,12 +6,13 @@ 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" -DOTNET_RUNTIME_NAME = "aspnet" -JAVA_RUNTIME_NAME = "java" -OS_DEFAULT = "Windows" diff --git a/src/webapp/azext_webapp/create_util.py b/src/webapp/azext_webapp/create_util.py index 86ecc4d2bcf..85b60d3ae4d 100644 --- a/src/webapp/azext_webapp/create_util.py +++ b/src/webapp/azext_webapp/create_util.py @@ -17,7 +17,8 @@ DOTNET_RUNTIME_NAME, DOTNET_VERSION_DEFAULT, DOTNET_VERSIONS, - JAVA_RUNTIME_NAME) + JAVA_RUNTIME_NAME, + STATIC_RUNTIME_NAME) def _resource_client_factory(cli_ctx, **_): @@ -57,18 +58,20 @@ def zip_contents_from_dir(dirPath, lang): def get_runtime_version_details(file_path, lang_name): version_detected = None version_to_create = None - print(lang_name.lower()) 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 + # 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} @@ -123,6 +126,7 @@ def get_lang_from_content(src_path): package_json_file = os.path.join(src_path, 'package.json') 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 @@ -137,6 +141,10 @@ def get_lang_from_content(src_path): 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 @@ -158,8 +166,8 @@ 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'] + 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' diff --git a/src/webapp/azext_webapp/custom.py b/src/webapp/azext_webapp/custom.py index 4457ce23d3a..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, JAVA_RUNTIME_NAME) +from ._constants import (NODE_RUNTIME_NAME, OS_DEFAULT, JAVA_RUNTIME_NAME, STATIC_RUNTIME_NAME) logger = get_logger(__name__) @@ -65,12 +68,13 @@ def create_deploy_webapp(cmd, name, location=None, dryrun=False): sku = lang_details.get("default_sku") language = lang_details.get("language") 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) @@ -93,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: @@ -143,16 +147,15 @@ 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: - if not is_java: + 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"]) @@ -175,8 +178,8 @@ def create_deploy_webapp(cmd, name, location=None, dryrun=False): # 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) if not is_java: # Remove the file afer deployment, handling exception if user removed the file manually @@ -185,7 +188,7 @@ def create_deploy_webapp(cmd, name, location=None, dryrun=False): 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 @@ -253,4 +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() - \ No newline at end of file + + +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',