Skip to content
Merged
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
9 changes: 8 additions & 1 deletion src/amg/HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,11 @@ Release History

2.7.0
++++++
* `az grafana api-key`: deprecate command group as Grafana Labs is sunsetting API keys
* `az grafana api-key`: deprecate command group as Grafana Labs is sunsetting API keys

2.8.0
++++++
* `az grafana folder create`: support parent folder argument for nested folder creation
* `az grafana folder list`: support nested folders
* `az grafana folder show`: support nested folders
* `az grafana backup`: include corresponding nested folders when individual folders are specified
1 change: 1 addition & 0 deletions src/amg/azext_amg/_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def load_arguments(self, _):

with self.argument_context("grafana folder") as c:
c.argument("title", help="title of the folder")
c.argument("parent_folder", options_list=["--parent-folder"], help="Name or uid of the parent folder, if any")

with self.argument_context("grafana user") as c:
c.argument("user", help="user login name or email")
Expand Down
8 changes: 6 additions & 2 deletions src/amg/azext_amg/backup_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,14 @@ def get_all_folders(grafana_url, http_headers, **kwargs):
folders_to_exclude = kwargs.get('folders_to_exclude')
if folders_to_include:
folders_to_include = [f.lower() for f in folders_to_include]
folders = [f for f in folders if f.get('title', '').lower() in folders_to_include]
folders = [f for f in folders if
f.get('title', '').lower() in folders_to_include or
f.get('folderTitle', '').lower() in folders_to_include]
if folders_to_exclude:
folders_to_exclude = [f.lower() for f in folders_to_exclude]
folders = [f for f in folders if f.get('title', '').lower() not in folders_to_exclude]
folders = [f for f in folders if
f.get('title', '').lower() not in folders_to_exclude and
f.get('folderTitle', '').lower() not in folders_to_exclude]

individual_folders = []
for folder in folders:
Expand Down
76 changes: 46 additions & 30 deletions src/amg/azext_amg/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from .aaz.latest.grafana._update import Update as _GrafanaUpdate

from ._client_factory import cf_amg
from .utils import get_yes_or_no_option
from .utils import get_yes_or_no_option, search_folders

logger = get_logger(__name__)

Expand Down Expand Up @@ -538,19 +538,33 @@ def test_notification_channel(cmd, grafana_name, notification_channel, resource_
return json.loads(response.content)


def create_folder(cmd, grafana_name, title, resource_group_name=None, api_key_or_token=None, subscription=None):
def create_folder(cmd, grafana_name, title, parent_folder=None, resource_group_name=None, api_key_or_token=None,
subscription=None):
payload = {
"title": title
"title": title,
}
if parent_folder:
data = _find_folder(cmd, resource_group_name, grafana_name, parent_folder, api_key_or_token=api_key_or_token)
payload["parentUid"] = data["uid"]

response = _send_request(cmd, resource_group_name, grafana_name, "post", "/api/folders", payload,
api_key_or_token=api_key_or_token, subscription=subscription)
return json.loads(response.content)


def list_folders(cmd, grafana_name, resource_group_name=None, api_key_or_token=None, subscription=None):
response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/folders",
api_key_or_token=api_key_or_token, subscription=subscription)
return json.loads(response.content)
endpoint, headers = _get_grafana_request_context(cmd, resource_group_name, grafana_name, subscription,
api_key_or_token=api_key_or_token)

status, content = search_folders(
grafana_url=endpoint,
http_get_headers=headers
)

if status >= 400:
raise ArgumentUsageError(f"Failed to find folders. Error: {content}")

return content


def update_folder(cmd, grafana_name, folder, title, resource_group_name=None, api_key_or_token=None):
Expand All @@ -575,22 +589,19 @@ def delete_folder(cmd, grafana_name, folder, resource_group_name=None, api_key_o


def _find_folder(cmd, resource_group_name, grafana_name, folder, api_key_or_token=None):
response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/folders/" + folder,
raise_for_error_status=False, api_key_or_token=api_key_or_token)
if response.status_code >= 400:
response = _send_request(cmd, resource_group_name, grafana_name, "get", "/api/folders",
api_key_or_token=api_key_or_token)
if response.status_code >= 400:
raise ArgumentUsageError(f"Couldn't find the folder '{folder}'. Ex: {response.status_code}")
result = json.loads(response.content)
result = [f for f in result if f["title"] == folder]
if len(result) == 0:
raise ArgumentUsageError(f"Couldn't find the folder '{folder}'. Ex: {response.status_code}")
if len(result) > 1:
raise ArgumentUsageError((f"More than one folder has the same title of '{folder}'. Please use other "
f"unique identifiers"))
return result[0]
return json.loads(response.content)
folders = list_folders(cmd, grafana_name, resource_group_name=resource_group_name,
api_key_or_token=api_key_or_token)

# try uid first
match = next((f for f in folders if f['uid'] == folder), None)
if match:
return match

# If we get here, the folder is not found by uid, so we try by title
match = next((f for f in folders if f['title'] == folder), None)
if not match:
raise ArgumentUsageError(f"Couldn't find the folder '{folder}'.")
return match


def list_api_keys(cmd, grafana_name, resource_group_name=None):
Expand Down Expand Up @@ -914,14 +925,8 @@ def _get_grafana_endpoint(cmd, resource_group_name, grafana_name, subscription):

def _send_request(cmd, resource_group_name, grafana_name, http_method, path, body=None, raise_for_error_status=True,
api_key_or_token=None, subscription=None):
endpoint = _get_grafana_endpoint(cmd, resource_group_name, grafana_name, subscription)

creds = _get_data_plane_creds(cmd, api_key_or_token, subscription)

headers = {
"content-type": "application/json",
"authorization": "Bearer " + creds[1]
}
endpoint, headers = _get_grafana_request_context(cmd, resource_group_name, grafana_name, subscription,
api_key_or_token=api_key_or_token)

# TODO: handle re-try on 429
response = requests.request(http_method, url=endpoint + path, headers=headers, json=body, timeout=60,
Expand All @@ -932,3 +937,14 @@ def _send_request(cmd, resource_group_name, grafana_name, http_method, path, bod
response.raise_for_status()
# TODO: log headers, requests and response
return response


def _get_grafana_request_context(cmd, resource_group_name, grafana_name, subscription, api_key_or_token=None):
endpoint = _get_grafana_endpoint(cmd, resource_group_name, grafana_name, subscription)
creds = _get_data_plane_creds(cmd, api_key_or_token, subscription)
headers = {
"content-type": "application/json",
"authorization": "Bearer " + creds[1]
}

return endpoint, headers
Loading
Loading