Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[App Service] BREAKING CHANGE: az webapp list-runtimes: Use new stacks APIs #21057

Merged
merged 48 commits into from
Feb 11, 2022
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
f1bab4e
WIP
StrawnSC Nov 10, 2021
fc7c00b
WIP
StrawnSC Dec 2, 2021
8b29533
WIP
StrawnSC Dec 2, 2021
a2bc4cc
WIP
StrawnSC Dec 2, 2021
48d6318
finish list runtimes impl and switch to new argument usage -- webapp …
StrawnSC Dec 2, 2021
ba0bfe2
fix az webapp create
StrawnSC Dec 2, 2021
bbe5d37
WIP
StrawnSC Jan 18, 2022
2675935
WIP
StrawnSC Jan 18, 2022
dd8e907
WIP
StrawnSC Jan 19, 2022
215d441
WIP
StrawnSC Jan 19, 2022
d70e741
fix tests
StrawnSC Jan 19, 2022
39755c9
fix tests
StrawnSC Jan 19, 2022
112f2c3
fix tests
StrawnSC Jan 19, 2022
02914e2
fix tests
StrawnSC Jan 19, 2022
2676c6d
fix test_webapp_up_python_e2e
StrawnSC Jan 19, 2022
1cfc519
fix test_webapp_up_statichtml_e2e
StrawnSC Jan 19, 2022
7ee6cc5
use runtime objects over dicts and include ease of use runtime name r…
StrawnSC Jan 21, 2022
5b5eb65
mark flaky tests to skip them
StrawnSC Jan 21, 2022
b34122d
bug fix
StrawnSC Jan 21, 2022
7b311c0
bug fix
StrawnSC Jan 21, 2022
ca08b9c
bug fix
StrawnSC Jan 21, 2022
7cfbe94
fix style
StrawnSC Jan 21, 2022
7b6e3a4
merge dev
StrawnSC Jan 21, 2022
9ca2670
rerecord webapp tests
StrawnSC Jan 21, 2022
ff9776f
rectify webapp up tests
StrawnSC Jan 21, 2022
cdbdab9
rectify webapp up tests again
StrawnSC Jan 21, 2022
d0d380f
remove erroneous import
StrawnSC Jan 21, 2022
57dede0
update webapp up test
StrawnSC Jan 21, 2022
c78a60e
update webapp up test
StrawnSC Jan 21, 2022
41a65ce
rerecord webapp access restriction tests
StrawnSC Jan 21, 2022
5d80a50
fix credscan fail
StrawnSC Jan 24, 2022
7dbdd2c
fix windows java SE configs
StrawnSC Jan 24, 2022
dceb351
merge dev
StrawnSC Jan 26, 2022
9fb5293
return both windows and linux stacks by default
StrawnSC Jan 26, 2022
9e7f061
use --os argument over --windows
StrawnSC Jan 26, 2022
b791493
implement 'az functionapp list-runtimes'
StrawnSC Jan 28, 2022
2501e72
change '(WIP) az functionapp create' to use new runtimes API ; still …
StrawnSC Jan 31, 2022
0841b82
rerecord 'test_functionapp_commands' tests
StrawnSC Feb 1, 2022
877d889
rerecord more tests
StrawnSC Feb 1, 2022
06d8743
Merge branch 'dev' into unhardcode_webapp_list-runtimes
StrawnSC Feb 1, 2022
d8d886d
fix GH actions
StrawnSC Feb 1, 2022
90687ad
remove function app stacks JSON file
StrawnSC Feb 2, 2022
92fab03
Merge branch 'dev' into unhardcode_webapp_list-runtimes
StrawnSC Feb 8, 2022
365cfb4
add back hardcoded function; use colon delimiter; fix deprecation
StrawnSC Feb 8, 2022
b2ddda3
remove creds from recordings
StrawnSC Feb 9, 2022
b8f7a91
add back appservice package data
StrawnSC Feb 9, 2022
15eca32
Merge branch 'dev' into unhardcode_webapp_list-runtimes
StrawnSC Feb 10, 2022
896da30
dummy commit to rerun CI
StrawnSC Feb 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,18 @@
# --------------------------------------------------------------------------------------------
import os

