Skip to content

Commit 953ed5f

Browse files
Aws logs sync state (#353)
* Add enable_logs_sync to signalfx_aws_integration * Bump signalfx-go
1 parent 66a4f2e commit 953ed5f

10 files changed

+175
-128
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 6.9.0
2+
3+
IMPROVEMENTS:
4+
* resource/signalfx_aws_integration: Add support for AWS metric streams and AWS logs synchronization [#351](https://github.com/splunk-terraform/terraform-provider-signalfx/pull/351) [#353](https://github.com/splunk-terraform/terraform-provider-signalfx/pull/353)
5+
16
## 6.8.1
27

38
BUGFIXES:

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,23 +65,31 @@ $ export SFX_AUTH_TOKEN=XXXXXX
6565
$ make testacc
6666
```
6767

68-
To also run the AWS integration tests for Cloudwatch Metric Streams synchronization, you must create an actual AWS IAM user with an access key and secret that SignalFx can use to manage AWS Cloudwatch Metric Streams resources, and define the `SFX_TEST_AWS_ACCESS_KEY_ID` and `SFX_TEST_AWS_SECRET_ACCESS_KEY` environment variables:
68+
To also run the AWS integration tests for CloudWatch Metric Streams and AWS logs synchronization, you must create an actual AWS IAM user with an access key and secret that SignalFx can use to manage AWS resources, and define the `SFX_TEST_AWS_ACCESS_KEY_ID` and `SFX_TEST_AWS_SECRET_ACCESS_KEY` environment variables:
6969

7070
```sh
7171
export SFX_TEST_AWS_ACCESS_KEY_ID=AKIAXXXXXX
7272
export SFX_TEST_AWS_SECRET_ACCESS_KEY=XXXXXX
7373
```
7474

75-
The policies that this user must be granted are:
75+
The following permissions must be granted. Additional permissions may be required to capture logs from specific AWS services.
7676

7777
```
78-
"cloudwatch:ListMetricStreams",
78+
"cloudwatch:DeleteMetricStream",
7979
"cloudwatch:GetMetricStream",
80+
"cloudwatch:ListMetricStreams",
8081
"cloudwatch:PutMetricStream",
81-
"cloudwatch:DeleteMetricStream",
8282
"cloudwatch:StartMetricStreams",
8383
"cloudwatch:StopMetricStreams",
84-
"iam:PassRole"
84+
"iam:PassRole",
85+
86+
"logs:DeleteSubscriptionFilter",
87+
"logs:DescribeLogGroups",
88+
"logs:DescribeSubscriptionFilters",
89+
"logs:PutSubscriptionFilter",
90+
"s3:GetBucketLogging",
91+
"s3:GetBucketNotification",
92+
"s3:PutBucketNotification"
8593
```
8694

8795
See [Connect to AWS using the guided setup in Splunk Observability Cloud](https://docs.splunk.com/Observability/gdi/get-data-in/connect/aws/aws-wizardconfig.html) and [Enable CloudWatch Metric Streams](https://docs.splunk.com/Observability/gdi/get-data-in/connect/aws/aws-apiconfig.html#enable-cloudwatch-metric-streams) in Splunk documentation for more details about creating that IAM policy.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ require (
66
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d
77
github.com/hashicorp/terraform-plugin-sdk v1.15.0
88
github.com/mitchellh/go-homedir v1.1.0
9-
github.com/signalfx/signalfx-go v1.8.7
9+
github.com/signalfx/signalfx-go v1.8.8
1010
github.com/stretchr/testify v1.6.1
1111
)
1212

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,8 @@ github.com/signalfx/golib/v3 v3.3.37 h1:CvL6Q8hySjm7D82P039qH2lFWJvBa18IJdAwa4LH
217217
github.com/signalfx/golib/v3 v3.3.37/go.mod h1:zH70SVnrzVUqDmUFEwDk1HSwS71Pi2w3bSsNpnOtYuQ=
218218
github.com/signalfx/gomemcache v0.0.0-20180823214636-4f7ef64c72a9/go.mod h1:Ytb8KfCSyuwy/VILnROdgCvbQLA5ch0nkbG7lKT0BXw=
219219
github.com/signalfx/sapm-proto v0.4.0/go.mod h1:x3gtwJ1GRejtkghB4nYpwixh2zqJrLbPU959ZNhM0Fk=
220-
github.com/signalfx/signalfx-go v1.8.7 h1:05lfikNYb1ef8+RxmxZS1JnS5DgRoBK1a0bgRzJoA+s=
221-
github.com/signalfx/signalfx-go v1.8.7/go.mod h1:YhPTMdQJDfphcRBdk+9acbbAw1gYY7z5BIHUWzLmGlA=
220+
github.com/signalfx/signalfx-go v1.8.8 h1:Da0r1WVlXSRL6q2MhIXXkGmRYVWuh46NKm63Gn6Z2Go=
221+
github.com/signalfx/signalfx-go v1.8.8/go.mod h1:YhPTMdQJDfphcRBdk+9acbbAw1gYY7z5BIHUWzLmGlA=
222222
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
223223
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac h1:wbW+Bybf9pXxnCFAOWZTqkRjAc7rAIwo2e1ArUhiHxg=
224224
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=

signalfx/resource_signalfx_aws_external_integration.go

Lines changed: 2 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7-
"log"
8-
"strings"
9-
"time"
10-
11-
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
127
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
138
"github.com/signalfx/signalfx-go/integration"
9+
"log"
10+
"strings"
1411
)
1512

1613
func integrationAWSExternalResource() *schema.Resource {
@@ -126,66 +123,3 @@ func integrationAWSExternalCreate(d *schema.ResourceData, meta interface{}) erro
126123
func integrationAWSExternalDelete(d *schema.ResourceData, meta interface{}) error {
127124
return DoIntegrationAWSDelete(d, meta)
128125
}
129-
130-
// DoIntegrationAWSDelete is public because it is also used by integrationAWSTokenDelete
131-
func DoIntegrationAWSDelete(d *schema.ResourceData, meta interface{}) error {
132-
config := meta.(*signalfxConfig)
133-
134-
// Retrieve current integration state
135-
int, err := config.Client.GetAWSCloudWatchIntegration(context.TODO(), d.Id())
136-
if err != nil {
137-
return fmt.Errorf("Error fetching existing integration %s, %s", d.Id(), err.Error())
138-
}
139-
140-
// Only disable the Cloudwatch Metric Stream synchronization if needed
141-
if int.MetricStreamsSyncState != "" && int.MetricStreamsSyncState != "DISABLED" {
142-
int.MetricStreamsSyncState = "CANCELLING"
143-
_, err := config.Client.UpdateAWSCloudWatchIntegration(context.TODO(), d.Id(), int)
144-
if err != nil {
145-
if strings.Contains(err.Error(), "40") {
146-
err = fmt.Errorf("%s\nPlease verify you are using an admin token when working with integrations", err.Error())
147-
}
148-
return err
149-
}
150-
// Wait for expected Cloudwatch Metric Streams sync state to be disabled
151-
if _, err = waitForAWSIntegrationMetricStreamsSyncStateDelete(d, config, int.Id); err != nil {
152-
return err
153-
}
154-
}
155-
156-
return config.Client.DeleteAWSCloudWatchIntegration(context.TODO(), d.Id())
157-
}
158-
159-
func waitForAWSIntegrationMetricStreamsSyncStateDelete(d *schema.ResourceData, config *signalfxConfig, id string) (*integration.AwsCloudWatchIntegration, error) {
160-
pending := []string{
161-
"ENABLED",
162-
"CANCELLING",
163-
}
164-
target := []string{
165-
"",
166-
"DISABLED",
167-
}
168-
169-
stateConf := &resource.StateChangeConf{
170-
Pending: pending,
171-
Target: target,
172-
Refresh: func() (interface{}, string, error) {
173-
int, err := config.Client.GetAWSCloudWatchIntegration(context.TODO(), id)
174-
if err != nil {
175-
return 0, "", err
176-
}
177-
return int, int.MetricStreamsSyncState, nil
178-
},
179-
Timeout: d.Timeout(schema.TimeoutUpdate) - time.Minute,
180-
Delay: 10 * time.Second,
181-
MinTimeout: 5 * time.Second,
182-
}
183-
184-
int, err := stateConf.WaitForState()
185-
if err != nil {
186-
return nil, fmt.Errorf(
187-
"Error waiting for integration (%s) Cloudwatch Metric Streams sync state to become disabled: %s",
188-
id, err)
189-
}
190-
return int.(*integration.AwsCloudWatchIntegration), nil
191-
}

signalfx/resource_signalfx_aws_integration.go

Lines changed: 88 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@ import (
1414
"github.com/signalfx/signalfx-go/integration"
1515
)
1616

17+
type stateSupplierFunc = func(*integration.AwsCloudWatchIntegration) string
18+
19+
func metricStreamsStateSupplier(int *integration.AwsCloudWatchIntegration) string {
20+
return int.MetricStreamsSyncState
21+
}
22+
23+
func logsSyncStateSupplier(int *integration.AwsCloudWatchIntegration) string {
24+
return int.LogsSyncState
25+
}
26+
1727
func integrationAWSResource() *schema.Resource {
1828
return &schema.Resource{
1929
Schema: map[string]*schema.Schema{
@@ -185,6 +195,12 @@ func integrationAWSResource() *schema.Resource {
185195
Computed: true,
186196
Description: "Enables the use of Cloudwatch Metric Streams for metrics synchronization.",
187197
},
198+
"enable_logs_sync": &schema.Schema{
199+
Type: schema.TypeBool,
200+
Optional: true,
201+
Computed: true,
202+
Description: "Enables AWS logs synchronization.",
203+
},
188204
"named_token": &schema.Schema{
189205
Type: schema.TypeString,
190206
Optional: true,
@@ -274,6 +290,9 @@ func awsIntegrationAPIToTF(d *schema.ResourceData, aws *integration.AwsCloudWatc
274290
if err := d.Set("use_metric_streams_sync", aws.MetricStreamsSyncState == "ENABLED"); err != nil {
275291
return err
276292
}
293+
if err := d.Set("enable_logs_sync", aws.LogsSyncState == "ENABLED"); err != nil {
294+
return err
295+
}
277296
if err := d.Set("enable_check_large_volume", aws.EnableCheckLargeVolume); err != nil {
278297
return err
279298
}
@@ -382,8 +401,13 @@ func getPayloadAWSIntegration(d *schema.ResourceData) (*integration.AwsCloudWatc
382401
if d.Get("use_metric_streams_sync").(bool) {
383402
aws.MetricStreamsSyncState = "ENABLED"
384403
} else if d.HasChange("use_metric_streams_sync") {
385-
// Only set to CANCELLING state if the use_metric_streams_sync is false and it has changed, meaning it was ENABLED before
386-
aws.MetricStreamsSyncState = "CANCELLING"
404+
aws.MetricStreamsSyncState = "CANCELLING" // use_metric_streams_sync is false, and it has changed, meaning it was ENABLED before
405+
}
406+
407+
if d.Get("enable_logs_sync").(bool) {
408+
aws.LogsSyncState = "ENABLED"
409+
} else if d.HasChange("enable_logs_sync") {
410+
aws.LogsSyncState = "CANCELLING" // enable_logs_sync is false, and it has changed, meaning it was ENABLED before
387411
}
388412

389413
if d.Get("external_id").(string) != "" {
@@ -553,50 +577,88 @@ func integrationAWSUpdate(d *schema.ResourceData, meta interface{}) error {
553577
}
554578

555579
if d.HasChange("use_metric_streams_sync") {
556-
// Wait for expected Cloudwatch Metric Streams sync state to be reached
557-
if int, err = waitForAWSIntegrationMetricStreamsSyncState(d, config, int.Id); err != nil {
580+
if int, err = waitForIntegrationStateToSettle(d, config, int.Id, "use_metric_streams_sync", metricStreamsStateSupplier); err != nil {
581+
return err
582+
}
583+
}
584+
if d.HasChange("enable_logs_sync") {
585+
if int, err = waitForIntegrationStateToSettle(d, config, int.Id, "enable_logs_sync", logsSyncStateSupplier); err != nil {
558586
return err
559587
}
560588
}
561589

562590
return awsIntegrationAPIToTF(d, int)
563591
}
564592

565-
func waitForAWSIntegrationMetricStreamsSyncState(d *schema.ResourceData, config *signalfxConfig, id string) (*integration.AwsCloudWatchIntegration, error) {
593+
func DoIntegrationAWSDelete(d *schema.ResourceData, meta interface{}) error {
594+
config := meta.(*signalfxConfig)
595+
596+
// Retrieve current integration state
597+
int, err := config.Client.GetAWSCloudWatchIntegration(context.TODO(), d.Id())
598+
if err != nil {
599+
return fmt.Errorf("Error fetching existing integration %s, %s", d.Id(), err.Error())
600+
}
601+
602+
// Disable the AWS logs sync and/or CloudWatch metric streams sync if needed
603+
needToDisableMetricStreams := int.Enabled && int.MetricStreamsSyncState != "" && int.MetricStreamsSyncState != "DISABLED"
604+
needToDisableLogsSync := int.Enabled && int.LogsSyncState != "" && int.LogsSyncState != "DISABLED"
605+
if needToDisableMetricStreams || needToDisableLogsSync {
606+
if needToDisableMetricStreams {
607+
int.MetricStreamsSyncState = "CANCELLING"
608+
}
609+
if needToDisableLogsSync {
610+
int.LogsSyncState = "CANCELLING"
611+
}
612+
_, err := config.Client.UpdateAWSCloudWatchIntegration(context.TODO(), d.Id(), int)
613+
if err != nil {
614+
if strings.Contains(err.Error(), "40") {
615+
err = fmt.Errorf("%s\nPlease verify you are using an admin token when working with integrations", err.Error())
616+
}
617+
return err
618+
}
619+
if needToDisableMetricStreams {
620+
if _, err = waitForIntegrationSpecificSyncStateToSettle(d, false, config, int.Id, "use_metric_streams_sync", metricStreamsStateSupplier); err != nil {
621+
return err
622+
}
623+
}
624+
if needToDisableLogsSync {
625+
if _, err = waitForIntegrationSpecificSyncStateToSettle(d, false, config, int.Id, "enable_logs_sync", logsSyncStateSupplier); err != nil {
626+
return err
627+
}
628+
}
629+
}
630+
631+
return config.Client.DeleteAWSCloudWatchIntegration(context.TODO(), d.Id())
632+
}
633+
634+
func waitForIntegrationStateToSettle(d *schema.ResourceData, config *signalfxConfig, intId string, syncStateField string,
635+
stateSupplier stateSupplierFunc) (*integration.AwsCloudWatchIntegration, error) {
636+
return waitForIntegrationSpecificSyncStateToSettle(d, d.Get(syncStateField).(bool), config, intId, syncStateField, stateSupplier)
637+
}
638+
639+
func waitForIntegrationSpecificSyncStateToSettle(d *schema.ResourceData, syncState bool, config *signalfxConfig, intId string, syncStateField string,
640+
stateSupplier stateSupplierFunc) (*integration.AwsCloudWatchIntegration, error) {
566641
var pending, target []string
567-
useMetricStreamsSync := d.Get("use_metric_streams_sync").(bool)
568642
var expectedState string
569-
if useMetricStreamsSync {
643+
if syncState {
570644
expectedState = "enabled"
571-
pending = []string{
572-
"DISABLED",
573-
"CANCELLING",
574-
"CANCELLATION_FAILED",
575-
}
576-
target = []string{
577-
"ENABLED",
578-
}
645+
pending = []string{"DISABLED", "CANCELLING", "CANCELLATION_FAILED"}
646+
target = []string{"ENABLED"}
579647
} else {
580648
expectedState = "disabled"
581-
pending = []string{
582-
"ENABLED",
583-
"CANCELLING",
584-
}
585-
target = []string{
586-
"",
587-
"DISABLED",
588-
}
649+
pending = []string{"ENABLED", "CANCELLING"}
650+
target = []string{"", "DISABLED"}
589651
}
590652

591653
stateConf := &resource.StateChangeConf{
592654
Pending: pending,
593655
Target: target,
594656
Refresh: func() (interface{}, string, error) {
595-
int, err := config.Client.GetAWSCloudWatchIntegration(context.TODO(), id)
657+
int, err := config.Client.GetAWSCloudWatchIntegration(context.TODO(), intId)
596658
if err != nil {
597659
return 0, "", err
598660
}
599-
return int, int.MetricStreamsSyncState, nil
661+
return int, stateSupplier(int), nil
600662
},
601663
Timeout: d.Timeout(schema.TimeoutUpdate) - time.Minute,
602664
Delay: 10 * time.Second,
@@ -605,9 +667,7 @@ func waitForAWSIntegrationMetricStreamsSyncState(d *schema.ResourceData, config
605667

606668
int, err := stateConf.WaitForState()
607669
if err != nil {
608-
return nil, fmt.Errorf(
609-
"Error waiting for integration (%s) Cloudwatch Metric Streams sync state to become %s: %s",
610-
id, expectedState, err)
670+
return nil, fmt.Errorf("Error waiting for integration %s state for %s to become %s: %s", intId, syncStateField, expectedState, err)
611671
}
612672
return int.(*integration.AwsCloudWatchIntegration), nil
613673
}

0 commit comments

Comments
 (0)