Skip to content

Commit 6d11a8a

Browse files
authored
[lib][feat] Unify groups and categories (#2194)
1 parent ac8c4a0 commit 6d11a8a

30 files changed

+183
-161
lines changed

fixlib/fixlib/basecategories.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ class Networking(BaseCategory):
5757

5858

5959
@dataclass(frozen=True)
60-
class Iam(BaseCategory):
61-
name: str = "iam"
62-
description: str = "Identity & Access Management"
60+
class AccessControl(BaseCategory):
61+
name: str = "access_control"
62+
description: str = "Access Control"
6363

6464

6565
@dataclass(frozen=True)
@@ -98,16 +98,30 @@ class Dns(BaseCategory):
9898
description: str = "DNS"
9999

100100

101+
@dataclass(frozen=True)
102+
class ManagedKubernetes(BaseCategory):
103+
name: str = "managed_kubernetes"
104+
description: str = "Managed Kubernetes"
105+
106+
107+
@dataclass(frozen=True)
108+
class Misc(BaseCategory):
109+
name: str = "misc"
110+
description: str = "Miscellaneous"
111+
112+
101113
class Category(Enum):
114+
ai = Ai()
115+
analytics = Analytics()
102116
compute = Compute()
103-
storage = Storage()
104117
database = Database()
105-
security = Security()
106-
networking = Networking()
107-
iam = Iam()
108-
management = Management()
109-
monitoring = Monitoring()
110-
analytics = Analytics()
111-
ai = Ai()
112118
devops = DevOps()
113119
dns = Dns()
120+
access_control = AccessControl()
121+
managed_kubernetes = ManagedKubernetes()
122+
management = Management()
123+
misc = Misc()
124+
monitoring = Monitoring()
125+
networking = Networking()
126+
security = Security()
127+
storage = Storage()

fixlib/fixlib/baseresources.py

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -738,9 +738,12 @@ def gather_categories(class_type: Type["BaseResource"]) -> List[Category]:
738738

739739
return list(set(gather_categories(cls)))
740740

741-
@property
742-
def categories(self) -> List[str]:
743-
return [str(category.value) for category in self.get_all_categories()]
741+
@classmethod
742+
def categories(cls) -> List[str]:
743+
cs = {str(category.value) for category in cls.get_all_categories()}
744+
if group := cls.metadata.get("group"):
745+
cs.add(group)
746+
return list(cs)
744747

745748

746749
BaseResource.ctime = property(BaseResource._ctime_getter, BaseResource._ctime_setter) # type: ignore
@@ -777,7 +780,7 @@ def cleanup(self, graph: Optional[Any] = None) -> bool:
777780

778781
@define(eq=False, slots=False)
779782
class BaseQuota(PhantomBaseResource):
780-
metadata: ClassVar[Dict[str, Any]] = {"icon": "quota", "group": "control"}
783+
metadata: ClassVar[Dict[str, Any]] = {"icon": "quota", "group": "management"}
781784

782785
kind: ClassVar[str] = "quota"
783786
kind_display: ClassVar[str] = "Quota"
@@ -806,7 +809,7 @@ class BaseType(BaseQuota):
806809
kind: ClassVar[str] = "type"
807810
kind_display: ClassVar[str] = "Type"
808811
kind_description: ClassVar[str] = "A generic type."
809-
metadata: ClassVar[Dict[str, Any]] = {"icon": "type", "group": "control"}
812+
metadata: ClassVar[Dict[str, Any]] = {"icon": "type", "group": "management"}
810813

811814

812815
@define(eq=False, slots=False)
@@ -850,7 +853,7 @@ class BaseCloud(PhantomBaseResource):
850853
kind: ClassVar[str] = "base_cloud"
851854
kind_display: ClassVar[str] = "Cloud"
852855
kind_description: ClassVar[str] = "A cloud."
853-
metadata: ClassVar[Dict[str, Any]] = {"icon": "cloud", "group": "control"}
856+
metadata: ClassVar[Dict[str, Any]] = {"icon": "cloud", "group": "management"}
854857

855858
def cloud(self, graph: Optional[Any] = None) -> BaseCloud:
856859
return self
@@ -861,7 +864,7 @@ class BaseAccount(BaseResource):
861864
kind: ClassVar[str] = "account"
862865
kind_display: ClassVar[str] = "Account"
863866
kind_description: ClassVar[str] = "An account."
864-
metadata: ClassVar[Dict[str, Any]] = {"icon": "account", "group": "control"}
867+
metadata: ClassVar[Dict[str, Any]] = {"icon": "account", "group": "management"}
865868

866869
def account(self, graph: Optional[Any] = None) -> BaseAccount:
867870
return self
@@ -872,7 +875,7 @@ class BaseRegion(PhantomBaseResource):
872875
kind: ClassVar[str] = "region"
873876
kind_display: ClassVar[str] = "Region"
874877
kind_description: ClassVar[str] = "A region."
875-
metadata: ClassVar[Dict[str, Any]] = {"icon": "region", "group": "control"}
878+
metadata: ClassVar[Dict[str, Any]] = {"icon": "region", "group": "management"}
876879

877880
long_name: Optional[str] = None
878881
latitude: Optional[float] = None
@@ -892,7 +895,7 @@ class BaseZone(PhantomBaseResource):
892895
kind: ClassVar[str] = "zone"
893896
kind_display: ClassVar[str] = "Zone"
894897
kind_description: ClassVar[str] = "A zone."
895-
metadata: ClassVar[Dict[str, Any]] = {"icon": "zone", "group": "control"}
898+
metadata: ClassVar[Dict[str, Any]] = {"icon": "zone", "group": "management"}
896899

897900
long_name: Optional[str] = None
898901

@@ -1000,7 +1003,7 @@ class Cloud(BaseCloud):
10001003
kind: ClassVar[str] = "cloud"
10011004
kind_display: ClassVar[str] = "Cloud"
10021005
kind_description: ClassVar[str] = "A cloud."
1003-
metadata: ClassVar[Dict[str, Any]] = {"icon": "cloud", "group": "control"}
1006+
metadata: ClassVar[Dict[str, Any]] = {"icon": "cloud", "group": "management"}
10041007

10051008
def delete(self, graph: Any) -> bool:
10061009
return False
@@ -1011,7 +1014,7 @@ class GraphRoot(PhantomBaseResource):
10111014
kind: ClassVar[str] = "graph_root"
10121015
kind_display: ClassVar[str] = "Graph Root"
10131016
kind_description: ClassVar[str] = "The root of the graph."
1014-
metadata: ClassVar[Dict[str, Any]] = {"icon": "graph_root", "group": "control"}
1017+
metadata: ClassVar[Dict[str, Any]] = {"icon": "graph_root", "group": "management"}
10151018

10161019
def delete(self, graph: Any) -> bool:
10171020
return False
@@ -1051,7 +1054,7 @@ class BaseKeyPair(BaseResource):
10511054
kind_description: ClassVar[str] = "A key pair."
10521055
metadata: ClassVar[Dict[str, Any]] = {"icon": "key", "group": "access_control"}
10531056
fingerprint: str = ""
1054-
_categories: ClassVar[List[Category]] = [Category.iam]
1057+
_categories: ClassVar[List[Category]] = [Category.access_control]
10551058

10561059

10571060
@define(eq=False, slots=False)
@@ -1256,7 +1259,7 @@ class BaseIamPrincipal(BaseResource):
12561259
"An IAM principal is an entity that can be authenticated and authorized to access resources."
12571260
)
12581261
metadata: ClassVar[Dict[str, Any]] = {"icon": "user", "group": "access_control"}
1259-
_categories: ClassVar[List[Category]] = [Category.iam]
1262+
_categories: ClassVar[List[Category]] = [Category.access_control]
12601263

