Skip to content

Commit 62a867e

Browse files
authored
[lib][feat] Improve model check (#2193)
1 parent d7fcb84 commit 62a867e

File tree

4 files changed

+56
-16
lines changed

4 files changed

+56
-16
lines changed

fixlib/fixlib/baseresources.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ class BaseInstanceType(BaseType):
828828
kind: ClassVar[str] = "instance_type"
829829
kind_display: ClassVar[str] = "Instance Type"
830830
kind_description: ClassVar[str] = "An instance type."
831-
metadata: ClassVar[Dict[str, Any]] = {"icon": "instance_type", "group": "compute"}
831+
metadata: ClassVar[Dict[str, Any]] = {"icon": "type", "group": "compute"}
832832
instance_type: Optional[str] = None
833833
instance_cores: float = 0.0
834834
instance_memory: float = 0.0
@@ -926,7 +926,7 @@ class BaseVolumeType(BaseType):
926926
kind: ClassVar[str] = "volume_type"
927927
kind_display: ClassVar[str] = "Volume Type"
928928
kind_description: ClassVar[str] = "A volume type."
929-
metadata: ClassVar[Dict[str, Any]] = {"icon": "volume_type", "group": "storage"}
929+
metadata: ClassVar[Dict[str, Any]] = {"icon": "type", "group": "storage"}
930930
volume_type: Optional[str] = None
931931
ondemand_cost: Optional[float] = None
932932

@@ -1049,7 +1049,7 @@ class BaseKeyPair(BaseResource):
10491049
kind: ClassVar[str] = "keypair"
10501050
kind_display: ClassVar[str] = "Key Pair"
10511051
kind_description: ClassVar[str] = "A key pair."
1052-
metadata: ClassVar[Dict[str, Any]] = {"icon": "keypair", "group": "access_control"}
1052+
metadata: ClassVar[Dict[str, Any]] = {"icon": "key", "group": "access_control"}
10531053
fingerprint: str = ""
10541054
_categories: ClassVar[List[Category]] = [Category.iam]
10551055

@@ -1166,7 +1166,7 @@ class BaseTunnel(BaseResource):
11661166
kind: ClassVar[str] = "tunnel"
11671167
kind_display: ClassVar[str] = "Networking Tunnel"
11681168
kind_description: ClassVar[str] = "A networking tunnel."
1169-
metadata: ClassVar[Dict[str, Any]] = {"icon": "tunnel", "group": "networking"}
1169+
metadata: ClassVar[Dict[str, Any]] = {"icon": "link", "group": "networking"}
11701170
_categories: ClassVar[List[Category]] = [Category.networking]
11711171

11721172

@@ -1201,7 +1201,7 @@ class BaseRoute(BaseResource):
12011201
kind: ClassVar[str] = "route"
12021202
kind_display: ClassVar[str] = "Network Route"
12031203
kind_description: ClassVar[str] = "A network route."
1204-
metadata: ClassVar[Dict[str, Any]] = {"icon": "route", "group": "networking"}
1204+
metadata: ClassVar[Dict[str, Any]] = {"icon": "routing_table", "group": "networking"}
12051205
_categories: ClassVar[List[Category]] = [Category.networking]
12061206

12071207

@@ -1210,7 +1210,7 @@ class BaseNetworkAcl(BaseResource):
12101210
kind: ClassVar[str] = "network_acl"
12111211
kind_display: ClassVar[str] = "Network ACL"
12121212
kind_description: ClassVar[str] = "A network access control list."
1213-
metadata: ClassVar[Dict[str, Any]] = {"icon": "acl", "group": "networking"}
1213+
metadata: ClassVar[Dict[str, Any]] = {"icon": "access_control", "group": "networking"}
12141214
_categories: ClassVar[List[Category]] = [Category.networking, Category.security]
12151215

12161216

@@ -1237,7 +1237,7 @@ class BaseNetworkInterface(BaseResource):
12371237
kind: ClassVar[str] = "network_interface"
12381238
kind_display: ClassVar[str] = "Network Interface"
12391239
kind_description: ClassVar[str] = "A network interface."
1240-
metadata: ClassVar[Dict[str, Any]] = {"icon": "network_interface", "group": "networking"}
1240+
metadata: ClassVar[Dict[str, Any]] = {"icon": "endpoint", "group": "networking"}
12411241
_categories: ClassVar[List[Category]] = [Category.networking]
12421242
network_interface_status: str = ""
12431243
network_interface_type: Optional[str] = None
@@ -1255,7 +1255,7 @@ class BaseIamPrincipal(BaseResource):
12551255
kind_description: ClassVar[str] = (
12561256
"An IAM principal is an entity that can be authenticated and authorized to access resources."
12571257
)
1258-
metadata: ClassVar[Dict[str, Any]] = {"icon": "iam_principal", "group": "access_control"}
1258+
metadata: ClassVar[Dict[str, Any]] = {"icon": "user", "group": "access_control"}
12591259
_categories: ClassVar[List[Category]] = [Category.iam]
12601260

12611261

@@ -1300,7 +1300,7 @@ class BaseInstanceProfile(BaseResource):
13001300
kind: ClassVar[str] = "instance_profile"
13011301
kind_display: ClassVar[str] = "Instance Profile"
13021302
kind_description: ClassVar[str] = "An instance profile."
1303-
metadata: ClassVar[Dict[str, Any]] = {"icon": "instance_profile", "group": "access_control"}
1303+
metadata: ClassVar[Dict[str, Any]] = {"icon": "profile", "group": "access_control"}
13041304
_categories: ClassVar[List[Category]] = [Category.iam]
13051305

13061306

@@ -1373,7 +1373,7 @@ class BaseHealthCheck(BaseResource):
13731373
kind: ClassVar[str] = "health_check"
13741374
kind_display: ClassVar[str] = "Health Check"
13751375
kind_description: ClassVar[str] = "A health check."
1376-
metadata: ClassVar[Dict[str, Any]] = {"icon": "health_check", "group": "compute"}
1376+
metadata: ClassVar[Dict[str, Any]] = {"icon": "health", "group": "compute"}
13771377
_categories: ClassVar[List[Category]] = [Category.monitoring]
13781378
check_interval: Optional[int] = None
13791379
healthy_threshold: Optional[int] = None

fixlib/fixlib/core/model_check.py

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,32 @@
99
from fixlib.json import from_json
1010
from fixlib.types import Json
1111

12+
# fmt: off
13+
14+
# Possible group value: any addition needs to be supported in the frontend
15+
ResourceGroups: Set[str] = {
16+
"access_control", "compute", "control", "database", "generative_ai",
17+
"group", "managed_kubernetes", "misc", "networking", "storage"
18+
}
19+
# Possible icon value: any addition needs to be supported in the frontend
20+
ResourceIcons: Set[str] = {
21+
"access_control", "account", "alarm", "application", "autoscaling_group", "backup",
22+
"bucket", "cdn", "certificate", "cloud", "cluster", "config", "connection", "container",
23+
"database", "dns", "dns_record", "endpoint", "environment", "firewall", "function",
24+
"gateway", "graph_root", "group", "health", "host", "image", "instance", "job", "key",
25+
"link", "load_balancer", "lock", "log", "network", "network_address", "network_share",
26+
"plan", "policy", "profile", "provider", "proxy", "queue", "quota", "region",
27+
"repository", "resource", "role", "routing_table", "security_group", "service",
28+
"snapshot", "stack", "subnet", "type", "user", "vault", "version", "volume", "zone",
29+
}
30+
# Define classes that should be ignored for the resource check
31+
IgnoredClasses: Set[str] = {
32+
"aws_resource", "aws_account", "aws_region",
33+
"microsoft_resource",
34+
"gcp_resource", "gcp_region", "gcp_project", "gcp_zone", "gcp_region_quota"
35+
}
36+
# fmt: on
37+
1238

1339
@define
1440
class CheckProp:
@@ -111,11 +137,19 @@ def check_not_redeclared(root: CheckClass, clazz: CheckClass, props: Set[str]) -
111137

112138

113139
def check_model_class(clazz: Type[BaseResource]) -> None:
114-
if clazz.__name__.startswith("Aws"): # Currently only AWS resources provide this information
115-
if "kind_display" not in vars(clazz):
116-
raise AttributeError(f"Class {clazz.__name__} does not have a kind_display attribute")
117-
if "kind_description" not in vars(clazz):
118-
raise AttributeError(f"Class {clazz.__name__} does not have a kind_description attribute")
140+
name = clazz.__name__
141+
kind = clazz.kind
142+
if any(kind.startswith(a) for a in ["aws", "gcp", "azure", "microsoft"]): # only selected providers support this
143+
props = vars(clazz)
144+
if "kind_display" not in props:
145+
raise AttributeError(f"Class {name} does not have a kind_display attribute")
146+
assert clazz.kind_service is not None, f"Class {name} does not have a kind_service attribute"
147+
148+
# make sure metadata is valid
149+
if icon := clazz.metadata.get("icon"):
150+
assert icon in ResourceIcons, f"{name} has unknown icon {icon}"
151+
if group := clazz.metadata.get("group"):
152+
assert group in ResourceGroups, f"{name} has unknown group {group}"
119153

120154

121155
def load_plugin_classes(*base: Type[BaseResource]) -> Set[Type[BaseResource]]:
@@ -172,7 +206,8 @@ def check_overlap(*base: Type[BaseResource]) -> None:
172206

173207
if issubclass(model, BaseResource) and not getattr(model, "__abstractmethods__"):
174208
# check that the model class is not abstract and has no abstract methods
175-
check_model_class(model)
209+
if model.kind not in IgnoredClasses:
210+
check_model_class(model)
176211

177212
check_overlap_for(resource_classes_to_fixcore_model(model_classes, aggregate_root=BaseResource))
178213

plugins/azure/fix_plugin_azure/resource/base.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ def parse_json(
8282
@define(eq=False, slots=False)
8383
class MicrosoftResource(BaseResource):
8484
kind: ClassVar[str] = "microsoft_resource"
85+
kind_display: ClassVar[str] = "Microsoft Resource"
8586
# The mapping to transform the incoming API json into the internal representation.
8687
mapping: ClassVar[Dict[str, Bender]] = {}
8788
# Which API to call and what to expect in the result.

plugins/azure/fix_plugin_azure/resource/microsoft_graph.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
@define(eq=False, slots=False)
2121
class MicrosoftGraphEntity(MicrosoftResource):
2222
kind: ClassVar[str] = "microsoft_graph_entity"
23+
kind_display: ClassVar[str] = "Microsoft Graph Entity"
24+
kind_service: ClassVar[Optional[str]] = "graph"
2325
mapping: ClassVar[Dict[str, Bender]] = {
2426
"id": S("id"),
2527
"deleted_date_time": S("deletedDateTime"),
@@ -1047,6 +1049,7 @@ def add_transitive(gp: MicrosoftGraphGroup) -> None:
10471049
class MicrosoftGraphOrganization(MicrosoftGraphEntity, BaseAccount):
10481050
kind: ClassVar[str] = "microsoft_graph_organization"
10491051
kind_service: ClassVar[Optional[str]] = "entra_id"
1052+
kind_display: ClassVar[str] = "Microsoft Graph Organization"
10501053
api_spec: ClassVar[MicrosoftRestSpec] = RestApiSpec(
10511054
"graph",
10521055
"https://graph.microsoft.com/v1.0/organization",
@@ -1131,6 +1134,7 @@ def deferred_edge_to_subscription(cls, builder: GraphBuilder) -> None:
11311134
class MicrosoftGraphOrganizationRoot(MicrosoftGraphEntity, BaseRegion):
11321135
kind: ClassVar[str] = "microsoft_graph_organization_root"
11331136
kind_service: ClassVar[Optional[str]] = "entra_id"
1137+
kind_display: ClassVar[str] = "Microsoft Graph Organization Root"
11341138

11351139

11361140
@define(eq=False, slots=False)

0 commit comments

Comments
 (0)