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

[feat][fixlib] Extend base resources with additional common properties #2278

Merged
merged 17 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
39 changes: 37 additions & 2 deletions fixlib/fixlib/baseresources.py
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,15 @@ class BaseBucket(BaseResource):
_metadata: ClassVar[Dict[str, Any]] = {"icon": "bucket", "group": "storage"}
_categories: ClassVar[List[Category]] = [Category.storage]

encryption_enabled: Optional[bool] = None
versioning_enabled: Optional[bool] = None


@unique
class QueueType(Enum):
kind: ClassVar[str] = "queue_type"
STANDARD = "standard"
FIFO = "fifo"

@define(eq=False, slots=False)
class BaseQueue(BaseResource):
Expand All @@ -1097,6 +1106,9 @@ class BaseQueue(BaseResource):
_kind_description: ClassVar[str] = "A storage queue."
_metadata: ClassVar[Dict[str, Any]] = {"icon": "queue", "group": "storage"}
_categories: ClassVar[List[Category]] = [Category.storage]
queue_type: Optional[QueueType] = None
approximate_message_count: Optional[int] = None
message_retention_period: Optional[int] = None


@define(eq=False, slots=False)
Expand Down Expand Up @@ -1125,6 +1137,8 @@ class BaseServerlessFunction(BaseResource):
_metadata: ClassVar[Dict[str, Any]] = {"icon": "function", "group": "compute"}
_categories: ClassVar[List[Category]] = [Category.compute]

memory_size: Optional[int] = None


@define(eq=False, slots=False)
class BaseNetwork(BaseResource):
Expand All @@ -1134,6 +1148,8 @@ class BaseNetwork(BaseResource):
_metadata: ClassVar[Dict[str, Any]] = {"icon": "network", "group": "networking"}
_categories: ClassVar[List[Category]] = [Category.networking]

cidr_blocks: List[str] = field(factory=list)


@define(eq=False, slots=False)
class BaseNetworkQuota(BaseQuota):
Expand Down Expand Up @@ -1215,6 +1231,8 @@ class BaseSubnet(BaseResource):
_metadata: ClassVar[Dict[str, Any]] = {"icon": "subnet", "group": "networking"}
_categories: ClassVar[List[Category]] = [Category.networking]

cidr_block: Optional[str] = None


@define(eq=False, slots=False)
class BaseGateway(BaseResource):
Expand Down Expand Up @@ -1374,8 +1392,8 @@ class BaseAccessKey(BaseResource):
_kind_display: ClassVar[str] = "Access Key"
_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.access_control, Category.security]
access_key_status: Optional[str] = None


@define(eq=False, slots=False)
Expand Down Expand Up @@ -1404,10 +1422,10 @@ class BaseStack(BaseResource):
_kind_display: ClassVar[str] = "Stack"
_kind_description: ClassVar[str] = "A stack."
_metadata: ClassVar[Dict[str, Any]] = {"icon": "stack", "group": "management"}
_categories: ClassVar[List[Category]] = [Category.devops, Category.management]
stack_status: str = ""
stack_status_reason: str = ""
stack_parameters: Dict[str, str] = field(factory=dict)
_categories: ClassVar[List[Category]] = [Category.devops, Category.management]


@define(eq=False, slots=False)
Expand Down Expand Up @@ -1453,6 +1471,8 @@ class BaseDNSZone(BaseResource):
_kind_description: ClassVar[str] = "A DNS zone."
_metadata: ClassVar[Dict[str, Any]] = {"icon": "dns", "group": "networking"}
_categories: ClassVar[List[Category]] = [Category.dns, Category.networking]
private_zone: Optional[bool] = None
zone_resource_record_set_count: Optional[int] = field(default=None, metadata=dict(ignore_history=True))
1101-1 marked this conversation as resolved.
Show resolved Hide resolved


@define(eq=False, slots=False)
Expand Down Expand Up @@ -1574,6 +1594,19 @@ class BaseManagedKubernetesClusterProvider(BaseResource):
endpoint: Optional[str] = field(default=None, metadata={"description": "The kubernetes API endpoint"})


class AIJobStatus(Enum):
PENDING = "pending"
PREPARING = "preparing"
RUNNING = "running"
STOPPING = "stopping"
STOPPED = "stopped"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
PAUSED = "paused"
UNKNOWN = "unknown"


