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

[AppService] az functionapp create: Refactor eol impl and sort based on runtime eol date #30636

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -59,6 +59,7 @@ def __init__(self):
self.USE_32_BIT_WORKER_PROC = 'use32BitWorkerProcess'
self.FUNCTIONS_WORKER_RUNTIME = 'FUNCTIONS_WORKER_RUNTIME'
self.GIT_HUB_ACTION_SETTINGS = 'git_hub_action_settings'
self.END_OF_LIFE_DATE = 'endOfLifeDate'


GENERATE_RANDOM_APP_NAMES = os.path.abspath(os.path.join(os.path.abspath(__file__),
Expand Down
62 changes: 25 additions & 37 deletions src/azure-cli/azure/cli/command_modules/appservice/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -4331,7 +4331,7 @@ class _FunctionAppStackRuntimeHelper(_AbstractStackRuntimeHelper):
class Runtime:
def __init__(self, name=None, version=None, is_preview=False, supported_func_versions=None, linux=False,
app_settings_dict=None, site_config_dict=None, app_insights=False, default=False,
github_actions_properties=None):
github_actions_properties=None, end_of_life_date=None):
self.name = name
self.version = version
self.is_preview = is_preview
Expand All @@ -4342,8 +4342,10 @@ def __init__(self, name=None, version=None, is_preview=False, supported_func_ver
self.app_insights = app_insights
self.default = default
self.github_actions_properties = github_actions_properties
self.end_of_life_date = end_of_life_date

self.display_name = "{}|{}".format(name, version) if version else name
self.deprecation_link = LANGUAGE_EOL_DEPRECATION_NOTICES.get(self.display_name, '')

# used for displaying stacks
def to_dict(self):
Expand All @@ -4354,29 +4356,27 @@ def to_dict(self):
d["linux_fx_version"] = self.site_config_dict.linux_fx_version
return d

class RuntimeEOL:
def __init__(self, name=None, version=None, eol=None):
self.name = name
self.version = version
self.eol = eol
self.display_name = "{}|{}".format(name, version)
self.deprecation_link = LANGUAGE_EOL_DEPRECATION_NOTICES.get(self.display_name)

def __init__(self, cmd, linux=False, windows=False):
self.disallowed_functions_versions = {"~1", "~2", "~3"}
self.KEYS = FUNCTIONS_STACKS_API_KEYS()
self.end_of_life_dates = []
super().__init__(cmd, linux=linux, windows=windows)

def validate_end_of_life_date(self, runtime, version):
from dateutil.relativedelta import relativedelta
# we would not be able to validate for a custom runtime
if runtime == 'custom':
return

today = datetime.datetime.now(datetime.timezone.utc)
six_months = today + relativedelta(months=+6)
runtimes_eol = [r for r in self.end_of_life_dates if runtime == r.name]
matched_runtime_eol = next((r for r in runtimes_eol if r.version == version), None)
if matched_runtime_eol:
eol = matched_runtime_eol.eol
runtime_deprecation_link = matched_runtime_eol.deprecation_link or ''
runtimes = [r for r in self.stacks if runtime == r.name]
matched_runtime = next((r for r in runtimes if r.version == version), None)
if matched_runtime:
eol = matched_runtime.end_of_life_date
runtime_deprecation_link = matched_runtime.deprecation_link

if eol is None:
return

if eol < today:
raise ValidationError('{} has reached EOL on {} and is no longer supported. {}'
Expand Down Expand Up @@ -4441,7 +4441,9 @@ def resolve(self, runtime, version=None, functions_version=None, linux=False, di

def get_default_version(self, runtime, functions_version, linux=False):
runtimes = [r for r in self.stacks if r.linux == linux and r.name == runtime]
runtimes.sort(key=lambda r: r.default, reverse=True) # make runtimes with default=True appear first
# sort runtimes by end of life date
runtimes.sort(key=lambda r: r.end_of_life_date or
datetime.datetime.min.replace(tzinfo=datetime.timezone.utc), reverse=True)
for r in runtimes:
if functions_version in r.supported_func_versions:
return r
Expand Down Expand Up @@ -4480,8 +4482,7 @@ def _get_valid_function_versions(self, runtime_settings):
valid_versions.append(self._format_version_name(v))
return valid_versions

def _parse_minor_version(self, runtime_settings, major_version_name, minor_version_name, runtime_to_version,
runtime_to_version_eol):
def _parse_minor_version(self, runtime_settings, major_version_name, minor_version_name, runtime_to_version):
runtime_name = (runtime_settings.app_settings_dictionary.get(self.KEYS.FUNCTIONS_WORKER_RUNTIME) or
major_version_name)
if not runtime_settings.is_deprecated:
Expand All @@ -4494,17 +4495,13 @@ def _parse_minor_version(self, runtime_settings, major_version_name, minor_versi
self.KEYS.APPLICATION_INSIGHTS: runtime_settings.app_insights_settings.is_supported,
self.KEYS.SITE_CONFIG_DICT: runtime_settings.site_config_properties_dictionary,
self.KEYS.IS_DEFAULT: bool(runtime_settings.is_default),
self.KEYS.GIT_HUB_ACTION_SETTINGS: runtime_settings.git_hub_action_settings
self.KEYS.GIT_HUB_ACTION_SETTINGS: runtime_settings.git_hub_action_settings,
self.KEYS.END_OF_LIFE_DATE: runtime_settings.end_of_life_date,
}

runtime_to_version[runtime_name] = runtime_to_version.get(runtime_name, {})
runtime_to_version[runtime_name][minor_version_name] = runtime_version_properties

# obtain end of life date for all runtime versions
if runtime_settings.end_of_life_date is not None:
runtime_to_version_eol[runtime_name] = runtime_to_version_eol.get(runtime_name, {})
runtime_to_version_eol[runtime_name][minor_version_name] = runtime_settings.end_of_life_date

def _create_runtime_from_properties(self, runtime_name, version_name, version_properties, linux):
supported_func_versions = version_properties[self.KEYS.SUPPORTED_EXTENSION_VERSIONS]
return self.Runtime(name=runtime_name,
Expand All @@ -4516,14 +4513,13 @@ def _create_runtime_from_properties(self, runtime_name, version_name, version_pr
app_settings_dict=version_properties[self.KEYS.APP_SETTINGS_DICT],
app_insights=version_properties[self.KEYS.APPLICATION_INSIGHTS],
default=version_properties[self.KEYS.IS_DEFAULT],
github_actions_properties=version_properties[self.KEYS.GIT_HUB_ACTION_SETTINGS]
)
github_actions_properties=version_properties[self.KEYS.GIT_HUB_ACTION_SETTINGS],
end_of_life_date=version_properties[self.KEYS.END_OF_LIFE_DATE])

def _parse_raw_stacks(self, stacks):
# build a map of runtime -> runtime version -> runtime version properties
runtime_to_version_linux = {}
runtime_to_version_windows = {}
runtime_to_version_end_of_life = {}
for runtime in stacks:
for major_version in runtime.major_versions:
for minor_version in major_version.minor_versions:
Expand All @@ -4535,19 +4531,16 @@ def _parse_raw_stacks(self, stacks):
self._parse_minor_version(runtime_settings=linux_settings,
major_version_name=runtime.name,
minor_version_name=runtime_version,
runtime_to_version=runtime_to_version_linux,
runtime_to_version_eol=runtime_to_version_end_of_life)
runtime_to_version=runtime_to_version_linux)

if windows_settings is not None and not windows_settings.is_hidden:
self._parse_minor_version(runtime_settings=windows_settings,
major_version_name=runtime.name,
minor_version_name=runtime_version,
runtime_to_version=runtime_to_version_windows,
runtime_to_version_eol=runtime_to_version_end_of_life)
runtime_to_version=runtime_to_version_windows)

runtime_to_version_linux = self._format_version_names(runtime_to_version_linux)
runtime_to_version_windows = self._format_version_names(runtime_to_version_windows)
runtime_to_version_end_of_life = self._format_version_names(runtime_to_version_end_of_life)

for runtime_name, versions in runtime_to_version_windows.items():
for version_name, version_properties in versions.items():
Expand All @@ -4559,11 +4552,6 @@ def _parse_raw_stacks(self, stacks):
r = self._create_runtime_from_properties(runtime_name, version_name, version_properties, linux=True)
self._stacks.append(r)

for runtime_name, versions in runtime_to_version_end_of_life.items():
for version_name, version_eol in versions.items():
r = self.RuntimeEOL(name=runtime_name, version=version_name, eol=version_eol)
self.end_of_life_dates.append(r)


def get_app_insights_key(cli_ctx, resource_group, name):
appinsights_client = get_mgmt_service_client(cli_ctx, ApplicationInsightsManagementClient)
Expand Down
Loading