-
Notifications
You must be signed in to change notification settings - Fork 123
Introducting new aws_rds_db_global_cluster table #2707
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
base: main
Are you sure you want to change the base?
Changes from all commits
1a57bce
2b1d099
97460ce
515569d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,254 @@ | ||
| package aws | ||
|
|
||
| import ( | ||
| "context" | ||
|
|
||
| "github.com/aws/aws-sdk-go-v2/aws" | ||
| "github.com/aws/aws-sdk-go-v2/service/rds" | ||
| "github.com/aws/aws-sdk-go-v2/service/rds/types" | ||
|
|
||
| "github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto" | ||
| "github.com/turbot/steampipe-plugin-sdk/v5/plugin" | ||
| "github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform" | ||
| ) | ||
|
|
||
| //// TABLE DEFINITION | ||
|
|
||
| func tableAwsRDSGlobalCluster(_ context.Context) *plugin.Table { | ||
| return &plugin.Table{ | ||
| Name: "aws_rds_db_global_cluster", | ||
| Description: "AWS RDS Global Cluster", | ||
| Get: &plugin.GetConfig{ | ||
| KeyColumns: plugin.SingleColumn("global_cluster_identifier"), | ||
| IgnoreConfig: &plugin.IgnoreConfig{ | ||
| ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"GlobalClusterNotFoundFault"}), | ||
| }, | ||
| Hydrate: getRDSGlobalCluster, | ||
| Tags: map[string]string{"service": "rds", "action": "DescribeGlobalClusters"}, | ||
| }, | ||
| List: &plugin.ListConfig{ | ||
| Hydrate: listRDSGlobalClusters, | ||
| Tags: map[string]string{"service": "rds", "action": "DescribeGlobalClusters"}, | ||
| }, | ||
| HydrateConfig: []plugin.HydrateConfig{ | ||
| { | ||
| Func: getRDSGlobalClusterTags, | ||
| Tags: map[string]string{"service": "rds", "action": "ListTagsForResource"}, | ||
| }, | ||
| }, | ||
| GetMatrixItemFunc: SupportedRegionMatrix(AWS_RDS_SERVICE_ID), | ||
| Columns: awsRegionalColumns([]*plugin.Column{ | ||
| { | ||
| Name: "global_cluster_identifier", | ||
| Description: "Contains a user-supplied global database cluster identifier.", | ||
| Type: proto.ColumnType_STRING, | ||
| }, | ||
| { | ||
| Name: "global_cluster_arn", | ||
| Description: "The Amazon Resource Name (ARN) for the global database cluster.", | ||
| Type: proto.ColumnType_STRING, | ||
| }, | ||
|
Comment on lines
+47
to
+50
|
||
| { | ||
| Name: "status", | ||
| Description: "Specifies the current state of this global database cluster.", | ||
| Type: proto.ColumnType_STRING, | ||
| }, | ||
| { | ||
| Name: "endpoint", | ||
| Description: "The writer endpoint for the primary DB cluster in this global database cluster.", | ||
| Type: proto.ColumnType_STRING, | ||
| Transform: transform.FromField("Endpoint"), | ||
| }, | ||
|
ppapishe marked this conversation as resolved.
|
||
| { | ||
| Name: "global_cluster_resource_id", | ||
| Description: "The AWS Region-unique, immutable identifier for the global database cluster.", | ||
| Type: proto.ColumnType_STRING, | ||
| }, | ||
| { | ||
| Name: "database_name", | ||
| Description: "The default database name within the global database cluster.", | ||
| Type: proto.ColumnType_STRING, | ||
| }, | ||
| { | ||
| Name: "deletion_protection", | ||
| Description: "The deletion protection setting for the global database cluster.", | ||
| Type: proto.ColumnType_BOOL, | ||
| }, | ||
| { | ||
| Name: "engine", | ||
| Description: "The Aurora database engine used by the global database cluster.", | ||
| Type: proto.ColumnType_STRING, | ||
| }, | ||
| { | ||
| Name: "engine_version", | ||
| Description: "Indicates the database engine version.", | ||
| Type: proto.ColumnType_STRING, | ||
| }, | ||
| { | ||
| Name: "storage_encrypted", | ||
| Description: "The storage encryption setting for the global database cluster.", | ||
| Type: proto.ColumnType_BOOL, | ||
| }, | ||
| { | ||
| Name: "failover_state", | ||
| Description: "Properties for the current state of an in-process or pending switchover/failover.", | ||
| Type: proto.ColumnType_JSON, | ||
| }, | ||
| { | ||
| Name: "global_cluster_members", | ||
| Description: "The list of primary and secondary clusters within the global database cluster.", | ||
| Type: proto.ColumnType_JSON, | ||
| }, | ||
| { | ||
| Name: "tags_src", | ||
| Description: "A list of tags attached to the global database cluster.", | ||
| Hydrate: getRDSGlobalClusterTags, | ||
| Type: proto.ColumnType_JSON, | ||
| Transform: transform.FromValue(), | ||
| }, | ||
|
|
||
| // Standard columns | ||
| { | ||
| Name: "title", | ||
| Description: resourceInterfaceDescription("title"), | ||
| Type: proto.ColumnType_STRING, | ||
| Transform: transform.FromField("GlobalClusterIdentifier"), | ||
| }, | ||
| { | ||
| Name: "tags", | ||
| Description: resourceInterfaceDescription("tags"), | ||
| Type: proto.ColumnType_JSON, | ||
| Hydrate: getRDSGlobalClusterTags, | ||
| Transform: transform.FromValue().Transform(rdsGlobalClusterTagListToTurbotTags), | ||
| }, | ||
| { | ||
| Name: "akas", | ||
| Description: resourceInterfaceDescription("akas"), | ||
| Type: proto.ColumnType_JSON, | ||
| Transform: transform.FromField("GlobalClusterArn").Transform(arnToAkas), | ||
| }, | ||
| }), | ||
| } | ||
| } | ||
|
|
||
| //// LIST FUNCTION | ||
|
|
||
| func listRDSGlobalClusters(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { | ||
| svc, err := RDSClient(ctx, d) | ||
| if err != nil { | ||
| plugin.Logger(ctx).Error("aws_rds_db_global_cluster.listRDSGlobalClusters", "connection_error", err) | ||
| return nil, err | ||
| } | ||
|
|
||
| maxLimit := int32(100) | ||
| if d.QueryContext.Limit != nil { | ||
| limit := int32(*d.QueryContext.Limit) | ||
| if limit < maxLimit { | ||
| if limit < 20 { | ||
| maxLimit = 20 | ||
| } else { | ||
| maxLimit = limit | ||
| } | ||
| } | ||
| } | ||
|
|
||
| input := &rds.DescribeGlobalClustersInput{MaxRecords: aws.Int32(maxLimit)} | ||
| paginator := rds.NewDescribeGlobalClustersPaginator(svc, input, func(o *rds.DescribeGlobalClustersPaginatorOptions) { | ||
| o.Limit = maxLimit | ||
| o.StopOnDuplicateToken = true | ||
| }) | ||
|
|
||
| for paginator.HasMorePages() { | ||
| d.WaitForListRateLimit(ctx) | ||
|
|
||
| output, err := paginator.NextPage(ctx) | ||
| if err != nil { | ||
| plugin.Logger(ctx).Error("aws_rds_db_global_cluster.listRDSGlobalClusters", "api_error", err) | ||
| return nil, err | ||
| } | ||
|
|
||
| for _, globalCluster := range output.GlobalClusters { | ||
| d.StreamListItem(ctx, globalCluster) | ||
| if d.RowsRemaining(ctx) == 0 { | ||
| return nil, nil | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return nil, nil | ||
| } | ||
|
|
||
| //// HYDRATE FUNCTIONS | ||
|
|
||
| func getRDSGlobalCluster(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { | ||
| globalClusterIdentifier := d.EqualsQuals["global_cluster_identifier"].GetStringValue() | ||
| if globalClusterIdentifier == "" { | ||
| return nil, nil | ||
| } | ||
|
|
||
| svc, err := RDSClient(ctx, d) | ||
| if err != nil { | ||
| plugin.Logger(ctx).Error("aws_rds_db_global_cluster.getRDSGlobalCluster", "connection_error", err) | ||
| return nil, err | ||
| } | ||
|
|
||
| input := &rds.DescribeGlobalClustersInput{ | ||
| GlobalClusterIdentifier: aws.String(globalClusterIdentifier), | ||
| } | ||
|
|
||
| op, err := svc.DescribeGlobalClusters(ctx, input) | ||
| if err != nil { | ||
| plugin.Logger(ctx).Error("aws_rds_db_global_cluster.getRDSGlobalCluster", "api_error", err) | ||
| return nil, err | ||
| } | ||
|
|
||
| if len(op.GlobalClusters) > 0 { | ||
| return op.GlobalClusters[0], nil | ||
| } | ||
|
|
||
| return nil, nil | ||
| } | ||
|
|
||
| func getRDSGlobalClusterTags(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { | ||
| svc, err := RDSClient(ctx, d) | ||
| if err != nil { | ||
| plugin.Logger(ctx).Error("aws_rds_db_global_cluster.getRDSGlobalClusterTags", "connection_error", err) | ||
| return nil, err | ||
| } | ||
|
|
||
| globalCluster := h.Item.(types.GlobalCluster) | ||
| if globalCluster.GlobalClusterArn == nil { | ||
| return nil, nil | ||
| } | ||
|
|
||
| input := &rds.ListTagsForResourceInput{ResourceName: globalCluster.GlobalClusterArn} | ||
| op, err := svc.ListTagsForResource(ctx, input) | ||
| if err != nil { | ||
| plugin.Logger(ctx).Error("aws_rds_db_global_cluster.getRDSGlobalClusterTags", "api_error", err) | ||
| return nil, err | ||
| } | ||
|
|
||
| return op.TagList, nil | ||
| } | ||
|
|
||
| //// TRANSFORM FUNCTION | ||
|
|
||
| func rdsGlobalClusterTagListToTurbotTags(_ context.Context, d *transform.TransformData) (interface{}, error) { | ||
| tagList, ok := d.Value.([]types.Tag) | ||
| if !ok { | ||
| return nil, nil | ||
| } | ||
|
|
||
| if len(tagList) == 0 { | ||
| return nil, nil | ||
| } | ||
|
|
||
| tagsMap := map[string]string{} | ||
| for _, tag := range tagList { | ||
| if tag.Key != nil && tag.Value != nil { | ||
| tagsMap[*tag.Key] = *tag.Value | ||
| } | ||
| } | ||
|
|
||
| return tagsMap, nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| --- | ||
| title: "Steampipe Table: aws_rds_db_global_cluster - Query AWS RDS Global Clusters using SQL" | ||
| description: "Allows users to query AWS RDS Global Clusters and retrieve details about global database topology, status, and member clusters." | ||
| folder: "RDS" | ||
| --- | ||
|
|
||
| # Table: aws_rds_db_global_cluster - Query AWS RDS Global Clusters using SQL | ||
|
|
||
| An AWS RDS Global Cluster (Aurora Global Database) is an overarching layer that groups regional Aurora DB clusters into a single globally distributed database. | ||
|
|
||
| ## Table Usage Guide | ||
|
|
||
| The `aws_rds_db_global_cluster` table in Steampipe provides information about RDS global clusters in your account. You can use this table to audit global database configurations, check failover readiness, and inspect member clusters and encryption settings. | ||
|
|
||
| ## Examples | ||
|
|
||
| ### List RDS global clusters | ||
|
|
||
| ```sql+postgres | ||
| select | ||
| global_cluster_identifier, | ||
| status, | ||
| endpoint, | ||
| engine, | ||
| engine_version | ||
| from | ||
| aws_rds_db_global_cluster; | ||
| ``` | ||
|
|
||
| ```sql+sqlite | ||
| select | ||
| global_cluster_identifier, | ||
| status, | ||
| endpoint, | ||
| engine, | ||
| engine_version | ||
| from | ||
| aws_rds_db_global_cluster; | ||
| ``` | ||
|
|
||
| ### Show global cluster members | ||
|
|
||
| ```sql+postgres | ||
| select | ||
| global_cluster_identifier, | ||
| member ->> 'DBClusterArn' as db_cluster_arn, | ||
| member ->> 'IsWriter' as is_writer, | ||
| member -> 'Readers' as readers | ||
| from | ||
| aws_rds_db_global_cluster | ||
| cross join jsonb_array_elements(global_cluster_members) as member; | ||
| ``` | ||
|
|
||
| ```sql+sqlite | ||
| select | ||
| global_cluster_identifier, | ||
| json_extract(member.value, '$.DBClusterArn') as db_cluster_arn, | ||
| json_extract(member.value, '$.IsWriter') as is_writer, | ||
| json_extract(member.value, '$.Readers') as readers | ||
| from | ||
| aws_rds_db_global_cluster, | ||
| json_each(global_cluster_members) as member; | ||
| ``` | ||
|
|
||
| ### Find unencrypted global clusters | ||
|
|
||
| ```sql+postgres | ||
| select | ||
| global_cluster_identifier, | ||
| storage_encrypted | ||
| from | ||
| aws_rds_db_global_cluster | ||
| where | ||
| not storage_encrypted; | ||
| ``` | ||
|
|
||
| ```sql+sqlite | ||
| select | ||
| global_cluster_identifier, | ||
| storage_encrypted | ||
| from | ||
| aws_rds_db_global_cluster | ||
| where | ||
| storage_encrypted = 0; | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The table factory function name is inconsistent with the existing RDS DB table naming pattern (e.g., tableAwsRDSDBCluster/tableAwsRDSDBOptionGroup). To match the convention and improve discoverability, rename this to
tableAwsRDSDBGlobalClusterand update the reference inaws/plugin.go.