@define(eq=False, slots=False)
class BaseAIResource(BaseResource):
kind: ClassVar[str] = "ai_resource"
Expand All @@ -1590,6 +1623,8 @@ class BaseAIJob(BaseAIResource):
_kind_description: ClassVar[str] = "An AI Job resource."
_metadata: ClassVar[Dict[str, Any]] = {"icon": "job", "group": "ai"}

ai_job_status: Optional[AIJobStatus] = field(default=None, metadata={"description": "Current status of the AI job"})


@define(eq=False, slots=False)
class BaseAIModel(BaseAIResource):
Expand Down
1 change: 1 addition & 0 deletions plugins/aws/fix_plugin_aws/resource/acm.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class AwsAcmCertificate(AwsResource, BaseCertificate):
"tags": S("Tags", default=[]) >> ToDict(),
"name": S("DomainName"),
"ctime": S("CreatedAt"),
"mtime": S("RenewalSummary", "UpdatedAt"),
"arn": S("CertificateArn"),
"subject_alternative_names": S("SubjectAlternativeNames", default=[]),
"domain_validation_options": S("DomainValidationOptions", default=[])
Expand Down
20 changes: 14 additions & 6 deletions plugins/aws/fix_plugin_aws/resource/bedrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
from fix_plugin_aws.resource.lambda_ import AwsLambdaFunction
from fix_plugin_aws.resource.s3 import AwsS3Bucket
from fix_plugin_aws.resource.rds import AwsRdsCluster, AwsRdsInstance
from fixlib.baseresources import BaseAIJob, ModelReference, BaseAIModel
from fixlib.baseresources import AIJobStatus, BaseAIJob, ModelReference, BaseAIModel
from fixlib.graph import Graph
from fixlib.json_bender import Bender, S, ForallBend, Bend, Sort
from fixlib.json_bender import Bender, S, ForallBend, Bend, MapEnum, Sort
from fixlib.types import Json

log = logging.getLogger("fix.plugins.aws")
Expand Down Expand Up @@ -82,6 +82,16 @@ def service_name(cls) -> str:
return service_name


AWS_BEDROCK_JOB_STATUS_MAPPING = {
"InProgress": AIJobStatus.RUNNING,
"Completed": AIJobStatus.COMPLETED,
"Failed": AIJobStatus.FAILED,
"Stopping": AIJobStatus.STOPPING,
"Stopped": AIJobStatus.STOPPED,
"Deleting": AIJobStatus.STOPPING,
}