12611264

12621265
@define(eq=False, slots=False)
@@ -1265,7 +1268,7 @@ class BaseUser(BaseResource):
12651268
kind_display: ClassVar[str] = "User"
12661269
kind_description: ClassVar[str] = "A user."
12671270
metadata: ClassVar[Dict[str, Any]] = {"icon": "user", "group": "access_control"}
1268-
_categories: ClassVar[List[Category]] = [Category.iam]
1271+
_categories: ClassVar[List[Category]] = [Category.access_control]
12691272

12701273

12711274
@define(eq=False, slots=False)
@@ -1274,7 +1277,7 @@ class BaseGroup(BaseResource):
12741277
kind_display: ClassVar[str] = "Group"
12751278
kind_description: ClassVar[str] = "A group."
12761279
metadata: ClassVar[Dict[str, Any]] = {"icon": "group", "group": "access_control"}
1277-
_categories: ClassVar[List[Category]] = [Category.iam]
1280+
_categories: ClassVar[List[Category]] = [Category.access_control]
12781281

12791282

12801283
@define(eq=False, slots=False)
@@ -1283,7 +1286,7 @@ class BasePolicy(BaseResource):
12831286
kind_display: ClassVar[str] = "Policy"
12841287
kind_description: ClassVar[str] = "A policy."
12851288
metadata: ClassVar[Dict[str, Any]] = {"icon": "policy", "group": "access_control"}
1286-
_categories: ClassVar[List[Category]] = [Category.iam]
1289+
_categories: ClassVar[List[Category]] = [Category.access_control]
12871290

12881291