NODE_VERSION_DEFAULT = "10.14"
NODE_VERSION_NEWER = "12-lts"
NODE_EXACT_VERSION_DEFAULT = "10.14.1"
NETCORE_VERSION_DEFAULT = "3.1"
ASPDOTNET_VERSION_DEFAULT = "4.8"
DOTNET_VERSION_DEFAULT = "5.0"
DOTNET_TARGET_FRAMEWORK_REGEX = r"^net\d+\.\d+$"
PYTHON_VERSION_DEFAULT = "3.7"
NETCORE_RUNTIME_NAME = "dotnetcore"
ASPDOTNET_RUNTIME_NAME = "aspnet"
DOTNET_RUNTIME_NAME = "dotnet"
NODE_RUNTIME_NAME = "node"
PYTHON_RUNTIME_NAME = "python"
OS_DEFAULT = "Windows"
LINUX_OS_NAME = "linux"
WINDOWS_OS_NAME = "windows"
STATIC_RUNTIME_NAME = "static" # not an official supported runtime but used for CLI logic
NODE_VERSIONS = ['10.6', '10.14']
PYTHON_VERSIONS = ['3.9', '3.8', '3.7', '3.6']
NETCORE_VERSIONS = ['2.1', '3.1']
DOTNET_VERSIONS = ['5.0', '6.0']
ASPDOTNET_VERSIONS = ['3.5', '4.8']
LINUX_SKU_DEFAULT = "P1V2"
FUNCTIONS_VERSIONS = ['2', '3', '4']
FUNCTIONS_STACKS_API_JSON_PATHS = {
'windows': os.path.abspath(os.path.join(os.path.abspath(__file__), '../resources/WindowsFunctionsStacks.json')),
'linux': os.path.abspath(os.path.join(os.path.abspath(__file__), '../resources/LinuxFunctionsStacks.json'))
}
FUNCTIONS_LINUX_RUNTIME_VERSION_REGEX = r"^.*\|(.*)$"
FUNCTIONS_WINDOWS_RUNTIME_VERSION_REGEX = r"^~(.*)$"
FUNCTIONS_NO_V2_REGIONS = {
Expand Down Expand Up @@ -69,9 +55,6 @@ def __init__(self):
self.FUNCTIONS_WORKER_RUNTIME = 'FUNCTIONS_WORKER_RUNTIME'


RUNTIME_STACKS = os.path.abspath(os.path.join(os.path.abspath(__file__),
'../resources/WebappRuntimeStacks.json'))

GENERATE_RANDOM_APP_NAMES = os.path.abspath(os.path.join(os.path.abspath(__file__),
'../resources/GenerateRandomAppNames.json'))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@
from azure.cli.core.util import get_file_json
from azure.mgmt.web.models import SkuDescription

from ._constants import (NETCORE_VERSION_DEFAULT, NETCORE_VERSIONS, NODE_VERSION_DEFAULT,
NODE_VERSIONS, NETCORE_RUNTIME_NAME, NODE_RUNTIME_NAME, ASPDOTNET_RUNTIME_NAME,
ASPDOTNET_VERSION_DEFAULT, DOTNET_VERSIONS, STATIC_RUNTIME_NAME,
PYTHON_RUNTIME_NAME, PYTHON_VERSION_DEFAULT, LINUX_SKU_DEFAULT, OS_DEFAULT,
NODE_VERSION_NEWER, DOTNET_RUNTIME_NAME, DOTNET_VERSION_DEFAULT, ASPDOTNET_VERSIONS,
from ._constants import (NETCORE_RUNTIME_NAME, NODE_RUNTIME_NAME, ASPDOTNET_RUNTIME_NAME, STATIC_RUNTIME_NAME,
PYTHON_RUNTIME_NAME, LINUX_SKU_DEFAULT, OS_DEFAULT, DOTNET_RUNTIME_NAME,
DOTNET_TARGET_FRAMEWORK_REGEX, GENERATE_RANDOM_APP_NAMES)
from .utils import get_resource_if_exists

Expand Down Expand Up @@ -75,31 +72,31 @@ def zip_contents_from_dir(dirPath, lang):
return zip_file_path


def get_runtime_version_details(file_path, lang_name):
def get_runtime_version_details(file_path, lang_name, stack_helper, is_linux=False):
version_detected = None
version_to_create = None
if lang_name.lower() == DOTNET_RUNTIME_NAME:
version_detected = parse_dotnet_version(file_path, DOTNET_VERSION_DEFAULT)
version_to_create = detect_dotnet_version_tocreate(version_detected, DOTNET_VERSION_DEFAULT, DOTNET_VERSIONS)

if lang_name.lower() != STATIC_RUNTIME_NAME:
versions = stack_helper.get_version_list(lang_name, is_linux)
default_version = stack_helper.get_default_version(lang_name, is_linux)
version_to_create = default_version
if lang_name.lower() in [DOTNET_RUNTIME_NAME, ASPDOTNET_RUNTIME_NAME]:
version_detected = parse_dotnet_version(file_path, default_version)
version_to_create = detect_dotnet_version_tocreate(version_detected, default_version, versions)
elif 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() == ASPDOTNET_RUNTIME_NAME:
# method returns list in DESC, pick the first
version_detected = parse_dotnet_version(file_path, ASPDOTNET_VERSION_DEFAULT)
version_to_create = detect_dotnet_version_tocreate(version_detected,
ASPDOTNET_VERSION_DEFAULT, ASPDOTNET_VERSIONS)
version_to_create = detect_dotnet_version_tocreate(version_detected, default_version, versions)
elif lang_name.lower() == NODE_RUNTIME_NAME:
if file_path == '':
version_detected = "-"
version_to_create = NODE_VERSION_DEFAULT
version_to_create = default_version
else:
version_detected = parse_node_version(file_path)[0]
version_to_create = detect_node_version_tocreate(version_detected)
version_to_create = detect_node_version_tocreate(version_detected, versions, default_version)
elif lang_name.lower() == PYTHON_RUNTIME_NAME:
version_detected = "-"
version_to_create = PYTHON_VERSION_DEFAULT
version_to_create = default_version
elif lang_name.lower() == STATIC_RUNTIME_NAME:
version_detected = "-"
version_to_create = "-"
Expand Down Expand Up @@ -138,7 +135,7 @@ def get_num_apps_in_asp(cmd, rg_name, asp_name):


# pylint:disable=unexpected-keyword-arg
def get_lang_from_content(src_path, html=False):
def get_lang_from_content(src_path, html=False, is_linux=False):
# NODE: package.json should exist in the application root dir
# NETCORE & DOTNET: *.csproj should exist in the application dir
# NETCORE: <TargetFramework>netcoreapp2.0</TargetFramework>
Expand Down Expand Up @@ -181,7 +178,7 @@ def get_lang_from_content(src_path, html=False):
runtime_details_dict['file_loc'] = package_json_file if os.path.isfile(package_json_file) else ''
runtime_details_dict['default_sku'] = LINUX_SKU_DEFAULT
elif package_netcore_file:
runtime_lang = detect_dotnet_lang(package_netcore_file)
runtime_lang = detect_dotnet_lang(package_netcore_file, is_linux=is_linux)
runtime_details_dict['language'] = runtime_lang
runtime_details_dict['file_loc'] = package_netcore_file
runtime_details_dict['default_sku'] = 'F1'
Expand All @@ -192,9 +189,13 @@ def get_lang_from_content(src_path, html=False):
return runtime_details_dict


def detect_dotnet_lang(csproj_path):
def detect_dotnet_lang(csproj_path, is_linux=False):
import xml.etree.ElementTree as ET
import re

if is_linux:
return NETCORE_RUNTIME_NAME

parsed_file = ET.parse(csproj_path)
root = parsed_file.getroot()
version_lang = ''
Expand Down Expand Up @@ -269,12 +270,6 @@ def parse_node_version(file_path):
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, default_version, versions_list):
min_ver = versions_list[0]
if detected_ver in versions_list:
Expand All @@ -284,18 +279,11 @@ def detect_dotnet_version_tocreate(detected_ver, default_version, versions_list)
return default_version


def detect_node_version_tocreate(detected_ver):
if detected_ver in NODE_VERSIONS:
# TODO include better detections logic here
def detect_node_version_tocreate(detected_ver, node_versions, default_node_version):
if detected_ver in node_versions:
return detected_ver
# get major version & get the closest version from supported list
major_ver = int(detected_ver.split('.')[0])
node_ver = NODE_VERSION_DEFAULT
# TODO: Handle checking for minor versions if node major version is 10
if major_ver <= 11:
node_ver = NODE_VERSION_DEFAULT
else:
node_ver = NODE_VERSION_NEWER
return node_ver
return default_node_version


def find_key_in_json(json_data, key):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1449,7 +1449,7 @@
az webapp create -g MyResourceGroup -p MyPlan -n MyUniqueAppName --runtime "java:11:Java SE:11"
- name: Create a web app with a NodeJS 10.14 runtime and deployed from a local git repository.
text: >
az webapp create -g MyResourceGroup -p MyPlan -n MyUniqueAppName --runtime "node|10.14" --deployment-local-git
az webapp create -g MyResourceGroup -p MyPlan -n MyUniqueAppName --runtime "node|12LTS" --deployment-local-git
- name: Create a web app with an image from DockerHub.
text: >
az webapp create -g MyResourceGroup -p MyPlan -n MyUniqueAppName -i nginx
Expand Down Expand Up @@ -1821,6 +1821,11 @@
short-summary: List available built-in stacks which can be used for web apps.
"""

helps['functionapp list-runtimes'] = """
type: command
short-summary: List available built-in stacks which can be used for function apps. Runtimes are shown in the form "runtime|version"
"""

helps['webapp log'] = """
type: group
short-summary: Manage web app logs.
Expand Down
65 changes: 10 additions & 55 deletions src/azure-cli/azure/cli/command_modules/appservice/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
from azure.cli.core.commands.parameters import (resource_group_name_type, get_location_type,
get_resource_name_completion_list, file_type,
get_three_state_flag, get_enum_type, tags_type)
from azure.cli.core.util import get_file_json
from azure.cli.core.local_context import LocalContextAttribute, LocalContextAction
from azure.cli.command_modules.appservice._appservice_utils import MSI_LOCAL_ID
from azure.mgmt.web.models import DatabaseType, ConnectionStringType, BuiltInAuthenticationProvider, AzureStorageType

from ._completers import get_hostname_completion_list
from ._constants import FUNCTIONS_VERSIONS, FUNCTIONS_STACKS_API_JSON_PATHS, FUNCTIONS_STACKS_API_KEYS
from ._constants import (FUNCTIONS_VERSIONS, WINDOWS_OS_NAME, LINUX_OS_NAME)

from ._validators import (validate_timeout_value, validate_site_create, validate_asp_create,
validate_add_vnet, validate_front_end_scale_factor, validate_ase_create, validate_ip_address,
validate_service_tag, validate_public_cloud)
Expand All @@ -32,8 +32,6 @@
MULTI_CONTAINER_TYPES = ['COMPOSE', 'KUBE']
FTPS_STATE_TYPES = ['AllAllowed', 'FtpsOnly', 'Disabled']
OS_TYPES = ['Windows', 'Linux']
LINUX_RUNTIMES = ['dotnet', 'node', 'python', 'java']
WINDOWS_RUNTIMES = ['dotnet', 'node', 'java', 'powershell']
ACCESS_RESTRICTION_ACTION_TYPES = ['Allow', 'Deny']
ASE_LOADBALANCER_MODES = ['Internal', 'External']
ASE_KINDS = ['ASEv2', 'ASEv3']
Expand Down Expand Up @@ -80,8 +78,6 @@ def load_arguments(self, _):
arg_type=get_enum_type(['Free', 'Standard'])
)

