From 460d26296e716b05c1e96897d57579f0a6b0dcbc Mon Sep 17 00:00:00 2001 From: Etourneau Gwenn Date: Thu, 18 Jan 2024 14:45:50 +0900 Subject: [PATCH 1/3] Add sumologic as supported metrics sink --- go.mod | 2 +- go.sum | 8 +-- managed/resource_metrics_exporter.go | 89 +++++++++++++++++++++++++--- 3 files changed, 87 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index a6cf709..49cbc6f 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/hashicorp/terraform-plugin-framework-validators v0.4.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/sethvargo/go-retry v0.2.3 - github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20231030163130-9268a00de50c + github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20240116030641-973390683b73 ) require ( diff --git a/go.sum b/go.sum index bda9639..2bff395 100644 --- a/go.sum +++ b/go.sum @@ -159,10 +159,10 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9 github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20230915211221-361825bc5618 h1:ubU9s9gJPqDVcl9ZFd6sXZWhvRWJtcgdG5WtVVn4WE4= -github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20230915211221-361825bc5618/go.mod h1:5vW0xIzIZw+1djkiWKx0qqNmqbRBSf4mjc4qw8lIMik= -github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20231030163130-9268a00de50c h1:MzOpYz9LCniKMUhWZOnp6kn8bda5zq8vQ0oI58akrtc= -github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20231030163130-9268a00de50c/go.mod h1:5vW0xIzIZw+1djkiWKx0qqNmqbRBSf4mjc4qw8lIMik= +github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20240116030641-973390683b73 h1:FWFFiWGqOjKxXay6ycDmVXZPX6oYFyvPGa+TSXYPm3k= +github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20240116030641-973390683b73/go.mod h1:5vW0xIzIZw+1djkiWKx0qqNmqbRBSf4mjc4qw8lIMik= +github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20240117020629-6fbf564e7278 h1:1qDNJO7TUYYNK+E7BiHLoVrpSvftNkPUo51fvh0Vtyk= +github.com/yugabyte/yugabytedb-managed-go-client-internal v0.0.0-20240117020629-6fbf564e7278/go.mod h1:5vW0xIzIZw+1djkiWKx0qqNmqbRBSf4mjc4qw8lIMik= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/managed/resource_metrics_exporter.go b/managed/resource_metrics_exporter.go index ba5fe88..b810ddd 100644 --- a/managed/resource_metrics_exporter.go +++ b/managed/resource_metrics_exporter.go @@ -49,13 +49,14 @@ func (r resourceMetricsExporterType) GetSchema(_ context.Context) (tfsdk.Schema, Description: "The type of third party metrics sink. ", Type: types.StringType, Required: true, - Validators: []tfsdk.AttributeValidator{stringvalidator.OneOf("DATADOG", "GRAFANA")}, + Validators: []tfsdk.AttributeValidator{stringvalidator.OneOf("DATADOG", "GRAFANA", "SUMOLOGIC")}, }, "datadog_spec": { Description: "Configuration for Datadog metrics sink.", Optional: true, Validators: []tfsdk.AttributeValidator{ schemavalidator.ConflictsWith(path.MatchRoot("grafana_spec")), + schemavalidator.ConflictsWith(path.MatchRoot("sumologic_spec")), }, Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ "api_key": { @@ -74,6 +75,10 @@ func (r resourceMetricsExporterType) GetSchema(_ context.Context) (tfsdk.Schema, "grafana_spec": { Description: "Configuration for Grafana metrics sink.", Optional: true, + Validators: []tfsdk.AttributeValidator{ + schemavalidator.ConflictsWith(path.MatchRoot("datadog_spec")), + schemavalidator.ConflictsWith(path.MatchRoot("sumologic_spec")), + }, Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ "access_policy_token": { Description: "Grafana Access Policy Token", @@ -98,6 +103,34 @@ func (r resourceMetricsExporterType) GetSchema(_ context.Context) (tfsdk.Schema, }, }), }, + "sumologic_spec": { + Description: "Configuration for Sumologic metrics sink.", + Optional: true, + Validators: []tfsdk.AttributeValidator{ + schemavalidator.ConflictsWith(path.MatchRoot("datadog_spec")), + schemavalidator.ConflictsWith(path.MatchRoot("grafana_spec")), + }, + Attributes: tfsdk.SingleNestedAttributes(map[string]tfsdk.Attribute{ + "access_id": { + Description: "Sumo Logic Access Key ID", + Type: types.StringType, + Required: true, + Sensitive: true, + }, + "access_key": { + Description: "Sumo Logic Access Key", + Type: types.StringType, + Required: true, + Sensitive: true, + }, + "installation_token": { + Description: "A SumoLogic installation token to export metrics", + Type: types.StringType, + Required: true, + Sensitive: true, + }, + }), + }, }, }, nil } @@ -118,6 +151,7 @@ func getMetricsExporterPlan(ctx context.Context, plan tfsdk.Plan, me *MetricsExp diags.Append(plan.GetAttribute(ctx, path.Root("type"), &me.Type)...) diags.Append(plan.GetAttribute(ctx, path.Root("datadog_spec"), &me.DataDogSpec)...) diags.Append(plan.GetAttribute(ctx, path.Root("grafana_spec"), &me.GrafanaSpec)...) + diags.Append(plan.GetAttribute(ctx, path.Root("sumologic_spec"), &me.SumoLogicSpec)...) return diags } @@ -131,6 +165,8 @@ func getIDsFromMetricsExporterState(ctx context.Context, state tfsdk.State, me * state.GetAttribute(ctx, path.Root("datadog_spec"), &me.DataDogSpec) case string(openapiclient.METRICSEXPORTERCONFIGTYPEENUM_GRAFANA): state.GetAttribute(ctx, path.Root("grafana_spec"), &me.GrafanaSpec) + case string(openapiclient.METRICSEXPORTERCONFIGTYPEENUM_SUMOLOGIC): + state.GetAttribute(ctx, path.Root("sumologic_spec"), &me.SumoLogicSpec) } } @@ -186,6 +222,7 @@ func (r resourceMetricsExporter) Create(ctx context.Context, req tfsdk.CreateRes metricsExporterConfigSpec := openapiclient.NewMetricsExporterConfigurationSpec(configName, *metricsSinkTypeEnum) apiKey := "" + var sumoSpec *SumoLogicSpec switch *metricsSinkTypeEnum { case openapiclient.METRICSEXPORTERCONFIGTYPEENUM_DATADOG: if plan.DataDogSpec == nil { @@ -209,10 +246,21 @@ func (r resourceMetricsExporter) Create(ctx context.Context, req tfsdk.CreateRes grafanaSpec := openapiclient.NewGrafanaMetricsExporterConfigurationSpec(plan.GrafanaSpec.AccessTokenPolicy.Value, plan.GrafanaSpec.Zone.Value, plan.GrafanaSpec.InstanceId.Value, plan.GrafanaSpec.OrgSlug.Value) metricsExporterConfigSpec.SetGrafanaSpec(*grafanaSpec) apiKey = plan.GrafanaSpec.AccessTokenPolicy.Value + case openapiclient.METRICSEXPORTERCONFIGTYPEENUM_SUMOLOGIC: + if plan.SumoLogicSpec == nil { + resp.Diagnostics.AddError( + "sumologic_spec is required for type SUMOLOGIC", + "sumologic_spec is required when third party sink is SUMOLOGIC. Please include this field in the resource", + ) + return + } + sumoLogicSpec := openapiclient.NewSumologicMetricsExporterConfigurationSpec(plan.SumoLogicSpec.InstallationToken.Value, plan.SumoLogicSpec.AccessId.Value, plan.SumoLogicSpec.AccessKey.Value) + metricsExporterConfigSpec.SetSumologicSpec(*sumoLogicSpec) + sumoSpec = plan.SumoLogicSpec default: //We should never go there normally resp.Diagnostics.AddError( - "Only DATADOG and GRAFANA is currently supported as a third party sink", + "Only DATADOG,GRAFANA,SUMOLOGIC are currently supported as a third party sink", "", ) return @@ -226,7 +274,7 @@ func (r resourceMetricsExporter) Create(ctx context.Context, req tfsdk.CreateRes metricsExporterId := CreateResp.GetData().Info.Id - config, readOK, message := resourceMetricsExporterRead(accountId, projectId, metricsExporterId, "", apiKey, apiClient) + config, readOK, message := resourceMetricsExporterRead(accountId, projectId, metricsExporterId, "", apiKey, apiClient, sumoSpec) if !readOK { resp.Diagnostics.AddError("Unable to read the state of the metrics exporter config ", message) return @@ -246,6 +294,7 @@ func (r resourceMetricsExporter) Read(ctx context.Context, req tfsdk.ReadResourc getIDsFromMetricsExporterState(ctx, req.State, &state) configID := state.ConfigID.Value apiKey := "" + var sumoSpec *SumoLogicSpec // We cannot use the api return as the apikey return by the api is masked. // We need to use the one provided by the user which should be in the state @@ -254,6 +303,8 @@ func (r resourceMetricsExporter) Read(ctx context.Context, req tfsdk.ReadResourc apiKey = state.DataDogSpec.ApiKey.Value case string(openapiclient.METRICSEXPORTERCONFIGTYPEENUM_GRAFANA): apiKey = state.GrafanaSpec.AccessTokenPolicy.Value + case string(openapiclient.METRICSEXPORTERCONFIGTYPEENUM_SUMOLOGIC): + sumoSpec = state.SumoLogicSpec } apiClient := r.p.client @@ -270,7 +321,7 @@ func (r resourceMetricsExporter) Read(ctx context.Context, req tfsdk.ReadResourc return } - config, readOK, message := resourceMetricsExporterRead(accountId, projectId, configID, "", apiKey, apiClient) + config, readOK, message := resourceMetricsExporterRead(accountId, projectId, configID, "", apiKey, apiClient, sumoSpec) if !readOK { resp.Diagnostics.AddError("Unable to read the state of the metrics exporter config ", message) return @@ -287,6 +338,16 @@ func (r resourceMetricsExporter) Read(ctx context.Context, req tfsdk.ReadResourc if config.GrafanaSpec.AccessTokenPolicy.Value == state.GrafanaSpec.EncryptedKey() { config.GrafanaSpec.AccessTokenPolicy.Value = apiKey } + case string(openapiclient.METRICSEXPORTERCONFIGTYPEENUM_SUMOLOGIC): + if config.SumoLogicSpec.AccessKey.Value == state.SumoLogicSpec.EncryptedKey("access_key") { + config.SumoLogicSpec.AccessKey.Value = sumoSpec.AccessKey.Value + } + if config.SumoLogicSpec.AccessId.Value == state.SumoLogicSpec.EncryptedKey("access_id") { + config.SumoLogicSpec.AccessId.Value = sumoSpec.AccessId.Value + } + if config.SumoLogicSpec.InstallationToken.Value == state.SumoLogicSpec.EncryptedKey("installation_token") { + config.SumoLogicSpec.InstallationToken.Value = sumoSpec.InstallationToken.Value + } } diags := resp.State.Set(ctx, &config) @@ -320,6 +381,7 @@ func (r resourceMetricsExporter) Update(ctx context.Context, req tfsdk.UpdateRes metricsExporterConfigSpec := openapiclient.NewMetricsExporterConfigurationSpec(configName, *metricsSinkTypeEnum) apiKey := "" + var sumoSpec *SumoLogicSpec switch *metricsSinkTypeEnum { case openapiclient.METRICSEXPORTERCONFIGTYPEENUM_DATADOG: if plan.DataDogSpec == nil { @@ -343,10 +405,21 @@ func (r resourceMetricsExporter) Update(ctx context.Context, req tfsdk.UpdateRes grafanaSpec := openapiclient.NewGrafanaMetricsExporterConfigurationSpec(plan.GrafanaSpec.AccessTokenPolicy.Value, plan.GrafanaSpec.Zone.Value, plan.GrafanaSpec.InstanceId.Value, plan.GrafanaSpec.OrgSlug.Value) metricsExporterConfigSpec.SetGrafanaSpec(*grafanaSpec) apiKey = plan.GrafanaSpec.AccessTokenPolicy.Value + case openapiclient.METRICSEXPORTERCONFIGTYPEENUM_SUMOLOGIC: + if plan.SumoLogicSpec == nil { + resp.Diagnostics.AddError( + "sumologic_spec is required for type SUMOLOGIC", + "sumologic_spec is required when third party sink is SUMOLOGIC. Please include this field in the resource", + ) + return + } + sumoLogicSpec := openapiclient.NewSumologicMetricsExporterConfigurationSpec(plan.SumoLogicSpec.InstallationToken.Value, plan.SumoLogicSpec.AccessId.Value, plan.SumoLogicSpec.AccessKey.Value) + metricsExporterConfigSpec.SetSumologicSpec(*sumoLogicSpec) + sumoSpec = plan.SumoLogicSpec default: //We should never go there normally resp.Diagnostics.AddError( - "Only DATADOG and GRAFANA is currently supported as a third party sink", + "Only DATADOG, GRAFANA and SUMOLOGIC are currently supported as a third party sink", "", ) return @@ -360,7 +433,7 @@ func (r resourceMetricsExporter) Update(ctx context.Context, req tfsdk.UpdateRes metricsExporterId := updateResp.GetData().Info.Id - config, readOK, message := resourceMetricsExporterRead(accountId, projectId, metricsExporterId, "", apiKey, apiClient) + config, readOK, message := resourceMetricsExporterRead(accountId, projectId, metricsExporterId, "", apiKey, apiClient, sumoSpec) if !readOK { resp.Diagnostics.AddError("Unable to read the state of the metrics exporter config ", message) return @@ -386,7 +459,7 @@ func (r resourceMetricsExporter) Update(ctx context.Context, req tfsdk.UpdateRes } -func resourceMetricsExporterRead(accountId string, projectId string, configID string, configName string, apiKey string, apiClient *openapiclient.APIClient) (me MetricsExporter, readOK bool, errorMessage string) { +func resourceMetricsExporterRead(accountId string, projectId string, configID string, configName string, apiKey string, apiClient *openapiclient.APIClient, sumoSpec *SumoLogicSpec) (me MetricsExporter, readOK bool, errorMessage string) { config, err := GetConfigByNameorID(accountId, projectId, configID, configName, apiClient) if err != nil { @@ -415,6 +488,8 @@ func resourceMetricsExporterRead(accountId string, projectId string, configID st InstanceId: types.String{Value: string(config.GetSpec().GrafanaSpec.Get().InstanceId)}, OrgSlug: types.String{Value: string(config.GetSpec().GrafanaSpec.Get().OrgSlug)}, } + case openapiclient.METRICSEXPORTERCONFIGTYPEENUM_SUMOLOGIC: + me.SumoLogicSpec = sumoSpec } return me, true, "" From 2091edb4a0d69c8ba5b77f580acf8e490e077aed Mon Sep 17 00:00:00 2001 From: Etourneau Gwenn Date: Thu, 18 Jan 2024 14:57:54 +0900 Subject: [PATCH 2/3] Fix Grafana obfuscation --- managed/models.go | 36 ++++++++++++++++++++++++++++-------- managed/util.go | 6 +++++- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/managed/models.go b/managed/models.go index bdae37e..8ad5a16 100644 --- a/managed/models.go +++ b/managed/models.go @@ -256,13 +256,14 @@ type ApiKey struct { } type MetricsExporter struct { - AccountID types.String `tfsdk:"account_id"` - ProjectID types.String `tfsdk:"project_id"` - ConfigID types.String `tfsdk:"config_id"` - ConfigName types.String `tfsdk:"config_name"` - Type types.String `tfsdk:"type"` - DataDogSpec *DataDogSpec `tfsdk:"datadog_spec"` - GrafanaSpec *GrafanaSpec `tfsdk:"grafana_spec"` + AccountID types.String `tfsdk:"account_id"` + ProjectID types.String `tfsdk:"project_id"` + ConfigID types.String `tfsdk:"config_id"` + ConfigName types.String `tfsdk:"config_name"` + Type types.String `tfsdk:"type"` + DataDogSpec *DataDogSpec `tfsdk:"datadog_spec"` + GrafanaSpec *GrafanaSpec `tfsdk:"grafana_spec"` + SumoLogicSpec *SumoLogicSpec `tfsdk:"sumologic_spec"` } type DataDogSpec struct { @@ -277,12 +278,31 @@ type GrafanaSpec struct { OrgSlug types.String `tfsdk:"org_slug"` } +type SumoLogicSpec struct { + AccessKey types.String `tfsdk:"access_key"` + AccessId types.String `tfsdk:"access_id"` + InstallationToken types.String `tfsdk:"installation_token"` +} + func (d DataDogSpec) EncryptedKey() string { return obfuscateString(d.ApiKey.Value) } func (g GrafanaSpec) EncryptedKey() string { - return obfuscateString(g.AccessTokenPolicy.Value) + return obfuscateStringLenght(g.AccessTokenPolicy.Value, 5) +} + +func (s SumoLogicSpec) EncryptedKey(key string) string { + switch key { + + case "access_key": + return obfuscateString(s.AccessKey.Value) + case "access_id": + return obfuscateString(s.AccessId.Value) + case "installation_token": + return obfuscateString(s.InstallationToken.Value) + } + return "" } type AssociateMetricsExporterCluster struct { diff --git a/managed/util.go b/managed/util.go index ceeb2fd..2d3ce0d 100644 --- a/managed/util.go +++ b/managed/util.go @@ -82,10 +82,14 @@ func SliceStringToSliceTypesString(slice []string) []types.String { } func obfuscateString(s string) string { + return obfuscateStringLenght(s, 2) +} + +func obfuscateStringLenght(s string, l int) string { if len(s) < 6 { return "X" } - substring := s[2 : len(s)-2] + substring := s[l : len(s)-l] replaced := strings.Replace(s, substring, strings.Repeat("X", len(substring)), 1) return replaced } From c5ba4a6b89a0085f64d39beeb9d9bf305e270a6e Mon Sep 17 00:00:00 2001 From: Etourneau Gwenn Date: Thu, 18 Jan 2024 15:02:24 +0900 Subject: [PATCH 3/3] Update Docs --- docs/resources/metrics_exporter.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/resources/metrics_exporter.md b/docs/resources/metrics_exporter.md index b0f8e42..2a83405 100644 --- a/docs/resources/metrics_exporter.md +++ b/docs/resources/metrics_exporter.md @@ -48,6 +48,7 @@ resource "ybm_metrics_exporter" "grafana" { - `datadog_spec` (Attributes) Configuration for Datadog metrics sink. (see [below for nested schema](#nestedatt--datadog_spec)) - `grafana_spec` (Attributes) Configuration for Grafana metrics sink. (see [below for nested schema](#nestedatt--grafana_spec)) +- `sumologic_spec` (Attributes) Configuration for Sumologic metrics sink. (see [below for nested schema](#nestedatt--sumologic_spec)) ### Read-Only @@ -72,4 +73,14 @@ Required: - `access_policy_token` (String, Sensitive) Grafana Access Policy Token - `instance_id` (String) Grafana InstanceID. - `org_slug` (String) Grafana OrgSlug. -- `zone` (String) Grafana Zone. \ No newline at end of file +- `zone` (String) Grafana Zone. + + + +### Nested Schema for `sumologic_spec` + +Required: + +- `access_id` (String, Sensitive) Sumo Logic Access Key ID +- `access_key` (String, Sensitive) Sumo Logic Access Key +- `installation_token` (String, Sensitive) A SumoLogic installation token to export metrics \ No newline at end of file