12891292
@define(eq=False, slots=False)
@@ -1292,7 +1295,7 @@ class BaseRole(BaseResource):
12921295
kind_display: ClassVar[str] = "Role"
12931296
kind_description: ClassVar[str] = "A role."
12941297
metadata: ClassVar[Dict[str, Any]] = {"icon": "role", "group": "access_control"}
1295-
_categories: ClassVar[List[Category]] = [Category.iam]
1298+
_categories: ClassVar[List[Category]] = [Category.access_control]
12961299

12971300

12981301
@define(eq=False, slots=False)
@@ -1301,7 +1304,7 @@ class BaseInstanceProfile(BaseResource):
13011304
kind_display: ClassVar[str] = "Instance Profile"
13021305
kind_description: ClassVar[str] = "An instance profile."
13031306
metadata: ClassVar[Dict[str, Any]] = {"icon": "profile", "group": "access_control"}
1304-
_categories: ClassVar[List[Category]] = [Category.iam]
1307+
_categories: ClassVar[List[Category]] = [Category.access_control]
13051308

13061309

13071310
@define(eq=False, slots=False)
@@ -1311,7 +1314,7 @@ class BaseAccessKey(BaseResource):
13111314
kind_description: ClassVar[str] = "An access key."
13121315
metadata: ClassVar[Dict[str, Any]] = {"icon": "key", "group": "access_control"}
13131316
access_key_status: str = ""
1314-
_categories: ClassVar[List[Category]] = [Category.iam, Category.security]
1317+
_categories: ClassVar[List[Category]] = [Category.access_control, Category.security]
13151318

13161319

13171320
@define(eq=False, slots=False)
@@ -1320,7 +1323,7 @@ class BaseCertificate(BaseResource):
13201323
kind_display: ClassVar[str] = "Certificate"
13211324
kind_description: ClassVar[str] = "A certificate."
13221325
metadata: ClassVar[Dict[str, Any]] = {"icon": "certificate", "group": "access_control"}
1323-
_categories: ClassVar[List[Category]] = [Category.iam, Category.security]
1326+
_categories: ClassVar[List[Category]] = [Category.access_control, Category.security]
13241327
expires: Optional[datetime] = None
13251328
dns_names: Optional[List[str]] = None
13261329
sha1_fingerprint: Optional[str] = None
@@ -1339,7 +1342,7 @@ class BaseStack(BaseResource):
13391342
kind: ClassVar[str] = "stack"
13401343
kind_display: ClassVar[str] = "Stack"
13411344
kind_description: ClassVar[str] = "A stack."
1342-
metadata: ClassVar[Dict[str, Any]] = {"icon": "stack", "group": "control"}
1345+
metadata: ClassVar[Dict[str, Any]] = {"icon": "stack", "group": "management"}
13431346
stack_status: str = ""
13441347
stack_status_reason: str = ""
13451348
stack_parameters: Dict[str, str] = field(factory=dict)

fixlib/fixlib/core/model_check.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from attrs import define
55

6+
from fixlib.basecategories import Category
67
from fixlib.baseresources import BaseResource
78
from fixlib.core.model_export import transitive_classes
89
from fixlib.graph import resource_classes_to_fixcore_model
@@ -12,10 +13,7 @@
1213
# fmt: off
1314