functionapp_runtime_strings, functionapp_runtime_to_version_strings = _get_functionapp_runtime_versions()

# use this hidden arg to give a command the right instance, that functionapp commands
# work on function app and webapp ones work on web app
with self.argument_context('webapp') as c:
Expand Down Expand Up @@ -170,7 +166,11 @@ def load_arguments(self, _):
c.argument('slot', options_list=['--slot', '-s'], help='Name of the web app slot. Default to the productions slot if not specified.')

with self.argument_context('webapp list-runtimes') as c:
StrawnSC marked this conversation as resolved.
Show resolved Hide resolved
c.argument('linux', action='store_true', help='list runtime stacks for linux based web apps')
c.argument('linux', action='store_true', help='list runtime stacks for linux based web apps. Use "--os-type linux" instead', deprecate_info=c.deprecate())
StrawnSC marked this conversation as resolved.
Show resolved Hide resolved
c.argument('os_type', options_list=["--os", "--os-type"], help="limit the output to just windows or linux runtimes", arg_type=get_enum_type([LINUX_OS_NAME, WINDOWS_OS_NAME]))

with self.argument_context('functionapp list-runtimes') as c:
c.argument('os_type', options_list=["--os", "--os-type"], help="limit the output to just windows or linux runtimes", arg_type=get_enum_type([LINUX_OS_NAME, WINDOWS_OS_NAME]))

with self.argument_context('webapp deleted list') as c:
c.argument('name', arg_type=webapp_name_arg_type, id_part=None)
Expand Down Expand Up @@ -322,7 +322,7 @@ def load_arguments(self, _):
c.argument('php_version', help='The version used to run your web app if using PHP, e.g., 5.5, 5.6, 7.0')
c.argument('python_version', help='The version used to run your web app if using Python, e.g., 2.7, 3.4')
c.argument('net_framework_version', help="The version used to run your web app if using .NET Framework, e.g., 'v4.0' for .NET 4.6 and 'v3.0' for .NET 3.5")
c.argument('linux_fx_version', help="The runtime stack used for your linux-based webapp, e.g., \"RUBY|2.5.5\", \"NODE|10.14\", \"PHP|7.2\", \"DOTNETCORE|2.1\". See https://aka.ms/linux-stacks for more info.")
c.argument('linux_fx_version', help="The runtime stack used for your linux-based webapp, e.g., \"RUBY|2.5.5\", \"NODE|12LTS\", \"PHP|7.2\", \"DOTNETCORE|2.1\". See https://aka.ms/linux-stacks for more info.")
c.argument('windows_fx_version', help="A docker image name used for your windows container web app, e.g., microsoft/nanoserver:ltsc2016")
if scope == 'functionapp':
c.ignore('windows_fx_version')
Expand Down Expand Up @@ -735,11 +735,10 @@ def load_arguments(self, _):
help='The container registry server password. Required for private registries.')
if scope == 'functionapp':
c.argument('functions_version', help='The functions app version. NOTE: This will be required starting the next release cycle', arg_type=get_enum_type(FUNCTIONS_VERSIONS))
c.argument('runtime', help='The functions runtime stack.',
arg_type=get_enum_type(functionapp_runtime_strings))
c.argument('runtime', help='The functions runtime stack. Use "az functionapp list-runtimes" to check supported runtimes and versions')
c.argument('runtime_version',
help='The version of the functions runtime stack. '
'Allowed values for each --runtime are: ' + ', '.join(functionapp_runtime_to_version_strings))
'The functions runtime stack. Use "az functionapp list-runtimes" to check supported runtimes and versions')

