-
Notifications
You must be signed in to change notification settings - Fork 0
[AZINTS-3933] Use ARG to collect existing Log Forwarders #9
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
Changes from all commits
cca1871
b697d02
a37f7d5
5763580
b9b8faf
9848630
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,84 +1,100 @@ | ||
| from dataclasses import dataclass | ||
| from json import JSONDecodeError, loads | ||
| from logging import getLogger | ||
| from typing import Final | ||
| from typing import Final, Optional | ||
|
|
||
| from .az_cmd import AzCmd, execute | ||
| from .configuration import Configuration | ||
|
|
||
| log = getLogger("installer") | ||
|
|
||
| CONTROL_PLANE_RESOURCES_TASK_PREFIX: Final = "resources-task" | ||
|
|
||
| @dataclass(frozen=True) | ||
| class LfoControlPlane: | ||
| subscription: tuple[str, str] # id, name | ||
| resource_group: str | ||
| region: str | ||
|
|
||
| @dataclass(frozen=True) | ||
| class LfoMetadata: | ||
| control_plane: LfoControlPlane | ||
| monitored_subs: dict[str, str] | ||
| control_plane_sub: tuple[str, str] | ||
| control_plane_rg: str | ||
|
|
||
| def find_existing_lfo_control_planes( | ||
| sub_id_to_name: dict[str, str], subscriptions: Optional[set[str]] = None | ||
| ) -> dict[str, LfoControlPlane]: | ||
| """Find existing lfo control planes in the tenant. If `subscriptions` is specified, search is limited to these subscriptions.""" | ||
| if subscriptions is not None: | ||
| if len(subscriptions) == 0: | ||
| return {} # searching empty set of subscriptions | ||
| subscriptions_clause = " and subscriptionId in ({})".format(", ".join(["'{}'".format(subscription_id) for subscription_id in subscriptions])) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 3.9 doesn't have f-strings? 🤔
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does but we're nesting and also using quotes in the string.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could probably make use of single and double quotes to make it work but doesn't matter |
||
| else: | ||
| subscriptions_clause = "" | ||
|
|
||
| # make sure azure resource graph extension is installed | ||
| if not execute(AzCmd("extension", "show").param("--name", "resource-graph"), can_fail=True): | ||
| execute(AzCmd("extension", "add").param("--name", "resource-graph").param("--yes", "")) | ||
|
|
||
| func_apps_json = execute(AzCmd("graph", "query").param( | ||
| "-q", | ||
| f"\"Resources | where type == 'microsoft.web/sites' and kind contains 'functionapp' and name startswith '{CONTROL_PLANE_RESOURCES_TASK_PREFIX}'{subscriptions_clause} | project name, resourceGroup, subscriptionId, location, properties.state\"", | ||
| )) | ||
| try: | ||
| func_apps_response = loads(func_apps_json) | ||
| except JSONDecodeError as e: | ||
| log.error(f"Invalid JSON: {func_apps_json}") | ||
| log.error(f"Error: {e}") | ||
| raise | ||
|
|
||
| existing_control_planes: dict[str, LfoControlPlane] = {} | ||
| for func_app in func_apps_response["data"]: | ||
| subscription_id = func_app["subscriptionId"] | ||
| existing_control_planes[func_app["name"]] = LfoControlPlane( | ||
| (subscription_id, sub_id_to_name[subscription_id]), | ||
| func_app["resourceGroup"], | ||
| func_app["location"], | ||
| ) | ||
| return existing_control_planes | ||
|
|
||
|
|
||
| def check_existing_lfo( | ||
| subscriptions: set[str], sub_id_to_name: dict[str, str] | ||
| ) -> dict[str, LfoMetadata]: | ||
| """Check if LFO is already installed""" | ||
| """Check if LFO is already installed on any of the given subscriptions""" | ||
| log.info( | ||
| "Checking if log forwarding is already installed in this Azure environment..." | ||
| ) | ||
|
|
||
| control_planes = find_existing_lfo_control_planes(sub_id_to_name, subscriptions) | ||
| existing_lfos: dict[str, LfoMetadata] = {} # map control plane ID to metadata | ||
|
|
||
| for sub_id in subscriptions: | ||
| func_apps_json = execute( | ||
| AzCmd("functionapp", "list") | ||
| .param("--subscription", sub_id) | ||
| .param( | ||
| "--query", | ||
| f"\"[?starts_with(name,'{CONTROL_PLANE_RESOURCES_TASK_PREFIX}')].{{resourceGroup:resourceGroup, name:name}}\"", | ||
| ) | ||
| .param("--output", "json") | ||
| for resource_task_name, control_plane in control_planes.items(): | ||
| control_plane_id = resource_task_name.split("-")[-1] | ||
|
|
||
| resource_task_monitored_sub_ids = execute( | ||
| AzCmd("functionapp", "config appsettings list") | ||
| .param("--subscription", control_plane.subscription[0]) | ||
| .param("--name", resource_task_name) | ||
| .param("--resource-group", control_plane.resource_group) | ||
| .param("--query", "\"[?name=='MONITORED_SUBSCRIPTIONS'].value\"") | ||
| .param("--output", "tsv") | ||
| ) | ||
|
|
||
| if not resource_task_monitored_sub_ids: | ||
| continue | ||
|
|
||
| try: | ||
| resources_task_json = loads(func_apps_json) | ||
| monitored_sub_ids = loads(resource_task_monitored_sub_ids) | ||
| except JSONDecodeError as e: | ||
| log.error(f"Invalid JSON: {func_apps_json}") | ||
| log.error(f"Invalid JSON: {resource_task_monitored_sub_ids}") | ||
| log.error(f"Error: {e}") | ||
| raise | ||
|
|
||
| if not resources_task_json: | ||
| continue | ||
|
|
||
| for resources_task in resources_task_json: | ||
| name = resources_task["name"] | ||
| rg = resources_task["resourceGroup"] | ||
| control_plane_id = name.split("-")[-1] | ||
|
|
||
| resource_task_monitored_sub_ids = execute( | ||
| AzCmd("functionapp", "config appsettings list") | ||
| .param("--subscription", sub_id) | ||
| .param("--name", name) | ||
| .param("--resource-group", rg) | ||
| .param("--query", "\"[?name=='MONITORED_SUBSCRIPTIONS'].value\"") | ||
| .param("--output", "tsv") | ||
| ) | ||
|
|
||
| if not resource_task_monitored_sub_ids: | ||
| continue | ||
|
|
||
| try: | ||
| monitored_sub_ids = loads(resource_task_monitored_sub_ids) | ||
| except JSONDecodeError as e: | ||
| log.error(f"Invalid JSON: {resource_task_monitored_sub_ids}") | ||
| log.error(f"Error: {e}") | ||
| raise | ||
|
|
||
| existing_lfos[control_plane_id] = LfoMetadata( | ||
| monitored_subs={ | ||
| sub_id: sub_id_to_name[sub_id] for sub_id in monitored_sub_ids | ||
| }, | ||
| control_plane_sub=(sub_id, sub_id_to_name[sub_id]), | ||
| control_plane_rg=rg, | ||
| ) | ||
| existing_lfos[control_plane_id] = LfoMetadata( | ||
| control_plane, | ||
| monitored_subs={ | ||
| sub_id: sub_id_to_name[sub_id] for sub_id in monitored_sub_ids | ||
| }, | ||
| ) | ||
|
|
||
| return existing_lfos | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a specific error string we can look for relating to the extension instead of using this flag?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I considered that but ultimately figured we're better off leaving it open ended. We don't want it to break if the error message changes, and the risk of attempting to (re?)-install the extension when we get some other error is low - it would just lead to another error being raised and logged later, most likely when we make that attempt.