1415
# 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-
}
16+
ResourceGroups: Set[str] = {c.name for c in Category}
1917
# Possible icon value: any addition needs to be supported in the frontend
2018
ResourceIcons: Set[str] = {
2119
"access_control", "account", "alarm", "application", "autoscaling_group", "backup",

fixlib/fixlib/core/model_export.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ def export_data_class(clazz: type) -> None:
251251
metadata["name"] = s
252252
if (s := getattr(clazz, "kind_service", None)) and isinstance(s, str):
253253
metadata["service"] = s
254+
if (slc := getattr(clazz, "categories", None)) and callable(slc) and (sl := slc()):
255+
metadata["categories"] = sl
254256
if with_description and (s := clazz.__dict__.get("kind_description", None)) and isinstance(s, str):
255257
metadata["description"] = s
256258

@@ -331,7 +333,7 @@ def node_to_dict(node: BaseResource, changes_only: bool = False, include_revisio
331333
"cleaned": node.cleaned,
332334
"phantom": node.phantom,
333335
"protected": node.protected,
334-
"categories": node.categories,
336+
"categories": node.categories(),
335337
**node._metadata,
336338
},
337339
"usage": node._resource_usage,

fixlib/test/test_basecategories.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,14 @@ class EmptyCategory(BaseResource):
2525
pass
2626

2727
assert EmptyCategory.get_all_categories() == []
28-
assert EmptyCategory(id="empty").categories == []
2928

3029

3130
def test_single_category():
3231
assert BaseInstance.get_all_categories() == [Category.compute]
33-
assert BaseInstance(id="instance").categories == ["compute"]
32+
assert BaseInstance(id="instance").categories() == ["compute"]
3433

3534
assert BaseVolume.get_all_categories() == [Category.storage]
36-
assert BaseVolume(id="volume").categories == ["storage"]
35+
assert BaseVolume(id="volume").categories() == ["storage"]
3736

3837

3938
def test_multiple_categories():
@@ -43,7 +42,7 @@ class CustomResource(BaseInstance, BaseVolume):
4342
expected_categories = [Category.compute, Category.storage, Category.management]
4443
expected_categories_str = [str(category.value) for category in expected_categories]
4544
assert set(CustomResource.get_all_categories()) == set(expected_categories)
46-
assert sorted(CustomResource(id="custom_resource").categories) == sorted(expected_categories_str)
45+
assert sorted(CustomResource(id="custom_resource").categories()) == sorted(expected_categories_str)
4746

4847

4948
def test_deeply_nested_categories():
@@ -56,7 +55,7 @@ class CustomResource2(CustomResource1, BaseVolume):
5655
expected_categories = [Category.compute, Category.monitoring, Category.storage, Category.security]
5756
expected_categories_str = [str(category.value) for category in expected_categories]
5857
assert set(CustomResource2.get_all_categories()) == set(expected_categories)
59-
assert sorted(CustomResource2(id="custom_resource2").categories) == sorted(expected_categories_str)
58+
assert sorted(CustomResource2(id="custom_resource2").categories()) == sorted(expected_categories_str)
6059

6160

6261
def test_all_categories():
@@ -67,11 +66,11 @@ def test_all_categories():
6766
BaseDatabase: [Category.compute, Category.database],
6867
BaseFirewall: [Category.networking, Category.security],
6968
BaseLoadBalancer: [Category.networking],
70-
BaseUser: [Category.iam],
71-
BaseGroup: [Category.iam],
72-
BasePolicy: [Category.iam],
73-
BaseRole: [Category.iam],
74-
BaseKeyPair: [Category.iam],
69+
BaseUser: [Category.access_control],
70+
BaseGroup: [Category.access_control],
71+
BasePolicy: [Category.access_control],
72+
BaseRole: [Category.access_control],
73+
BaseKeyPair: [Category.access_control],
7574
BaseSnapshot: [Category.storage],
7675
BaseHealthCheck: [Category.monitoring],
7776
BaseDNSZone: [Category.dns, Category.networking],
@@ -80,8 +79,4 @@ def test_all_categories():
8079
}
8180

8281
for resource_class, expected_categories_list in expected_categories.items():
83-
expected_categories_str = [str(category.value) for category in expected_categories_list]
8482
assert set(resource_class.get_all_categories()) == set(expected_categories_list)
85-
assert sorted(resource_class(id=f"{resource_class.__name__.lower()}").categories) == sorted(
86-
expected_categories_str
87-
)

fixlib/test/test_config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import pytest
33
from attrs import define, field
44
from typing import ClassVar, Dict, Any
5+
6+
from fixlib.baseresources import BaseResource, BaseSecurityGroup
57
from fixlib.config import Config, ConfigNotFoundError
68
from fixlib.args import get_arg_parser, ArgumentParser
79
from fixlib.core import add_args as core_add_args
@@ -146,3 +148,9 @@ class ConfigTest:
146148
factory=lambda: NestedConfigTest(),
147149
metadata={"description": "A test of nested config"},
148150
)
151+
152+
153+
def test_foo():
154+
r = BaseSecurityGroup(id="foo")
155+
p = getattr(r, "categories", None)
156+
print(p)

plugins/aws/fix_plugin_aws/collector.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ class AwsOrganizationalRoot(BaseOrganizationalRoot, AwsResource):
433433
kind_display: ClassVar[str] = "AWS Organizational Root"
434434
kind_description: ClassVar[str] = "An AWS Organizational Root is the root of an AWS Organization."
435435
kind_service = "organizations"
436-
metadata: ClassVar[Dict[str, Any]] = {"icon": "group", "group": "control"}
436+
metadata: ClassVar[Dict[str, Any]] = {"icon": "group", "group": "management"}
437437

438438

439439
@define(eq=False, slots=False)
@@ -442,4 +442,4 @@ class AwsOrganizationalUnit(BaseOrganizationalUnit, AwsResource):
442442
kind_display: ClassVar[str] = "AWS Organizational Unit"
443443
kind_description: ClassVar[str] = "An AWS Organizational Unit is a container for AWS Accounts."
444444
kind_service = "organizations"
445-
metadata: ClassVar[Dict[str, Any]] = {"icon": "group", "group": "control"}
445+
metadata: ClassVar[Dict[str, Any]] = {"icon": "group", "group": "management"}

0 commit comments

Comments
 (0)