Skip to content

Commit f692309

Browse files
authored
[azure][feat]: Update configs collection for mysql and postgresql (#2157)
1 parent 3410e81 commit f692309

File tree

6 files changed

+244
-174
lines changed

6 files changed

+244
-174
lines changed

plugins/azure/fix_plugin_azure/resource/mysql.py

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
AzureSystemData,
1414
)
1515
from fix_plugin_azure.resource.microsoft_graph import MicrosoftGraphServicePrincipal, MicrosoftGraphUser
16+
from fix_plugin_azure.utils import from_str_to_typed
1617
from fixlib.baseresources import (
1718
BaseDatabase,
1819
BaseDatabaseInstanceType,
@@ -21,7 +22,7 @@
2122
ModelReference,
2223
)
2324
from fixlib.graph import BySearchCriteria
24-
from fixlib.json_bender import K, AsBool, Bender, S, ForallBend, Bend, MapEnum, MapValue
25+
from fixlib.json_bender import K, Bender, S, ForallBend, Bend, MapEnum, MapValue
2526
from fixlib.types import Json
2627

2728
service_name = "azure_mysql"
@@ -318,39 +319,34 @@ def collect(
318319
class AzureMysqlServerConfiguration(MicrosoftResource):
319320
kind: ClassVar[str] = "azure_mysql_server_configuration"
320321
# Collect via AzureMysqlServer()
321-
mapping: ClassVar[Dict[str, Bender]] = {
322-
"id": S("id"),
323-
"tags": S("tags", default={}),
324-
"name": S("name"),
325-
"system_data": S("systemData") >> Bend(AzureSystemData.mapping),
326-
"type": S("type"),
327-
"ctime": S("systemData", "createdAt"),
328-
"mtime": S("systemData", "lastModifiedAt"),
329-
"allowed_values": S("properties", "allowedValues"),
330-
"current_value": S("properties", "currentValue"),
331-
"data_type": S("properties", "dataType"),
332-
"default_value": S("properties", "defaultValue"),
333-
"description": S("properties", "description"),
334-
"documentation_link": S("properties", "documentationLink"),
335-
"is_config_pending_restart": S("properties", "isConfigPendingRestart") >> AsBool(),
336-
"is_dynamic_config": S("properties", "isDynamicConfig") >> AsBool(),
337-
"is_read_only": S("properties", "isReadOnly") >> AsBool(),
338-
"source": S("properties", "source"),
339-
"value": S("properties", "value"),
340-
}
341-
allowed_values: Optional[str] = field(default=None, metadata={'description': 'Allowed values of the configuration.'}) # fmt: skip
342-
current_value: Optional[str] = field(default=None, metadata={"description": "Current value of the configuration."})
343-
data_type: Optional[str] = field(default=None, metadata={"description": "Data type of the configuration."})
344-
default_value: Optional[str] = field(default=None, metadata={"description": "Default value of the configuration."})
345-
description: Optional[str] = field(default=None, metadata={"description": "Description of the configuration."})
346-
documentation_link: Optional[str] = field(default=None, metadata={'description': 'The link used to get the document from community or Azure site.'}) # fmt: skip
347-
is_config_pending_restart: Optional[bool] = field(default=None, metadata={'description': 'If is the configuration pending restart or not.'}) # fmt: skip
348-
is_dynamic_config: Optional[bool] = field(default=None, metadata={'description': 'If is the configuration dynamic.'}) # fmt: skip
349-
is_read_only: Optional[bool] = field(default=None, metadata={"description": "If is the configuration read only."})
350-
source: Optional[str] = field(default=None, metadata={"description": "Source of the configuration."})
351-
value: Optional[str] = field(default=None, metadata={"description": "Value of the configuration."})
352-
system_data: Optional[AzureSystemData] = field(default=None, metadata={'description': 'Metadata pertaining to creation and last modification of the resource.'}) # fmt: skip
353-
type: Optional[str] = field(default=None, metadata={'description': 'The type of the resource. E.g. Microsoft.Compute/virtualMachines or Microsoft.Storage/storageAccounts '}) # fmt: skip
322+
config: Json = field(factory=dict)
323+
324+
@classmethod
325+
def collect_configs(
326+
cls,
327+
server_id: str,
328+
raw: List[Json],
329+
builder: GraphBuilder,
330+
) -> List["AzureMysqlServerConfiguration"]:
331+
if not raw:
332+
return []
333+
configuration_instance = AzureMysqlServerConfiguration(id=server_id)
334+
for js in raw:
335+
properties = js.get("properties")
336+
if not properties:
337+
continue
338+
if (
339+
(data_type := properties.get("dataType"))
340+
and (val := properties.get("currentValue") or properties.get("value"))
341+
and (config_name := js.get("name"))
342+
):
343+
value = from_str_to_typed(data_type, val)
344+
if not value:
345+
continue
346+
configuration_instance.config[config_name] = value
347+
if (added := builder.add_node(configuration_instance, configuration_instance.config)) is not None:
348+
return [added]
349+
return []
354350

355351

356352
@define(eq=False, slots=False)
@@ -667,14 +663,12 @@ def _collect_items(
667663
items = graph_builder.client.list(api_spec)
668664
if not items:
669665
return
670-
collected = class_instance.collect(items, graph_builder)
671-
for clazz in collected:
672-
graph_builder.add_edge(
673-
self,
674-
edge_type=EdgeType.default,
675-
id=clazz.id,
676-
clazz=class_instance,
677-
)
666+
if issubclass(AzureMysqlServerConfiguration, class_instance): # type: ignore
667+
collected = class_instance.collect_configs(self.id, items, graph_builder) # type: ignore
668+
else:
669+
collected = class_instance.collect(items, graph_builder)
670+
for resource in collected:
671+
graph_builder.add_edge(self, node=resource)
678672

679673
def post_process(self, graph_builder: GraphBuilder, source: Json) -> None:
680674
if server_id := self.id:

plugins/azure/fix_plugin_azure/resource/postgresql.py

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
AzureServerMaintenanceWindow,
2525
AzureServerNetwork,
2626
)
27+
from fix_plugin_azure.utils import from_str_to_typed
2728
from fixlib.baseresources import BaseDatabase, BaseDatabaseInstanceType, DatabaseInstanceStatus, ModelReference
2829
from fixlib.graph import BySearchCriteria
2930
from fixlib.json_bender import K, Bender, S, ForallBend, Bend, MapEnum, MapValue
@@ -313,37 +314,34 @@ def collect(cls, raw: List[Json], builder: GraphBuilder) -> List[AzurePostgresql
313314
class AzurePostgresqlServerConfiguration(MicrosoftResource, AzureProxyResource):
314315
kind: ClassVar[str] = "azure_postgresql_server_configuration"
315316
# Collect via AzurePostgresqlServer()
316-
mapping: ClassVar[Dict[str, Bender]] = AzureProxyResource.mapping | {
317-
"id": S("id"),
318-
"tags": S("tags", default={}),
319-
"name": S("name"),
320-
"ctime": S("systemData", "createdAt"),
321-
"mtime": S("systemData", "lastModifiedAt"),
322-
"allowed_values": S("properties", "allowedValues"),
323-
"data_type": S("properties", "dataType"),
324-
"default_value": S("properties", "defaultValue"),
325-
"description": S("properties", "description"),
326-
"documentation_link": S("properties", "documentationLink"),
327-
"is_config_pending_restart": S("properties", "isConfigPendingRestart"),
328-
"is_dynamic_config": S("properties", "isDynamicConfig"),
329-
"is_read_only": S("properties", "isReadOnly"),
330-
"configuration_source": S("properties", "source"),
331-
"system_data": S("systemData") >> Bend(AzureSystemData.mapping),
332-
"unit": S("properties", "unit"),
333-
"value": S("properties", "value"),
334-
}
335-
allowed_values: Optional[str] = field(default=None, metadata={'description': 'Allowed values of the configuration.'}) # fmt: skip
336-
data_type: Optional[str] = field(default=None, metadata={"description": "Data type of the configuration."})
337-
default_value: Optional[str] = field(default=None, metadata={"description": "Default value of the configuration."})
338-
description: Optional[str] = field(default=None, metadata={"description": "Description of the configuration."})
339-
documentation_link: Optional[str] = field(default=None, metadata={'description': 'Configuration documentation link.'}) # fmt: skip
340-
is_config_pending_restart: Optional[bool] = field(default=None, metadata={'description': 'Configuration is pending restart or not.'}) # fmt: skip
341-
is_dynamic_config: Optional[bool] = field(default=None, metadata={'description': 'Configuration dynamic or static.'}) # fmt: skip
342-
is_read_only: Optional[bool] = field(default=None, metadata={"description": "Configuration read-only or not."})
343-
configuration_source: Optional[str] = field(default=None, metadata={"description": "Source of the configuration."})
344-
system_data: Optional[AzureSystemData] = field(default=None, metadata={'description': 'Metadata pertaining to creation and last modification of the resource.'}) # fmt: skip
345-
unit: Optional[str] = field(default=None, metadata={"description": "Configuration unit."})
346-
value: Optional[str] = field(default=None, metadata={"description": "Value of the configuration."})
317+
config: Json = field(factory=dict)
318+
319+
@classmethod
320+
def collect_configs(
321+
cls,
322+
server_id: str,
323+
raw: List[Json],
324+
builder: GraphBuilder,
325+
) -> List[AzurePostgresqlServerConfiguration]:
326+
if not raw:
327+
return []
328+
configuration_instance = AzurePostgresqlServerConfiguration(id=server_id)
329+
for js in raw:
330+
properties = js.get("properties")
331+
if not properties:
332+
continue
333+
if (
334+
(data_type := properties.get("dataType"))
335+
and (val := properties.get("value"))
336+
and (config_name := js.get("name"))
337+
):
338+
value = from_str_to_typed(data_type, val)
339+
if not value:
340+
continue
341+
configuration_instance.config[config_name] = value
342+
if (added := builder.add_node(configuration_instance, configuration_instance.config)) is not None:
343+
return [added]
344+
return []
347345

348346

349347
@define(eq=False, slots=False)
@@ -542,7 +540,10 @@ def _collect_items(
542540
items = graph_builder.client.list(api_spec)
543541
if not items:
544542
return
545-
collected = class_instance.collect(items, graph_builder)
543+
if issubclass(AzurePostgresqlServerConfiguration, class_instance): # type: ignore
544+
collected = class_instance.collect_configs(self.id, items, graph_builder) # type: ignore
545+
else:
546+
collected = class_instance.collect(items, graph_builder)
546547
for clazz in collected:
547548
graph_builder.add_edge(self, node=clazz)
548549

plugins/azure/fix_plugin_azure/utils.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
from typing import Callable, Dict, TypeVar, Any
23
from attr import frozen
34
import functools
@@ -6,6 +7,7 @@
67

78

89
T = TypeVar("T")
10+
log = logging.getLogger("fix.plugins.azure")
911

1012

1113
def rgetattr(obj: Any, attr: str, *args: Any) -> Any:
@@ -44,6 +46,27 @@ def case_insensitive_eq(left: T, right: T) -> bool:
4446
return left == right
4547

4648

49+
def from_str_to_typed(config_type: str, value: str) -> Any:
50+
def set_bool(val: str) -> bool:
51+
if val.lower() == "on":
52+
return True
53+
return False
54+
55+
type_mapping = {
56+
"Enumeration": lambda x: set_bool(x) if x.lower() in ["on", "off"] else str(x),
57+
"Integer": int,
58+
"Numeric": float,
59+
"Set": lambda x: x.split(","),
60+
"String": str,
61+
"Boolean": set_bool,
62+
}
63+
try:
64+
return type_mapping[config_type](value) # type: ignore
65+
except Exception as e:
66+
log.warning(f"An error occured while typing value: {e}")
67+
return None
68+
69+
4770
@frozen(kw_only=True)
4871
class MetricNormalization:
4972
metric_name: MetricName

plugins/azure/test/collector_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ def test_collect(
4949
config, Cloud(id="azure"), azure_subscription, credentials, core_feedback
5050
)
5151
subscription_collector.collect()
52-
assert len(subscription_collector.graph.nodes) == 428
53-
assert len(subscription_collector.graph.edges) == 668
52+
assert len(subscription_collector.graph.nodes) == 420
53+
assert len(subscription_collector.graph.edges) == 652
5454

5555
graph_collector = MicrosoftGraphOrganizationCollector(
5656
config, Cloud(id="azure"), MicrosoftGraphOrganization(id="test", name="test"), credentials, core_feedback

0 commit comments

Comments
 (0)