@define(eq=False, slots=False)
class AwsBedrockFoundationModel(BaseAIModel, AwsResource):
kind: ClassVar[str] = "aws_bedrock_foundation_model"
Expand Down Expand Up @@ -553,7 +563,7 @@ class AwsBedrockModelCustomizationJob(BedrockTaggable, BaseAIJob, AwsResource):
"output_model_arn": S("outputModelArn"),
"client_request_token": S("clientRequestToken"),
"role_arn": S("roleArn"),
"status": S("status"),
"status": S("status") >> MapEnum(AWS_BEDROCK_JOB_STATUS_MAPPING, AIJobStatus.UNKNOWN),
"failure_message": S("failureMessage"),
"creation_time": S("creationTime"),
"last_modified_time": S("lastModifiedTime"),
Expand All @@ -575,7 +585,6 @@ class AwsBedrockModelCustomizationJob(BedrockTaggable, BaseAIJob, AwsResource):
output_model_arn: Optional[str] = field(default=None, metadata={"description": "The Amazon Resource Name (ARN) of the output model."}) # fmt: skip
client_request_token: Optional[str] = field(default=None, metadata={"description": "The token that you specified in the CreateCustomizationJob request."}) # fmt: skip
role_arn: Optional[str] = field(default=None, metadata={"description": "The Amazon Resource Name (ARN) of the IAM role."}) # fmt: skip
status: Optional[str] = field(default=None, metadata={"description": "The status of the job. A successful job transitions from in-progress to completed when the output model is ready to use. If the job failed, the failure message contains information about why the job failed."}) # fmt: skip
failure_message: Optional[str] = field(default=None, metadata={"description": "Information about why the job failed."}) # fmt: skip
creation_time: Optional[datetime] = field(default=None, metadata={"description": "Time that the resource was created."}) # fmt: skip
last_modified_time: Optional[datetime] = field(default=None, metadata={"description": "Time that the resource was last modified."}) # fmt: skip
Expand Down Expand Up @@ -777,7 +786,7 @@ class AwsBedrockEvaluationJob(BedrockTaggable, BaseAIJob, AwsResource):
"ctime": S("creationTime"),
"mtime": S("lastModifiedTime"),
"job_name": S("jobName"),
"status": S("status"),
"status": S("status") >> MapEnum(AWS_BEDROCK_JOB_STATUS_MAPPING, AIJobStatus.UNKNOWN),
"job_arn": S("jobArn"),
"job_description": S("jobDescription"),
"role_arn": S("roleArn"),
Expand All @@ -791,7 +800,6 @@ class AwsBedrockEvaluationJob(BedrockTaggable, BaseAIJob, AwsResource):
"failure_messages": S("failureMessages", default=[]),
}
job_name: Optional[str] = field(default=None, metadata={"description": "The name of the model evaluation job."}) # fmt: skip
status: Optional[str] = field(default=None, metadata={"description": "The status of the model evaluation job."}) # fmt: skip
job_arn: Optional[str] = field(default=None, metadata={"description": "The Amazon Resource Name (ARN) of the model evaluation job."}) # fmt: skip
job_description: Optional[str] = field(default=None, metadata={"description": "The description of the model evaluation job."}) # fmt: skip
role_arn: Optional[str] = field(default=None, metadata={"description": "The Amazon Resource Name (ARN) of the IAM service role used in the model evaluation job."}) # fmt: skip
Expand Down
1 change: 1 addition & 0 deletions plugins/aws/fix_plugin_aws/resource/cognito.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class AwsCognitoUser(AwsResource, BaseUser):
"enabled": S("Enabled"),
"user_status": S("UserStatus"),
"mfa_options": S("MFAOptions", default=[]) >> ForallBend(AwsCognitoMFAOptionType.mapping),
"username": S("Username"),
}
user_attributes: List[AwsCognitoAttributeType] = field(factory=list)
enabled: Optional[bool] = field(default=None)
Expand Down
32 changes: 29 additions & 3 deletions plugins/aws/fix_plugin_aws/resource/dynamodb.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@
from fix_plugin_aws.resource.kinesis import AwsKinesisStream
from fix_plugin_aws.resource.kms import AwsKmsKey
from fix_plugin_aws.utils import ToDict
from fixlib.baseresources import HasResourcePolicy, ModelReference, PolicySource, PolicySourceKind
from fixlib.baseresources import (
BaseDatabase,
DatabaseInstanceStatus,
HasResourcePolicy,
ModelReference,
PolicySource,
PolicySourceKind,
)
from fixlib.graph import Graph
from fixlib.json_bender import S, Bend, Bender, ForallBend, bend
from fixlib.json_bender import S, Bend, Bender, ForallBend, bend, K, MapValue
from fixlib.types import Json
from fixlib.json import sort_json

Expand Down Expand Up @@ -356,7 +363,7 @@ class AwsDynamoDbContinuousBackup:


@define(eq=False, slots=False)
class AwsDynamoDbTable(DynamoDbTaggable, AwsResource, HasResourcePolicy):
class AwsDynamoDbTable(DynamoDbTaggable, BaseDatabase, AwsResource, HasResourcePolicy):
kind: ClassVar[str] = "aws_dynamodb_table"
_kind_display: ClassVar[str] = "AWS DynamoDB Table"
_kind_description: ClassVar[str] = "AWS DynamoDB Table is a fully managed NoSQL database service that stores and retrieves data. It supports key-value and document data models, offering automatic scaling and low-latency performance. DynamoDB Tables handle data storage, indexing, and querying, providing consistent read and write throughput. They offer data encryption, backup, and recovery features for secure and reliable data management." # fmt: skip
Expand Down Expand Up @@ -396,6 +403,25 @@ class AwsDynamoDbTable(DynamoDbTaggable, AwsResource, HasResourcePolicy):
"dynamodb_sse_description": S("SSEDescription") >> Bend(AwsDynamoDbSSEDescription.mapping),
"dynamodb_archival_summary": S("ArchivalSummary") >> Bend(AwsDynamoDbArchivalSummary.mapping),
"dynamodb_table_class_summary": S("TableClassSummary") >> Bend(AwsDynamoDbTableClassSummary.mapping),
"db_type": K("dynamodb"),
"db_status": S("TableStatus")
>> MapValue(
{
"CREATING": DatabaseInstanceStatus.BUSY,
"UPDATING": DatabaseInstanceStatus.BUSY,
"DELETING": DatabaseInstanceStatus.BUSY,
"ACTIVE": DatabaseInstanceStatus.AVAILABLE,
"INACCESSIBLE_ENCRYPTION_CREDENTIALS": DatabaseInstanceStatus.FAILED,
"ARCHIVING": DatabaseInstanceStatus.BUSY,
"ARCHIVED": DatabaseInstanceStatus.STOPPED,
},
default=DatabaseInstanceStatus.UNKNOWN,
),
"volume_encrypted": S("SSEDescription", "Status")
>> MapValue(
{"ENABLING": True, "ENABLED": True, "DISABLING": False, "DISABLED": False, "UPDATING": None},
default=None,
),
}
arn: Optional[str] = field(default=None)
dynamodb_attribute_definitions: List[AwsDynamoDbAttributeDefinition] = field(factory=list)
Expand Down
3 changes: 2 additions & 1 deletion plugins/aws/fix_plugin_aws/resource/ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from fix_plugin_aws.resource.kms import AwsKmsKey
from fix_plugin_aws.resource.s3 import AwsS3Bucket
from fix_plugin_aws.utils import ToDict, TagsValue
from fix_plugin_aws.aws_client import AwsClient
from fixlib.baseresources import (
BaseInstance,
BaseKeyPair,
Expand Down Expand Up @@ -2155,6 +2154,7 @@ class AwsEc2Vpc(EC2Taggable, AwsResource, BaseNetwork):
"vpc_cidr_block_association_set": S("CidrBlockAssociationSet", default=[])
>> ForallBend(AwsEc2VpcCidrBlockAssociation.mapping),
"vpc_is_default": S("IsDefault"),
"cidr_blocks": S("CidrBlockAssociationSet", default=[]) >> ForallBend(S("CidrBlock")),
}
vpc_cidr_block: Optional[str] = field(default=None)
vpc_dhcp_options_id: Optional[str] = field(default=None)
Expand Down Expand Up @@ -2506,6 +2506,7 @@ class AwsEc2Subnet(EC2Taggable, AwsResource, BaseSubnet):
"subnet_ipv6_native": S("Ipv6Native"),
"subnet_private_dns_name_options_on_launch": S("PrivateDnsNameOptionsOnLaunch")
>> Bend(AwsEc2PrivateDnsNameOptionsOnLaunch.mapping),
"cidr_block": S("CidrBlock"),
}
subnet_availability_zone: Optional[str] = field(default=None)
subnet_availability_zone_id: Optional[str] = field(default=None)
Expand Down
1 change: 1 addition & 0 deletions plugins/aws/fix_plugin_aws/resource/iam.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ class AwsIamUser(AwsResource, BaseUser, BaseIamPrincipal):
"arn": S("Arn"),
"user_policies": S("UserPolicyList", default=[]) >> ForallBend(AwsIamPolicyDetail.mapping),
"user_permissions_boundary": S("PermissionsBoundary") >> Bend(AwsIamAttachedPermissionsBoundary.mapping),
"username": S("UserName"),
}
path: Optional[str] = field(default=None)
user_policies: List[AwsIamPolicyDetail] = field(factory=list)
Expand Down
1 change: 1 addition & 0 deletions plugins/aws/fix_plugin_aws/resource/lambda_.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ class AwsLambdaFunction(AwsResource, BaseServerlessFunction, HasResourcePolicy):
"function_signing_job_arn": S("SigningJobArn"),
"function_architectures": S("Architectures", default=[]),
"function_ephemeral_storage": S("EphemeralStorage", "Size"),
"memory_size": S("MemorySize"),
}
function_runtime: Optional[str] = field(default=None)
function_role: Optional[str] = field(default=None)
Expand Down
4 changes: 2 additions & 2 deletions plugins/aws/fix_plugin_aws/resource/route53.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,12 @@ class AwsRoute53Zone(AwsResource, BaseDNSZone):
"name": S("Name"),
"zone_caller_reference": S("CallerReference"),
"zone_config": S("Config") >> Bend(AwsRoute53ZoneConfig.mapping),
"zone_resource_record_set_count": S("ResourceRecordSetCount"),
"zone_linked_service": S("LinkedService") >> Bend(AwsRoute53LinkedService.mapping),
"private_zone": S("Config", "PrivateZone"),
"zone_resource_record_set_count": S("ResourceRecordSetCount"),
}
zone_caller_reference: Optional[str] = field(default=None)
zone_config: Optional[AwsRoute53ZoneConfig] = field(default=None)
zone_resource_record_set_count: Optional[int] = field(default=None, metadata=dict(ignore_history=True))
zone_linked_service: Optional[AwsRoute53LinkedService] = field(default=None)
zone_logging_config: Optional[AwsRoute53LoggingConfig] = field(default=None)

