Skip to content

Commit

Permalink
[lib][feat] Unify groups and categories
Browse files Browse the repository at this point in the history
  • Loading branch information
aquamatthias committed Sep 16, 2024
1 parent 62a867e commit 5fa33be
Show file tree
Hide file tree
Showing 30 changed files with 183 additions and 161 deletions.
36 changes: 25 additions & 11 deletions fixlib/fixlib/basecategories.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ class Networking(BaseCategory):


@dataclass(frozen=True)
class Iam(BaseCategory):
name: str = "iam"
description: str = "Identity & Access Management"
class AccessControl(BaseCategory):
name: str = "access_control"
description: str = "Access Control"


@dataclass(frozen=True)
Expand Down Expand Up @@ -98,16 +98,30 @@ class Dns(BaseCategory):
description: str = "DNS"


@dataclass(frozen=True)
class ManagedKubernetes(BaseCategory):
name: str = "managed_kubernetes"
description: str = "Managed Kubernetes"


@dataclass(frozen=True)
class Misc(BaseCategory):
name: str = "misc"
description: str = "Miscellaneous"


class Category(Enum):
ai = Ai()
analytics = Analytics()
compute = Compute()
storage = Storage()
database = Database()
security = Security()
networking = Networking()
iam = Iam()
management = Management()
monitoring = Monitoring()
analytics = Analytics()
ai = Ai()
devops = DevOps()
dns = Dns()
access_control = AccessControl()
managed_kubernetes = ManagedKubernetes()
management = Management()
misc = Misc()
monitoring = Monitoring()
networking = Networking()
security = Security()
storage = Storage()
45 changes: 24 additions & 21 deletions fixlib/fixlib/baseresources.py
Original file line number Diff line number Diff line change
Expand Up @@ -738,9 +738,12 @@ def gather_categories(class_type: Type["BaseResource"]) -> List[Category]:

return list(set(gather_categories(cls)))

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


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

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

kind: ClassVar[str] = "quota"
kind_display: ClassVar[str] = "Quota"
Expand Down Expand Up @@ -806,7 +809,7 @@ class BaseType(BaseQuota):
kind: ClassVar[str] = "type"
kind_display: ClassVar[str] = "Type"
kind_description: ClassVar[str] = "A generic type."
metadata: ClassVar[Dict[str, Any]] = {"icon": "type", "group": "control"}
metadata: ClassVar[Dict[str, Any]] = {"icon": "type", "group": "management"}


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

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

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

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

long_name: Optional[str] = None

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

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

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


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


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


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


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


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


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


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


@define(eq=False, slots=False)
Expand All @@ -1320,7 +1323,7 @@ class BaseCertificate(BaseResource):
kind_display: ClassVar[str] = "Certificate"
kind_description: ClassVar[str] = "A certificate."
metadata: ClassVar[Dict[str, Any]] = {"icon": "certificate", "group": "access_control"}
_categories: ClassVar[List[Category]] = [Category.iam, Category.security]
_categories: ClassVar[List[Category]] = [Category.access_control, Category.security]
expires: Optional[datetime] = None
dns_names: Optional[List[str]] = None
sha1_fingerprint: Optional[str] = None
Expand All @@ -1339,7 +1342,7 @@ class BaseStack(BaseResource):
kind: ClassVar[str] = "stack"
kind_display: ClassVar[str] = "Stack"
kind_description: ClassVar[str] = "A stack."
metadata: ClassVar[Dict[str, Any]] = {"icon": "stack", "group": "control"}
metadata: ClassVar[Dict[str, Any]] = {"icon": "stack", "group": "management"}
stack_status: str = ""
stack_status_reason: str = ""
stack_parameters: Dict[str, str] = field(factory=dict)
Expand Down
6 changes: 2 additions & 4 deletions fixlib/fixlib/core/model_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from attrs import define

from fixlib.basecategories import Category
from fixlib.baseresources import BaseResource
from fixlib.core.model_export import transitive_classes
from fixlib.graph import resource_classes_to_fixcore_model
Expand All @@ -12,10 +13,7 @@
# fmt: off