with self.argument_context('functionapp config hostname') as c:
c.argument('webapp_name', arg_type=functionapp_name_arg_type, id_part='name')
Expand Down Expand Up @@ -1052,47 +1051,3 @@ def load_arguments(self, _):
with self.argument_context('staticwebapp functions link') as c:
c.argument('function_resource_id', help="Resource ID of the functionapp to link. Can be retrieved with 'az functionapp --query id'")
c.argument('force', help="Force the function link even if the function is already linked to a static webapp. May be needed if the function was previously linked to a static webapp.")


def _get_functionapp_runtime_versions():
# set up functionapp create help menu
KEYS = FUNCTIONS_STACKS_API_KEYS()
stacks_api_json_list = []
stacks_api_json_list.append(get_file_json(FUNCTIONS_STACKS_API_JSON_PATHS['windows']))
stacks_api_json_list.append(get_file_json(FUNCTIONS_STACKS_API_JSON_PATHS['linux']))

# build a map of runtime -> runtime version -> runtime version properties
runtime_to_version = {}
for stacks_api_json in stacks_api_json_list:
for runtime_json in stacks_api_json[KEYS.VALUE]:
runtime_name = runtime_json[KEYS.NAME]
for runtime_version_json in runtime_json[KEYS.PROPERTIES][KEYS.MAJOR_VERSIONS]:
runtime_version = runtime_version_json[KEYS.DISPLAY_VERSION]
runtime_version_properties = {
KEYS.IS_HIDDEN: runtime_version_json[KEYS.IS_HIDDEN],
KEYS.IS_DEPRECATED: runtime_version_json[KEYS.IS_DEPRECATED],
KEYS.IS_PREVIEW: runtime_version_json[KEYS.IS_PREVIEW],
}
runtime_to_version[runtime_name] = runtime_to_version.get(runtime_name, dict())
runtime_to_version[runtime_name][runtime_version] = runtime_version_properties

