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

feat: capture arccontainerstorage in support bundle #351

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9f414e0
add esa for support bundle
Elsie4ever Sep 12, 2024
e632bec
merge latest
Elsie4ever Sep 12, 2024
d125725
fix
Elsie4ever Sep 12, 2024
badce8c
fix2
Elsie4ever Sep 12, 2024
bf4cef2
fix3
Elsie4ever Sep 12, 2024
bcf5248
fix4
Elsie4ever Sep 12, 2024
b0adf03
fix5
Elsie4ever Sep 12, 2024
86e1e80
fix6
Elsie4ever Sep 12, 2024
064920e
fix7
Elsie4ever Sep 12, 2024
40e8600
fix8
Elsie4ever Sep 12, 2024
675c093
fix9
Elsie4ever Sep 12, 2024
2d0bc8e
fix10
Elsie4ever Sep 12, 2024
ec72d6f
fix10
Elsie4ever Sep 12, 2024
4eb168d
fix11
Elsie4ever Sep 12, 2024
77863de
fix12
Elsie4ever Sep 12, 2024
4914293
fix13
Elsie4ever Sep 12, 2024
765985e
fix14
Elsie4ever Sep 12, 2024
ed58d95
update
Elsie4ever Sep 12, 2024
2b0b1ce
update2
Elsie4ever Sep 12, 2024
6f70ecc
add crd check
Elsie4ever Sep 12, 2024
d692909
name update for int
Elsie4ever Sep 12, 2024
269228d
name update for int2
Elsie4ever Sep 12, 2024
697bd2a
name update for int3
Elsie4ever Sep 12, 2024
ed3ee99
name update for int4
Elsie4ever Sep 12, 2024
436feb4
name update for int5
Elsie4ever Sep 12, 2024
0cd3240
update help
Elsie4ever Sep 12, 2024
a4963bd
lint
Elsie4ever Sep 12, 2024
8ae4e8d
update
Elsie4ever Sep 12, 2024
4a44a57
address comment
Elsie4ever Sep 12, 2024
c7d2a2c
Merge branch 'feature/0.7.0' into user/jiacju/esa_support_bundle
Elsie4ever Sep 12, 2024
a5c0355
Merge branch 'feature/0.7.0' into user/jiacju/esa_support_bundle
Elsie4ever Sep 12, 2024
11a7985
Merge branch 'user/jiacju/esa_support_bundle' of https://github.com/E…
Elsie4ever Sep 12, 2024
f7a105e
Merge branch 'feature/0.7.0' into user/jiacju/esa_support_bundle
Elsie4ever Sep 13, 2024
bce8766
address comment
Elsie4ever Sep 13, 2024
f248929
lint
Elsie4ever Sep 13, 2024
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
6 changes: 6 additions & 0 deletions azext_edge/edge/_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .providers.edge_api import MQ_ACTIVE_API
from .providers.support_bundle import (
COMPAT_AKRI_APIS,
COMPAT_ARCCONTAINERSTORAGE_APIS,
COMPAT_CLUSTER_CONFIG_APIS,
COMPAT_DEVICEREGISTRY_APIS,
COMPAT_MQTT_BROKER_APIS,
Expand Down Expand Up @@ -58,6 +59,7 @@ def load_iotops_help():
- {COMPAT_DEVICEREGISTRY_APIS.as_str()}
- {COMPAT_CLUSTER_CONFIG_APIS.as_str()}
- {COMPAT_DATAFLOW_APIS.as_str()}
- {COMPAT_ARCCONTAINERSTORAGE_APIS.as_str()}

Note: logs from evicted pod will not be captured, as they are inaccessible. For details
on why a pod was evicted, please refer to the related pod and node files.
Expand All @@ -79,6 +81,10 @@ def load_iotops_help():
- name: Include mqtt broker traces in the support bundle. This is an alias for stats trace fetch capability.
text: >
az iot ops support create-bundle --ops-service broker --broker-traces

- name: Include arc container storage resources in the support bundle.
text: >
az iot ops support create-bundle --ops-service acs
"""

helps[
Expand Down
1 change: 1 addition & 0 deletions azext_edge/edge/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ class OpsServiceType(ListableEnum):
billing = "billing"
dataflow = "dataflow"
schemaregistry = "schemaregistry"
arccontainerstorage = "acs"

@classmethod
def list_check_services(cls):
Expand Down
2 changes: 2 additions & 0 deletions azext_edge/edge/providers/edge_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
from .deviceregistry import DEVICEREGISTRY_API_V1, DeviceRegistryResourceKinds
from .dataflow import DATAFLOW_API_V1B1, DataflowResourceKinds
from .meta import META_API_V1B1, MetaResourceKinds
from .arccontainerstorage import ARCCONTAINERSTORAGE_API_V1

__all__ = [
"ARCCONTAINERSTORAGE_API_V1",
"CLUSTER_CONFIG_API_V1",
"EdgeResourceApi",
"EdgeApiManager",
Expand Down
12 changes: 12 additions & 0 deletions azext_edge/edge/providers/edge_api/arccontainerstorage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# coding=utf-8
# ----------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License file in the project root for license information.
# ----------------------------------------------------------------------------------------------

from .base import EdgeResourceApi


ARCCONTAINERSTORAGE_API_V1 = EdgeResourceApi(
group="arccontainerstorage.azure.net", version="v1", moniker="arccontainerstorage"
)
95 changes: 95 additions & 0 deletions azext_edge/edge/providers/support/arccontainerstorage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# coding=utf-8
# ----------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License file in the project root for license information.
# ----------------------------------------------------------------------------------------------

from functools import partial
from typing import Iterable, Optional

from knack.log import get_logger

from ..edge_api import ARCCONTAINERSTORAGE_API_V1, EdgeResourceApi
from .base import (
DAY_IN_SECONDS,
assemble_crd_work,
process_daemonsets,
process_deployments,
process_persistent_volume_claims,
process_replicasets,
process_services,
process_v1_pods,
)

logger = get_logger(__name__)

STORAGE_DIRECTORY_PATH = ARCCONTAINERSTORAGE_API_V1.moniker
# TODO: Use common label once it is ready
STORAGE_NAMESPACE = "azure-arc-containerstorage"


def fetch_deployments():
return process_deployments(
directory_path=STORAGE_DIRECTORY_PATH,
namespace=STORAGE_NAMESPACE,
)


def fetch_replicasets():
return process_replicasets(
directory_path=STORAGE_DIRECTORY_PATH,
namespace=STORAGE_NAMESPACE,
)


def fetch_pods(since_seconds: int = DAY_IN_SECONDS):
return process_v1_pods(
directory_path=STORAGE_DIRECTORY_PATH,
namespace=STORAGE_NAMESPACE,
since_seconds=since_seconds,
)


def fetch_daemonsets():
return process_daemonsets(
directory_path=STORAGE_DIRECTORY_PATH,
namespace=STORAGE_NAMESPACE,
)


def fetch_services():
return process_services(
directory_path=STORAGE_DIRECTORY_PATH,
namespace=STORAGE_NAMESPACE,
)


def fetch_peristent_volume_claims():
return process_persistent_volume_claims(
directory_path=STORAGE_DIRECTORY_PATH,
namespace=STORAGE_NAMESPACE,
)


support_runtime_elements = {
"daemonsets": fetch_daemonsets,
"deployments": fetch_deployments,
"persistentvolumeclaims": fetch_peristent_volume_claims,
"replicasets": fetch_replicasets,
"services": fetch_services,
}


def prepare_bundle(
log_age_seconds: int = DAY_IN_SECONDS,
apis: Optional[Iterable[EdgeResourceApi]] = None,
) -> dict:
acs_to_run = {}

if apis:
acs_to_run.update(assemble_crd_work(apis=apis, namespace=STORAGE_NAMESPACE))

support_runtime_elements["pods"] = partial(fetch_pods, since_seconds=log_age_seconds)
acs_to_run.update(support_runtime_elements)

return acs_to_run
85 changes: 61 additions & 24 deletions azext_edge/edge/providers/support/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def process_crd(
plural: str,
directory_path: str,
file_prefix: Optional[str] = None,
namespace: Optional[str] = None,
) -> List[dict]:
result: dict = get_custom_objects(
group=group,
Expand All @@ -70,7 +71,8 @@ def process_crd(
processed = []
namespaces = []
for r in result.get("items", []):
namespace = r["metadata"]["namespace"]
if not namespace:
namespace = r["metadata"]["namespace"]
namespaces.append(namespace)
name = r["metadata"]["name"]
processed.append(
Expand All @@ -92,6 +94,7 @@ def process_v1_pods(
prefix_names: Optional[List[str]] = None,
pod_prefix_for_init_container_logs: Optional[List[str]] = None,
exclude_prefixes: Optional[List[str]] = None,
namespace: Optional[str] = None,
) -> List[dict]:
from kubernetes.client.models import V1Pod

Expand All @@ -102,7 +105,10 @@ def process_v1_pods(
if not prefix_names:
prefix_names = []

pods: V1PodList = v1_api.list_pod_for_all_namespaces(label_selector=label_selector)
if namespace:
pods: V1PodList = v1_api.list_namespaced_pod(namespace=namespace, label_selector=label_selector)
else:
pods: V1PodList = v1_api.list_pod_for_all_namespaces(label_selector=label_selector)

if exclude_prefixes:
pods = exclude_resources_with_prefix(pods, exclude_prefixes)
Expand Down Expand Up @@ -186,11 +192,18 @@ def process_deployments(
label_selector: Optional[str] = None,
prefix_names: Optional[List[str]] = None,
exclude_prefixes: Optional[List[str]] = None,
namespace: Optional[str] = None,
) -> List[dict]:
v1_apps = client.AppsV1Api()
deployments: V1DeploymentList = v1_apps.list_deployment_for_all_namespaces(
label_selector=label_selector, field_selector=field_selector
)

if namespace:
deployments: V1DeploymentList = v1_apps.list_namespaced_deployment(
namespace=namespace, label_selector=label_selector, field_selector=field_selector
)
else:
deployments: V1DeploymentList = v1_apps.list_deployment_for_all_namespaces(
label_selector=label_selector, field_selector=field_selector
)

return _process_kubernetes_resources(
directory_path=directory_path,
Expand Down Expand Up @@ -237,11 +250,18 @@ def process_services(
label_selector: Optional[str] = None,
prefix_names: Optional[List[str]] = None,
exclude_prefixes: Optional[List[str]] = None,
namespace: Optional[str] = None,
) -> List[dict]:
v1_api = client.CoreV1Api()
services: V1ServiceList = v1_api.list_service_for_all_namespaces(
label_selector=label_selector, field_selector=field_selector
)

if namespace:
services: V1ServiceList = v1_api.list_namespaced_service(
namespace=namespace, label_selector=label_selector, field_selector=field_selector
)
else:
services: V1ServiceList = v1_api.list_service_for_all_namespaces(
label_selector=label_selector, field_selector=field_selector
)

return _process_kubernetes_resources(
directory_path=directory_path,
Expand All @@ -257,9 +277,16 @@ def process_replicasets(
label_selector: Optional[str] = None,
prefix_names: Optional[List[str]] = None,
exclude_prefixes: Optional[List[str]] = None,
namespace: Optional[str] = None,
) -> List[dict]:
v1_apps = client.AppsV1Api()
replicasets: V1ReplicaSetList = v1_apps.list_replica_set_for_all_namespaces(label_selector=label_selector)

if namespace:
replicasets: V1ReplicaSetList = v1_apps.list_namespaced_replica_set(
namespace=namespace, label_selector=label_selector
)
else:
replicasets: V1ReplicaSetList = v1_apps.list_replica_set_for_all_namespaces(label_selector=label_selector)

return _process_kubernetes_resources(
directory_path=directory_path,
Expand All @@ -275,11 +302,18 @@ def process_daemonsets(
field_selector: Optional[str] = None,
label_selector: Optional[str] = None,
prefix_names: Optional[List[str]] = None,
namespace: Optional[str] = None,
) -> List[dict]:
v1_apps = client.AppsV1Api()
daemonsets: V1DaemonSetList = v1_apps.list_daemon_set_for_all_namespaces(
label_selector=label_selector, field_selector=field_selector
)

if namespace:
daemonsets: V1DaemonSetList = v1_apps.list_namespaced_daemon_set(
namespace=namespace, label_selector=label_selector, field_selector=field_selector
)
else:
daemonsets: V1DaemonSetList = v1_apps.list_daemon_set_for_all_namespaces(
label_selector=label_selector, field_selector=field_selector
)

return _process_kubernetes_resources(
directory_path=directory_path,
Expand Down Expand Up @@ -359,11 +393,18 @@ def process_persistent_volume_claims(
field_selector: Optional[str] = None,
label_selector: Optional[str] = None,
prefix_names: Optional[List[str]] = None,
namespace: Optional[str] = None,
) -> List[dict]:
v1_api = client.CoreV1Api()
pvcs: V1PersistentVolumeClaimList = v1_api.list_persistent_volume_claim_for_all_namespaces(
label_selector=label_selector, field_selector=field_selector
)

if namespace:
pvcs: V1PersistentVolumeClaimList = v1_api.list_namespaced_persistent_volume_claim(
namespace=namespace, label_selector=label_selector, field_selector=field_selector
)
else:
pvcs: V1PersistentVolumeClaimList = v1_api.list_persistent_volume_claim_for_all_namespaces(
label_selector=label_selector, field_selector=field_selector
)

return _process_kubernetes_resources(
directory_path=directory_path,
Expand Down Expand Up @@ -417,6 +458,7 @@ def assemble_crd_work(
apis: Iterable[EdgeResourceApi],
file_prefix_map: Optional[Dict[str, str]] = None,
directory_path: Optional[str] = None,
namespace: Optional[str] = None,
) -> dict:
if not file_prefix_map:
file_prefix_map = {}
Expand All @@ -435,6 +477,7 @@ def assemble_crd_work(
plural=api._kinds[kind], # TODO: optimize
directory_path=directory_path,
file_prefix=file_prefix,
namespace=namespace,
)

return result
Expand Down Expand Up @@ -473,9 +516,7 @@ def _capture_pod_container_logs(
for capture_previous in capture_previous_log_runs:
try:
logger_debug_previous = "previous run " if capture_previous else ""
logger.debug(
f"Reading {logger_debug_previous}log from pod {pod_name} container {container.name}"
)
logger.debug(f"Reading {logger_debug_previous}log from pod {pod_name} container {container.name}")
log: str = v1_api.read_namespaced_pod_log(
name=pod_name,
namespace=pod_namespace,
Expand Down Expand Up @@ -542,12 +583,8 @@ def _process_kubernetes_resources(
return processed


def exclude_resources_with_prefix(
resources: K8sRuntimeResources, exclude_prefixes: List[str]
) -> K8sRuntimeResources:
def exclude_resources_with_prefix(resources: K8sRuntimeResources, exclude_prefixes: List[str]) -> K8sRuntimeResources:
for prefix in exclude_prefixes:
resources.items = [
resource for resource in resources.items if not resource.metadata.name.startswith(prefix)
]
resources.items = [resource for resource in resources.items if not resource.metadata.name.startswith(prefix)]

return resources
7 changes: 7 additions & 0 deletions azext_edge/edge/providers/support_bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
DEVICEREGISTRY_API_V1,
DATAFLOW_API_V1B1,
META_API_V1B1,
ARCCONTAINERSTORAGE_API_V1,
EdgeApiManager,
)

Expand All @@ -36,6 +37,7 @@
COMPAT_DEVICEREGISTRY_APIS = EdgeApiManager(resource_apis=[DEVICEREGISTRY_API_V1])
COMPAT_DATAFLOW_APIS = EdgeApiManager(resource_apis=[DATAFLOW_API_V1B1])
COMPAT_META_APIS = EdgeApiManager(resource_apis=[META_API_V1B1])
COMPAT_ARCCONTAINERSTORAGE_APIS = EdgeApiManager(resource_apis=[ARCCONTAINERSTORAGE_API_V1])


def build_bundle(
Expand All @@ -59,6 +61,7 @@ def build_bundle(
from .support.arcagents import prepare_bundle as prepare_arcagents_bundle
from .support.meta import prepare_bundle as prepare_meta_bundle
from .support.schemaregistry import prepare_bundle as prepare_schema_registry_bundle
from .support.arccontainerstorage import prepare_bundle as prepare_arccontainerstorage_bundle

def collect_default_works(
pending_work: dict,
Expand Down Expand Up @@ -104,6 +107,10 @@ def collect_default_works(
"apis": None,
"prepare_bundle": prepare_schema_registry_bundle,
},
OpsServiceType.arccontainerstorage.value: {
"apis": COMPAT_ARCCONTAINERSTORAGE_APIS,
"prepare_bundle": prepare_arccontainerstorage_bundle,
},
}

for service_moniker, api_info in api_map.items():
Expand Down
Loading