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

Add table aws_application_signals_service_level_objective (#2291) #2305

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion aws/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"aws_appautoscaling_policy": tableAwsAppAutoScalingPolicy(ctx),
"aws_appautoscaling_target": tableAwsAppAutoScalingTarget(ctx),
"aws_appconfig_application": tableAwsAppConfigApplication(ctx),
"aws_application_signals_service_level_objective": tableAwsApplicationSignalsServiceLevelObjective(ctx),
"aws_appstream_fleet": tableAwsAppStreamFleet(ctx),
"aws_appstream_image": tableAwsAppStreamImage(ctx),
"aws_appsync_graphql_api": tableAwsAppsyncGraphQLApi(ctx),
Expand Down Expand Up @@ -467,7 +468,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"aws_securityhub_hub": tableAwsSecurityHub(ctx),
"aws_securityhub_insight": tableAwsSecurityHubInsight(ctx),
"aws_securityhub_member": tableAwsSecurityHubMember(ctx),
"aws_securityhub_enabled_product_subscription": tableAwsSecurityhubEnabledProductSubscription(ctx),
"aws_securityhub_enabled_product_subscription": tableAwsSecurityhubEnabledProductSubscription(ctx),
"aws_securityhub_product": tableAwsSecurityhubProduct(ctx),
"aws_securityhub_standards_control": tableAwsSecurityHubStandardsControl(ctx),
"aws_securityhub_standards_subscription": tableAwsSecurityHubStandardsSubscription(ctx),
Expand Down
39 changes: 24 additions & 15 deletions aws/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/aws/aws-sdk-go-v2/service/apigatewayv2"
"github.com/aws/aws-sdk-go-v2/service/appconfig"
"github.com/aws/aws-sdk-go-v2/service/applicationautoscaling"
"github.com/aws/aws-sdk-go-v2/service/applicationsignals"
"github.com/aws/aws-sdk-go-v2/service/apprunner"
"github.com/aws/aws-sdk-go-v2/service/appstream"
"github.com/aws/aws-sdk-go-v2/service/appsync"
Expand Down Expand Up @@ -315,6 +316,17 @@ func ApplicationAutoScalingClient(ctx context.Context, d *plugin.QueryData) (*ap
return applicationautoscaling.NewFromConfig(*cfg), nil
}

func ApplicationSignalsClient(ctx context.Context, d *plugin.QueryData) (*applicationsignals.Client, error) {
cfg, err := getClientForQueryRegion(ctx, d)
if err != nil {
return nil, err
}
if cfg == nil {
return nil, nil
}
return applicationsignals.NewFromConfig(*cfg), nil
}

func AppRunnerClient(ctx context.Context, d *plugin.QueryData) (*apprunner.Client, error) {
cfg, err := getClientForQuerySupportedRegion(ctx, d, appRunnerEndpoint.EndpointsID)
if err != nil {
Expand Down Expand Up @@ -1861,21 +1873,18 @@ func getClientWithMaxRetries(ctx context.Context, d *plugin.QueryData, region st
if awsSpcConfig.EndpointUrl != nil {
awsEndpointUrl = *awsSpcConfig.EndpointUrl
if awsEndpointUrl != "" {
customResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
return aws.Endpoint{
PartitionID: "aws",
URL: awsEndpointUrl,
SigningRegion: region,
}, nil
})
newCfg, err := config.LoadDefaultConfig(ctx, config.WithEndpointResolverWithOptions(customResolver))
if err != nil {
plugin.Logger(ctx).Error("service.getClientWithMaxRetries", "connection_error", err)
return nil, err
}
newCfg.Retryer = cfg.Retryer
newCfg.Region = cfg.Region
cfg = newCfg
// The global endpoint resolution interface is deprecated. The API
// for endpoint resolution is now unique to each service and is set via the
// EndpointResolverV2 field on service client options. Setting a value for
// EndpointResolver on aws.Config or service client options will prevent you
// from using any endpoint-related service features released after the
// introduction of EndpointResolverV2. You may also encounter broken or
// unexpected behavior when using the old global interface with services that
// use many endpoint-related customizations such as S3.
// https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/endpoints/
// By default the custom endpoint provided in "BaseEndpoint" will be resolved for each service
// Eg: https://github.com/aws/aws-sdk-go-v2/blob/v1.30.5/service/cloudfront/api_client.go#L257
cfg.BaseEndpoint = &awsEndpointUrl
}
}

Expand Down
221 changes: 221 additions & 0 deletions aws/table_aws_application_signals_service_level_objective.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package aws

import (
"context"
"strings"

"github.com/aws/aws-sdk-go-v2/service/applicationsignals"
cloudwatchlogsv1 "github.com/aws/aws-sdk-go/service/cloudwatchlogs"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/applicationsignals/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"
)

func tableAwsApplicationSignalsServiceLevelObjective(_ context.Context) *plugin.Table {
return &plugin.Table{
Name: "aws_application_signals_service_level_objective",
Description: "AWS Application Signals Service Level Objective",
Get: &plugin.GetConfig{
Hydrate: getApplicationSignalsServiceLevelObjective,
KeyColumns: plugin.SingleColumn("arn"),
IgnoreConfig: &plugin.IgnoreConfig{
ShouldIgnoreErrorFunc: shouldIgnoreErrors([]string{"ResourceNotFoundException"}),
},
Tags: map[string]string{"service": "application-signals", "action": "GetApplicationSignalsServiceLevelObjective"},
},
List: &plugin.ListConfig{
Hydrate: listApplicationSignalsServiceLevelObjectives,
KeyColumns: plugin.OptionalColumns([]string{"operation_name"}),
Tags: map[string]string{"service": "application-signals", "action": "ListApplicationSignalsServiceLevelObjectives"},
},
HydrateConfig: []plugin.HydrateConfig{
{
Func: getApplicationSignalsServiceLevelObjective,
Tags: map[string]string{"service": "application-signals", "action": "GetApplicationSignalsServiceLevelObjective"},
},
},
// AWS Doesn't treat it as a separate service it is under CloudWatch service but the API package is different.
// https://aws.amazon.com/about-aws/whats-new/2024/06/amazon-cloudwatch-application-signals-application-monitoring/
GetMatrixItemFunc: SupportedRegionMatrix(cloudwatchlogsv1.EndpointsID),
Columns: awsRegionalColumns([]*plugin.Column{
{
Name: "arn",
Description: "The Amazon Resource Name (ARN) of the service level objective.",
Type: proto.ColumnType_STRING,
},
{
Name: "name",
Description: "The name of the service level objective.",
Type: proto.ColumnType_STRING,
},
{
Name: "operation_name",
Description: " If this service level objective is specific to a single operation, this field displays the name of that operation.",
Type: proto.ColumnType_STRING,
},
{
Name: "created_time",
Description: "The date and time that this SLO was created.",
Type: proto.ColumnType_TIMESTAMP,
},
{
Name: "evaluation_type",
Description: "Displays whether this is a period-based SLO or a request-based SLO.",
Type: proto.ColumnType_STRING,
Hydrate: getApplicationSignalsServiceLevelObjective,
},
{
Name: "attainment_goal",
Description: "The attainment goal of the service level objective.",
Type: proto.ColumnType_DOUBLE,
Hydrate: getApplicationSignalsServiceLevelObjective,
Transform: transform.FromField("Goal.AttainmentGoal"),
},
{
Name: "goal",
Description: "The goal of the service level objective.",
Type: proto.ColumnType_JSON,
Hydrate: getApplicationSignalsServiceLevelObjective,
},
{
Name: "sli",
Description: "The sli of the service level objective.",
Type: proto.ColumnType_JSON,
Hydrate: getApplicationSignalsServiceLevelObjective,
},
{
Name: "request_based_sli",
Description: "A structure containing information about the performance metric that this SLO monitors, if this is a period-based SLO.",
Type: proto.ColumnType_JSON,
Hydrate: getApplicationSignalsServiceLevelObjective,
},

//// Steampipe Standard Columns
{
Name: "title",
Description: resourceInterfaceDescription("title"),
Type: proto.ColumnType_STRING,
Transform: transform.FromField("Name"),
},
{
Name: "akas",
Description: resourceInterfaceDescription("akas"),
Type: proto.ColumnType_JSON,
Transform: transform.FromField("Arn").Transform(arnToAkas),
},
}),
}
}

//// LIST FUNCTION

func listApplicationSignalsServiceLevelObjectives(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {
svc, err := ApplicationSignalsClient(ctx, d)

// Unsupported region check
if svc == nil {
return nil, nil
}
if err != nil {
plugin.Logger(ctx).Error("aws_application_signals_service_level_objective.listApplicationSignalsServiceLevelObjectives", "client_error", err)
return nil, err
}

maxItems := int32(50)

// Reduce the basic request limit down if the user has only requested a small number
if d.QueryContext.Limit != nil {
limit := int32(*d.QueryContext.Limit)
if limit < maxItems {
maxItems = int32(limit)
}
}

input := &applicationsignals.ListServiceLevelObjectivesInput{
MaxResults: &maxItems,
}

if d.EqualsQualString("operation_name") != "" {
input.OperationName = aws.String(d.EqualsQualString("operation_name"))
}

paginator := applicationsignals.NewListServiceLevelObjectivesPaginator(svc, input, func(o *applicationsignals.ListServiceLevelObjectivesPaginatorOptions) {
o.Limit = maxItems
o.StopOnDuplicateToken = true
})

for paginator.HasMorePages() {
// apply rate limiting
d.WaitForListRateLimit(ctx)

output, err := paginator.NextPage(ctx)
if err != nil {
plugin.Logger(ctx).Error("aws_application_signals_service_level_objective.listApplicationSignalsServiceLevelObjectives", "api_error", err)
return nil, err
}

for _, sloSummary := range output.SloSummaries {
d.StreamListItem(ctx, sloSummary)

// Context may get cancelled due to manual cancellation or if the limit has been reached
if d.RowsRemaining(ctx) == 0 {
return nil, nil
}
}
}

return nil, nil
}

//// HYDRATE FUNCTIONS

func getApplicationSignalsServiceLevelObjective(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
region := d.EqualsQualString(matrixKeyRegion)
arn := ""

if h.Item != nil {
data := h.Item.(types.ServiceLevelObjectiveSummary)
arn = *data.Arn
} else {
arn = d.EqualsQualString("arn")
}

// check if name is empty
if strings.TrimSpace(arn) == "" {
return nil, nil
}

// Restrict API call for other regions
if len(strings.Split(arn, ":")) > 3 && strings.Split(arn, ":")[3] != region {
return nil, nil
}

// Get client
svc, err := ApplicationSignalsClient(ctx, d)

// Unsupported region check
if svc == nil {
return nil, nil
}

if err != nil {
plugin.Logger(ctx).Error("aws_application_signals_service_level_objective.getApplicationSignalsServiceLevelObjective", "client_error", err)
return nil, err
}

params := &applicationsignals.GetServiceLevelObjectiveInput{
Id: aws.String(arn),
}

item, err := svc.GetServiceLevelObjective(ctx, params)
if err != nil {
plugin.Logger(ctx).Error("aws_application_signals_service_level_objective.getApplicationSignalsServiceLevelObjective", "api_error", err)
return nil, err
}

return item.Slo, nil
}
Loading
Loading