# traverse the map to build an ordered string of runtimes -> runtime versions,
# taking their properties into account (i.e. isHidden, isPreview)
runtime_to_version_strings = []
for runtime, runtime_versions in runtime_to_version.items():
# dotnet and custom version is not configurable, so leave out of help menu
if runtime in ('dotnet', 'custom'):
continue
ordered_runtime_versions = list(runtime_versions.keys())
ordered_runtime_versions.sort(key=float)
ordered_runtime_versions_strings = []
for version in ordered_runtime_versions:
if runtime_versions[version][KEYS.IS_HIDDEN] or runtime_versions[version][KEYS.IS_DEPRECATED]:
continue
if runtime_versions[version][KEYS.IS_PREVIEW]:
ordered_runtime_versions_strings.append(version + ' (preview)')
else:
ordered_runtime_versions_strings.append(version)
runtime_to_version_strings.append(runtime + ' -> [' + ', '.join(ordered_runtime_versions_strings) + ']')

return runtime_to_version.keys(), runtime_to_version_strings
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@ def load_command_table(self, _):
g.custom_command('restart', 'restart_webapp')
g.custom_command('browse', 'view_in_browser')
g.custom_command('list-instances', 'list_instances')
# TO DO: Move back to using list_runtimes function once Available Stacks API is updated (it's updated with Antares deployments)
g.custom_command('list-runtimes', 'list_runtimes_hardcoded')
g.custom_command('list-runtimes', 'list_runtimes')
g.custom_command('identity assign', 'assign_identity')
g.custom_show_command('identity show', 'show_identity')
g.custom_command('identity remove', 'remove_identity')
Expand Down Expand Up @@ -309,6 +308,7 @@ def load_command_table(self, _):
with self.command_group('functionapp') as g:
g.custom_command('create', 'create_functionapp', exception_handler=ex_handler_factory(),
validator=validate_vnet_integration)
g.custom_command('list-runtimes', 'list_function_app_runtimes')
g.custom_command('list', 'list_function_app', table_transformer=transform_web_list_output)
g.custom_show_command('show', 'show_functionapp', table_transformer=transform_web_output)
g.custom_command('delete', 'delete_function_app')
Expand Down
Loading