Expand Down
3 changes: 3 additions & 0 deletions plugins/aws/fix_plugin_aws/resource/s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ def add_bucket_encryption(bck: AwsS3Bucket) -> None:
mapped = bend(AwsS3ServerSideEncryptionRule.mapping, raw)
if rule := parse_json(mapped, AwsS3ServerSideEncryptionRule, builder):
bck.bucket_encryption_rules.append(rule)
bck.encryption_enabled = len(bck.bucket_encryption_rules) > 0

def add_bucket_policy(bck: AwsS3Bucket) -> None:
with builder.suppress(f"{service_name}.get-bucket-policy"):
Expand Down Expand Up @@ -266,9 +267,11 @@ def add_bucket_versioning(bck: AwsS3Bucket) -> None:
):
bck.bucket_versioning = raw_versioning.get("Status") == "Enabled"
bck.bucket_mfa_delete = raw_versioning.get("MFADelete") == "Enabled"
bck.versioning_enabled = bck.bucket_versioning
else:
bck.bucket_versioning = False
bck.bucket_mfa_delete = False
bck.versioning_enabled = False

def add_public_access(bck: AwsS3Bucket) -> None:
with builder.suppress(f"{service_name}.get-public-access-block"):
Expand Down
8 changes: 6 additions & 2 deletions plugins/aws/fix_plugin_aws/resource/sqs.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
ModelReference,
PolicySource,
PolicySourceKind,
QueueType,
)
from fixlib.graph import Graph
from fixlib.json_bender import F, Bender, S, AsInt, AsBool, Bend, ParseJson, Sorted
Expand Down Expand Up @@ -80,6 +81,8 @@ class AwsSqsQueue(AwsResource, BaseQueue, HasResourcePolicy):
"sqs_delay_seconds": S("DelaySeconds") >> AsInt(),
"sqs_receive_message_wait_time_seconds": S("ReceiveMessageWaitTimeSeconds") >> AsInt(),
"sqs_managed_sse_enabled": S("SqsManagedSseEnabled") >> AsBool(),
"message_retention_period": S("MessageRetentionPeriod") >> AsInt(),
"approximate_message_count": S("ApproximateNumberOfMessages") >> AsInt(),
}
sqs_queue_url: Optional[str] = field(default=None)
sqs_approximate_number_of_messages: Optional[int] = field(default=None, metadata=dict(ignore_history=True))
Expand Down Expand Up @@ -118,16 +121,17 @@ def called_collect_apis(cls) -> List[AwsApiSpec]:
]

@classmethod
def collect(cls: Type[AwsResource], json: List[Json], builder: GraphBuilder) -> None:
def collect(cls, json: List[Json], builder: GraphBuilder) -> None:
def add_instance(queue_url: str) -> None:
queue_attributes = builder.client.get(
service_name, "get-queue-attributes", "Attributes", QueueUrl=queue_url, AttributeNames=["All"]
)
if queue_attributes is not None:
queue_attributes["QueueUrl"] = queue_url
queue_attributes["QueueName"] = queue_url.rsplit("/", 1)[-1]
if instance := cls.from_api(queue_attributes, builder):
if instance := AwsSqsQueue.from_api(queue_attributes, builder):
builder.add_node(instance, queue_attributes)
instance.queue_type = QueueType.FIFO if instance.sqs_fifo_queue else QueueType.STANDARD
builder.submit_work(service_name, add_tags, instance)

def add_tags(queue: AwsSqsQueue) -> None:
Expand Down
4 changes: 2 additions & 2 deletions plugins/aws/test/resources/cloudfront_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ def validate_delete_args(**kwargs: Any) -> Any:


def test_functions() -> None:
first, builder = round_trip_for(AwsCloudFrontFunction)
first, builder = round_trip_for(AwsCloudFrontFunction, "memory_size")
assert len(builder.resources_of(AwsCloudFrontFunction)) == 1
assert len(first.tags) == 1
assert first.arn == "arn"


def test_function_deletion() -> None:
func, _ = round_trip_for(AwsCloudFrontFunction)
func, _ = round_trip_for(AwsCloudFrontFunction, "memory_size")

def validate_delete_args(**kwargs: Any) -> Any:
assert kwargs["action"] == "delete-function"
Expand Down
Loading
Loading