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

[lib][feat] Unify groups and categories #2194

Merged
merged 2 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading