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

New resource: aws cloudwatch query definition #17899

Merged
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4c258f6
feat(aws_cloudwatch_query_definition): adds aws_cloudwatch_query_defi…
Feb 25, 2021
51161fb
chore(.gitignore): ignore the binary
Feb 25, 2021
b39e2eb
refactor(aws_cloudwatch_query_definition): handle the disappears case
Feb 26, 2021
0781a6e
style(fmt): run go fmt
Feb 26, 2021
5528826
test(aws_cloudwatch_query_definition): add basic acceptance test
Feb 26, 2021
9c4b012
refactor(aws_cloudwatch_query_definition): implement import and fix r…
Feb 26, 2021
84e2097
refactor(aws_cloudwatch_query_definition): set query_definition_id in…
Feb 26, 2021
f21ad4d
test(aws_cloudwatch_query_definition): include test for import
Feb 26, 2021
f415d4b
refactor(aws_cloudwatch_query_defintion): make provider function matc…
Feb 26, 2021
2144f2a
test(aws_cloudwatch_query_definition): test disappears case
Feb 26, 2021
96657fd
test(aws_cloudwatch_query_definition): test update
Feb 27, 2021
d62d0f0
refactor(aws_cloudwatch_query_definition): handle underscores in quer…
Mar 2, 2021
203abdb
refactor(aws_cloudwatch_query_definition): use aws.String func
Mar 2, 2021
b2fe015
refactor(cloudwatchlogs): generates page functions for DescribeQueryD…
Mar 2, 2021
13bde98
refactor(aws_cloudwatch_query_definition): better error handling, var…
Mar 2, 2021
277ec2e
refactor(aws_cloudwatch_query_definition): uses the generated paging …
Mar 2, 2021
d946107
style(finder): formats finder.go
Mar 2, 2021
53df017
test(aws_cloudwatch_query_definition): tests now use finder functions
Mar 2, 2021
0373da6
docs(aws_cloudwatch_query_definition): adds website documentation for…
Mar 2, 2021
de3b9eb
refactor(aws_cloudwatch_query_definition): uses aws.StringValue function
Mar 3, 2021
39b3d3e
chore(changelog): adds changelog entry for aws_cloudwatch_query_defin…
Mar 3, 2021
b7939b8
refactor(aws_cloudwatch_query_definition): fixes importlint issues
Mar 3, 2021
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ website/node_modules
log.txt
markdown-link-check*.txt
changelog.tmp
terraform-provider-aws

# Test exclusions
!command/test-fixtures/**/*.tfstate
Expand Down
31 changes: 31 additions & 0 deletions aws/internal/service/cloudwatchlogs/finder/finder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package finder

import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchlogs/lister"
)

func QueryDefinition(conn *cloudwatchlogs.CloudWatchLogs, qName, qId string) (*cloudwatchlogs.QueryDefinition, error) {
input := &cloudwatchlogs.DescribeQueryDefinitionsInput{
QueryDefinitionNamePrefix: aws.String(qName),
MaxResults: aws.Int64(10),
}

var result *cloudwatchlogs.QueryDefinition
err := lister.DescribeQueryDefinitionsPages(conn, input, func(page *cloudwatchlogs.DescribeQueryDefinitionsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, qd := range page.QueryDefinitions {
if aws.StringValue(qd.QueryDefinitionId) == qId {
result = qd
return false
}
}
return !lastPage
})

return result, err
}
3 changes: 3 additions & 0 deletions aws/internal/service/cloudwatchlogs/lister/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
//go:generate go run ../../../generators/listpages/main.go -function=DescribeQueryDefinitions github.com/aws/aws-sdk-go/service/cloudwatchlogs

package lister
31 changes: 31 additions & 0 deletions aws/internal/service/cloudwatchlogs/lister/list_pages_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ func Provider() *schema.Provider {
"aws_cloudwatch_composite_alarm": resourceAwsCloudWatchCompositeAlarm(),
"aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(),
"aws_cloudwatch_dashboard": resourceAwsCloudWatchDashboard(),
"aws_cloudwatch_query_definition": resourceAwsCloudWatchQueryDefinition(),
"aws_codedeploy_app": resourceAwsCodeDeployApp(),
"aws_codedeploy_deployment_config": resourceAwsCodeDeployDeploymentConfig(),
"aws_codedeploy_deployment_group": resourceAwsCodeDeployDeploymentGroup(),
Expand Down
168 changes: 168 additions & 0 deletions aws/resource_aws_cloudwatch_query_definition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package aws

import (
"context"
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/cloudwatchlogs/finder"
"log"
"strings"
)

func resourceAwsCloudWatchQueryDefinition() *schema.Resource {
return &schema.Resource{
CreateContext: resourceAwsCloudWatchQueryDefinitionCreate,
ReadContext: resourceAwsCloudWatchQueryDefinitionRead,
UpdateContext: resourceAwsCloudWatchQueryDefinitionUpdate,
DeleteContext: resourceAwsCloudWatchQueryDefinitionDelete,
Importer: &schema.ResourceImporter{
StateContext: resourceAwsCloudWatchQueryDefinitionImport,
},
SchemaVersion: 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SchemaVersion should not be set for new resources

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"query": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To more closely match the API, we should call this query_string

Type: schema.TypeString,
Required: true,
},
"query_definition_id": {
Type: schema.TypeString,
Computed: true,
},
"log_groups": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To more closely match the API, we should call this log_group_names

Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log group names have a particular format, so we can add ValidateFunc: validateLogGroupName so that validation can be done at plan time

},
},
},
}
}

func resourceAwsCloudWatchQueryDefinitionCreate(c context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*AWSClient).cloudwatchlogsconn
params := getAwsCloudWatchQueryDefinitionInput(d)
r, err := conn.PutQueryDefinition(params)
if err != nil {
return diag.FromErr(err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We prefer adding a context message to error returns, for example

Suggested change
return diag.FromErr(err)
return diag.FromErr(fmt.Errorf("error creating CloudWatch query definition (%s): %w", err))

}
d.SetId(*r.QueryDefinitionId)
if err := d.Set("query_definition_id", aws.StringValue(r.QueryDefinitionId)); err != nil {
return diag.FromErr(err)
}
Comment on lines +58 to +60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking for errors on Set() is only necessary when setting TypeList and TypeMap values

Suggested change
if err := d.Set("query_definition_id", aws.StringValue(r.QueryDefinitionId)); err != nil {
return diag.FromErr(err)
}
d.Set("query_definition_id", aws.StringValue(r.QueryDefinitionId))

return resourceAwsCloudWatchQueryDefinitionRead(c, d, meta)
}

func getAwsCloudWatchQueryDefinitionInput(d *schema.ResourceData) *cloudwatchlogs.PutQueryDefinitionInput {
name := d.Get("name").(string)
logGroups := d.Get("log_groups").([]interface{})
var lgs []*string

for _, group := range logGroups {
l := group.(string)
lgs = append(lgs, aws.String(l))
}

query := d.Get("query").(string)
return &cloudwatchlogs.PutQueryDefinitionInput{
Name: aws.String(name),
LogGroupNames: lgs,
QueryString: aws.String(query),
}
Comment on lines +66 to +79
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function expandStringList() can handle the conversion from []interface{} to []*string

Suggested change
logGroups := d.Get("log_groups").([]interface{})
var lgs []*string
for _, group := range logGroups {
l := group.(string)
lgs = append(lgs, aws.String(l))
}
query := d.Get("query").(string)
return &cloudwatchlogs.PutQueryDefinitionInput{
Name: aws.String(name),
LogGroupNames: lgs,
QueryString: aws.String(query),
}
logGroups := d.Get("log_groups").([]interface{})
query := d.Get("query").(string)
return &cloudwatchlogs.PutQueryDefinitionInput{
Name: aws.String(name),
LogGroupNames: expandStringList(logGroups),
QueryString: aws.String(query),
}

}

func resourceAwsCloudWatchQueryDefinitionRead(c context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*AWSClient).cloudwatchlogsconn
name := d.Get("name").(string)
id := d.Id()

result, err := finder.QueryDefinition(conn, name, id)

if err != nil {
return diag.FromErr(err)
}

if result == nil {
log.Printf("[WARN] cloudwatch query definition (%s) not found, removing from state", d.Id())
d.SetId("")
} else {
if err := d.Set("query", aws.StringValue(result.QueryString)); err != nil {
return diag.FromErr(err)
}
if err := d.Set("query_definition_id", aws.StringValue(result.QueryDefinitionId)); err != nil {
return diag.FromErr(err)
}
if err := d.Set("log_groups", aws.StringValueSlice(result.LogGroupNames)); err != nil {
return diag.FromErr(err)
}
}

return nil
}

func resourceAwsCloudWatchQueryDefinitionUpdate(c context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*AWSClient).cloudwatchlogsconn
queryId := d.Get("query_definition_id").(string)

parms := getAwsCloudWatchQueryDefinitionInput(d)
parms.QueryDefinitionId = aws.String(queryId)
_, err := conn.PutQueryDefinition(parms)
if err != nil {
return diag.FromErr(err)
}
return resourceAwsCloudWatchQueryDefinitionRead(c, d, meta)
}

func resourceAwsCloudWatchQueryDefinitionDelete(c context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
conn := meta.(*AWSClient).cloudwatchlogsconn
queryId := d.Get("query_definition_id").(string)

params := &cloudwatchlogs.DeleteQueryDefinitionInput{QueryDefinitionId: aws.String(queryId)}
_, err := conn.DeleteQueryDefinition(params)
if err != nil {
return diag.FromErr(err)
}
return nil
}

func resourceAwsCloudWatchQueryDefinitionImport(c context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
name, id, err := parseImportFields(d.Id())
if err != nil {
return nil, err
}

if err := d.Set("name", name); err != nil {
return nil, err
}

d.SetId(id)
return []*schema.ResourceData{d}, nil
}

func parseImportFields(id string) (string, string, error) {
// having underscores in the query name is valid. The last occurrence of the underscore should separate the ID
// from the name of the query.
malformed := "resource ID did not contain correct number of fields for import"
lastUnd := strings.LastIndexByte(id, '_')

// if there isn't an underscore, the import is malformed.
if lastUnd < 0 {
return "", "", fmt.Errorf(malformed)
}

name, qId := id[0:lastUnd], id[lastUnd+1:]

// if either name or ID are the empty string, the import is malformed.
if name == "" || qId == "" {
return "", "", fmt.Errorf(malformed)
}

return name, qId, nil
}
Loading