# Possible group value: any addition needs to be supported in the frontend
ResourceGroups: Set[str] = {
"access_control", "compute", "control", "database", "generative_ai",
"group", "managed_kubernetes", "misc", "networking", "storage"
}
ResourceGroups: Set[str] = {c.name for c in Category}
# Possible icon value: any addition needs to be supported in the frontend
ResourceIcons: Set[str] = {
"access_control", "account", "alarm", "application", "autoscaling_group", "backup",
Expand Down
4 changes: 3 additions & 1 deletion fixlib/fixlib/core/model_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ def export_data_class(clazz: type) -> None:
metadata["name"] = s
if (s := getattr(clazz, "kind_service", None)) and isinstance(s, str):
metadata["service"] = s
if (slc := getattr(clazz, "categories", None)) and callable(slc) and (sl := slc()):
metadata["categories"] = sl
if with_description and (s := clazz.__dict__.get("kind_description", None)) and isinstance(s, str):
metadata["description"] = s

Expand Down Expand Up @@ -331,7 +333,7 @@ def node_to_dict(node: BaseResource, changes_only: bool = False, include_revisio
"cleaned": node.cleaned,
"phantom": node.phantom,
"protected": node.protected,
"categories": node.categories,
"categories": node.categories(),
**node._metadata,
},
"usage": node._resource_usage,
Expand Down
23 changes: 9 additions & 14 deletions fixlib/test/test_basecategories.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ class EmptyCategory(BaseResource):
pass

assert EmptyCategory.get_all_categories() == []
assert EmptyCategory(id="empty").categories == []


def test_single_category():
assert BaseInstance.get_all_categories() == [Category.compute]
assert BaseInstance(id="instance").categories == ["compute"]
assert BaseInstance(id="instance").categories() == ["compute"]

assert BaseVolume.get_all_categories() == [Category.storage]
assert BaseVolume(id="volume").categories == ["storage"]
assert BaseVolume(id="volume").categories() == ["storage"]


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


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


def test_all_categories():
Expand All @@ -67,11 +66,11 @@ def test_all_categories():
BaseDatabase: [Category.compute, Category.database],
BaseFirewall: [Category.networking, Category.security],
BaseLoadBalancer: [Category.networking],
BaseUser: [Category.iam],
BaseGroup: [Category.iam],
BasePolicy: [Category.iam],
BaseRole: [Category.iam],
BaseKeyPair: [Category.iam],
BaseUser: [Category.access_control],
BaseGroup: [Category.access_control],
BasePolicy: [Category.access_control],
BaseRole: [Category.access_control],
BaseKeyPair: [Category.access_control],
BaseSnapshot: [Category.storage],
BaseHealthCheck: [Category.monitoring],
BaseDNSZone: [Category.dns, Category.networking],
Expand All @@ -80,8 +79,4 @@ def test_all_categories():
}

for resource_class, expected_categories_list in expected_categories.items():
expected_categories_str = [str(category.value) for category in expected_categories_list]
assert set(resource_class.get_all_categories()) == set(expected_categories_list)
assert sorted(resource_class(id=f"{resource_class.__name__.lower()}").categories) == sorted(
expected_categories_str
)
8 changes: 8 additions & 0 deletions fixlib/test/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import pytest
from attrs import define, field
from typing import ClassVar, Dict, Any

from fixlib.baseresources import BaseResource, BaseSecurityGroup
from fixlib.config import Config, ConfigNotFoundError
from fixlib.args import get_arg_parser, ArgumentParser
from fixlib.core import add_args as core_add_args
Expand Down Expand Up @@ -146,3 +148,9 @@ class ConfigTest:
factory=lambda: NestedConfigTest(),
metadata={"description": "A test of nested config"},
)


def test_foo():
r = BaseSecurityGroup(id="foo")
p = getattr(r, "categories", None)
print(p)
4 changes: 2 additions & 2 deletions plugins/aws/fix_plugin_aws/collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ class AwsOrganizationalRoot(BaseOrganizationalRoot, AwsResource):
kind_display: ClassVar[str] = "AWS Organizational Root"
kind_description: ClassVar[str] = "An AWS Organizational Root is the root of an AWS Organization."
kind_service = "organizations"
metadata: ClassVar[Dict[str, Any]] = {"icon": "group", "group": "control"}
metadata: ClassVar[Dict[str, Any]] = {"icon": "group", "group": "management"}


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

0 comments on commit 5fa33be

Please sign in to comment.