From 2252813c0c2b7265a236de10b44db7f79600869c Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 6 Aug 2024 14:18:14 +0200 Subject: [PATCH 01/72] feat(synthetics): add http and tcp monitor resource --- go.mod | 1 + go.sum | 2 + .../synthetics/private_location/create.go | 6 +- .../synthetics/private_location/delete.go | 6 +- .../synthetics/private_location/read.go | 6 +- .../synthetics/private_location/resource.go | 22 +- .../synthetics/private_location/schema.go | 13 - internal/kibana/synthetics/resource.go | 99 ++++++++ internal/kibana/synthetics/schema.go | 232 ++++++++++++++++++ .../kbapi/api.kibana_synthetics.go | 40 ++- 10 files changed, 377 insertions(+), 50 deletions(-) create mode 100644 internal/kibana/synthetics/resource.go diff --git a/go.mod b/go.mod index 245c7e789..b087d2bd1 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.21.0 // indirect github.com/hashicorp/terraform-json v0.22.1 // indirect + github.com/hashicorp/terraform-plugin-framework-jsontypes v0.1.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect diff --git a/go.sum b/go.sum index 57f5adc6b..701227735 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/hashicorp/terraform-json v0.22.1 h1:xft84GZR0QzjPVWs4lRUwvTcPnegqlyS7 github.com/hashicorp/terraform-json v0.22.1/go.mod h1:JbWSQCLFSXFFhg42T7l9iJwdGXBYV8fmmD6o/ML4p3A= github.com/hashicorp/terraform-plugin-framework v1.10.0 h1:xXhICE2Fns1RYZxEQebwkB2+kXouLC932Li9qelozrc= github.com/hashicorp/terraform-plugin-framework v1.10.0/go.mod h1:qBXLDn69kM97NNVi/MQ9qgd1uWWsVftGSnygYG1tImM= +github.com/hashicorp/terraform-plugin-framework-jsontypes v0.1.0 h1:b8vZYB/SkXJT4YPbT3trzE6oJ7dPyMy68+9dEDKsJjE= +github.com/hashicorp/terraform-plugin-framework-jsontypes v0.1.0/go.mod h1:tP9BC3icoXBz72evMS5UTFvi98CiKhPdXF6yLs1wS8A= github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 h1:bxZfGo9DIUoLLtHMElsu+zwqI4IsMZQBRRy4iLzZJ8E= github.com/hashicorp/terraform-plugin-framework-validators v0.13.0/go.mod h1:wGeI02gEhj9nPANU62F2jCaHjXulejm/X+af4PdZaNo= github.com/hashicorp/terraform-plugin-go v0.23.0 h1:AALVuU1gD1kPb48aPQUjug9Ir/125t+AAurhqphJ2Co= diff --git a/internal/kibana/synthetics/private_location/create.go b/internal/kibana/synthetics/private_location/create.go index 9f0bf2aa3..f66b95777 100644 --- a/internal/kibana/synthetics/private_location/create.go +++ b/internal/kibana/synthetics/private_location/create.go @@ -3,15 +3,13 @@ package private_location import ( "context" "fmt" + "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { - tflog.Info(ctx, "Create private location") - - kibanaClient := r.getKibanaClient(response.Diagnostics) + kibanaClient := synthetics.GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return } diff --git a/internal/kibana/synthetics/private_location/delete.go b/internal/kibana/synthetics/private_location/delete.go index 1b7ecb5f1..05cb32a73 100644 --- a/internal/kibana/synthetics/private_location/delete.go +++ b/internal/kibana/synthetics/private_location/delete.go @@ -3,15 +3,13 @@ package private_location import ( "context" "fmt" + "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { - tflog.Info(ctx, "Delete private location") - - kibanaClient := r.getKibanaClient(response.Diagnostics) + kibanaClient := synthetics.GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return } diff --git a/internal/kibana/synthetics/private_location/read.go b/internal/kibana/synthetics/private_location/read.go index dedd7a8b7..2645f9acb 100644 --- a/internal/kibana/synthetics/private_location/read.go +++ b/internal/kibana/synthetics/private_location/read.go @@ -5,15 +5,13 @@ import ( "errors" "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" + "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { - tflog.Info(ctx, "Read private location") - - kibanaClient := r.getKibanaClient(response.Diagnostics) + kibanaClient := synthetics.GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return } diff --git a/internal/kibana/synthetics/private_location/resource.go b/internal/kibana/synthetics/private_location/resource.go index 0da381cde..c685ca9ea 100644 --- a/internal/kibana/synthetics/private_location/resource.go +++ b/internal/kibana/synthetics/private_location/resource.go @@ -2,10 +2,8 @@ package private_location import ( "context" - "github.com/disaster37/go-kibana-rest/v8" "github.com/elastic/terraform-provider-elasticstack/internal/clients" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" - "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-log/tflog" @@ -17,9 +15,15 @@ const resourceName = synthetics.MetadataPrefix + "private_location" var _ resource.Resource = &Resource{} var _ resource.ResourceWithConfigure = &Resource{} var _ resource.ResourceWithImportState = &Resource{} +var _ synthetics.ESApiClient = &Resource{} type Resource struct { client *clients.ApiClient + synthetics.ESApiClient +} + +func (r *Resource) GetClient() *clients.ApiClient { + return r.client } func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { @@ -27,7 +31,6 @@ func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *res } func (r *Resource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { - tflog.Info(ctx, "Import private location") resource.ImportStatePassthroughID(ctx, path.Root("label"), request, response) } @@ -48,16 +51,3 @@ func (r *Resource) Update(ctx context.Context, _ resource.UpdateRequest, respons "Synthetics private location could only be replaced. Please, note, that only unused locations could be deleted.", ) } - -func (r *Resource) getKibanaClient(dg diag.Diagnostics) *kibana.Client { - if !r.resourceReady(&dg) { - return nil - } - - kibanaClient, err := r.client.GetKibanaClient() - if err != nil { - dg.AddError("unable to get kibana client", err.Error()) - return nil - } - return kibanaClient -} diff --git a/internal/kibana/synthetics/private_location/schema.go b/internal/kibana/synthetics/private_location/schema.go index 9b7316a5a..ebe1fabfc 100644 --- a/internal/kibana/synthetics/private_location/schema.go +++ b/internal/kibana/synthetics/private_location/schema.go @@ -3,7 +3,6 @@ package private_location import ( "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" - "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" @@ -72,18 +71,6 @@ func privateLocationSchema() schema.Schema { } } -func (r *Resource) resourceReady(dg *diag.Diagnostics) bool { - if r.client == nil { - dg.AddError( - "Unconfigured Client", - "Expected configured client. Please report this issue to the provider developers.", - ) - - return false - } - return true -} - func (m *tfModelV0) toPrivateLocation() kbapi.PrivateLocation { var geoConfig *kbapi.SyntheticGeoConfig if m.Geo != nil { diff --git a/internal/kibana/synthetics/resource.go b/internal/kibana/synthetics/resource.go new file mode 100644 index 000000000..b8278ff2a --- /dev/null +++ b/internal/kibana/synthetics/resource.go @@ -0,0 +1,99 @@ +package synthetics + +import ( + "context" + "github.com/disaster37/go-kibana-rest/v8" + "github.com/elastic/terraform-provider-elasticstack/internal/clients" + "github.com/hashicorp/terraform-plugin-framework-validators/resourcevalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +const resourceName = MetadataPrefix + "monitor" + +// Ensure provider defined types fully satisfy framework interfaces +var _ resource.Resource = &Resource{} +var _ resource.ResourceWithConfigure = &Resource{} +var _ resource.ResourceWithImportState = &Resource{} +var _ resource.ResourceWithConfigValidators = &Resource{} + +type ESApiClient interface { + GetClient() *clients.ApiClient +} + +func GetKibanaClient(c ESApiClient, dg diag.Diagnostics) *kibana.Client { + + client := c.GetClient() + if client == nil { + dg.AddError( + "Unconfigured Client", + "Expected configured client. Please report this issue to the provider developers.", + ) + return nil + } + + kibanaClient, err := client.GetKibanaClient() + if err != nil { + dg.AddError("unable to get kibana client", err.Error()) + return nil + } + return kibanaClient +} + +type Resource struct { + client *clients.ApiClient + ESApiClient +} + +func (r *Resource) GetClient() *clients.ApiClient { + return r.client +} +func (r *Resource) ConfigValidators(ctx context.Context) []resource.ConfigValidator { + return []resource.ConfigValidator{ + resourcevalidator.ExactlyOneOf( + path.MatchRoot("http"), + path.MatchRoot("tcp"), + // other monitor config types: icmp, browser + ), + } +} + +func (r *Resource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response) +} + +func (r *Resource) Configure(ctx context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { + client, diags := clients.ConvertProviderData(request.ProviderData) + response.Diagnostics.Append(diags...) + r.client = client +} + +func (r *Resource) Metadata(ctx context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = request.ProviderTypeName + resourceName +} + +func (r *Resource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = monitorConfigSchema() + +} + +func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + //TODO implement me + panic("implement me") +} + +func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + //TODO implement me + panic("implement me") +} + +func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + //TODO implement me + panic("implement me") +} + +func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + //TODO implement me + panic("implement me") +} diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index 91f29aaf6..c2e6a4c1f 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -2,14 +2,246 @@ package synthetics import ( "github.com/disaster37/go-kibana-rest/v8/kbapi" + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" + "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) +//TODO: monitor support is from 8.14.0 + const ( MetadataPrefix = "_kibana_synthetics_" ) +func MonitorScheduleSchema() schema.Attribute { + return schema.Int64Attribute{ + Optional: true, + Computed: true, + MarkdownDescription: "(Optional, number): The monitor’s schedule in minutes. Supported values are 1, 3, 5, 10, 15, 30, 60, 120 and 240.", + Validators: []validator.Int64{ + int64validator.OneOf(1, 3, 5, 10, 15, 30, 60, 120, 240), + }, + PlanModifiers: []planmodifier.Int64{ + int64planmodifier.UseStateForUnknown(), + }, + } +} + +func JsonObjectSchema() schema.Attribute { + return schema.StringAttribute{ + Computed: true, + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + MarkdownDescription: "Raw JSON object, use `jsonencode` function to represent JSON", + CustomType: jsontypes.NormalizedType{}, + } +} + +func StatusConfigSchema() schema.Attribute { + return schema.SingleNestedAttribute{ + Optional: true, + Description: "", + Attributes: map[string]schema.Attribute{ + "enabled": schema.BoolAttribute{ + Optional: true, + Description: "", + }, + }, + } +} + +func MonitorAlertConfigSchema() schema.Attribute { + return schema.SingleNestedAttribute{ + Optional: true, + Description: "", + Attributes: map[string]schema.Attribute{ + "status": StatusConfigSchema(), + "tls": StatusConfigSchema(), + }, + } +} + +func monitorConfigSchema() schema.Schema { + return schema.Schema{ + MarkdownDescription: "Synthetics monitor config, see https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html for more details", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Generated identifier for the monitor", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{ + Optional: false, + Description: "", + }, + "space_id": schema.StringAttribute{ + MarkdownDescription: "An identifier for the space. If space_id is not provided, the default space is used.", + Optional: true, + }, + "schedule": MonitorScheduleSchema(), + "locations": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "", + }, + "private_locations": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "", + }, + "enabled": schema.BoolAttribute{ + Optional: true, + Description: "", + }, + "tags": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + Description: "", + }, + "alert": MonitorAlertConfigSchema(), + "service_name": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "timeout": schema.Int64Attribute{ + Optional: true, + Description: "", + }, + "namespace": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "params": JsonObjectSchema(), + "retest_on_failure": schema.BoolAttribute{ + Optional: true, + Description: "", + }, + "http": HTTPMonitorFieldsSchema(), + "tcp": TCPPMonitorFieldsSchema(), + }, + } +} + +func MonitorScheduleConfigSchema() schema.Attribute { + return schema.SingleNestedAttribute{ + Optional: false, + Description: "", + Attributes: map[string]schema.Attribute{ + "number": schema.StringAttribute{ + Optional: false, + Description: "", + }, + "unit": schema.StringAttribute{ + Optional: false, + Description: "", + }, + }, + } +} + +func MonitorLocationConfigSchema() schema.Attribute { + return schema.SingleNestedAttribute{ + Optional: false, + Description: "", + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Optional: false, + Description: "", + }, + "label": schema.StringAttribute{ + Optional: false, + Description: "", + }, + "geo": GeoConfigSchema(), + "is_service_managed": schema.BoolAttribute{ + Optional: false, + Description: "", + }, + }, + } +} + +func HttpMonitorModeSchema() schema.Attribute { + return schema.StringAttribute{ + Optional: true, + Description: "", + } +} + +func HTTPMonitorFieldsSchema() schema.Attribute { + return schema.SingleNestedAttribute{ + Optional: false, + Description: "", + Attributes: map[string]schema.Attribute{ + "url": schema.StringAttribute{ + Optional: false, + Description: "", + }, + "ssl_setting": JsonObjectSchema(), + "max_redirects": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "mode": HttpMonitorModeSchema(), + "ipv4": schema.BoolAttribute{ + Optional: true, + Description: "", + }, + "ipv6": schema.BoolAttribute{ + Optional: true, + Description: "", + }, + "username": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "password": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "proxy_header": JsonObjectSchema(), + "proxy_url": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "response": JsonObjectSchema(), + "check": JsonObjectSchema(), + }, + } +} + +func TCPPMonitorFieldsSchema() schema.Attribute { + return schema.SingleNestedAttribute{ + Optional: false, + Description: "", + Attributes: map[string]schema.Attribute{ + "host": schema.StringAttribute{ + Optional: false, + Description: "", + }, + "ssh": JsonObjectSchema(), + "check": JsonObjectSchema(), + "proxy_url": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "proxy_use_local_resolver": schema.BoolAttribute{ + Optional: true, + Description: "", + }, + }, + } +} + func GeoConfigSchema() schema.Attribute { return schema.SingleNestedAttribute{ Optional: true, diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go index f141d8bed..9f3041d73 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go @@ -3,9 +3,10 @@ package kbapi import ( "encoding/json" "fmt" + "time" + "github.com/go-resty/resty/v2" log "github.com/sirupsen/logrus" - "time" ) const ( @@ -45,6 +46,10 @@ const ( ModeAny = "any" ) +type MonitorFields interface { + Type() MonitorType +} + type KibanaError struct { Code int `json:"statusCode,omitempty"` Error string `json:"error,omitempty"` @@ -81,6 +86,8 @@ type MonitorAlertConfig struct { Tls *SyntheticsStatusConfig `json:"tls,omitempty"` } +// TODO: TCPMonitorFields + type HTTPMonitorFields struct { Url string `json:"url"` SslSetting JsonObject `json:"ssl,omitempty"` //https://www.elastic.co/guide/en/beats/heartbeat/current/configuration-ssl.html @@ -96,6 +103,10 @@ type HTTPMonitorFields struct { Check JsonObject `json:"check,omitempty"` } +func (f HTTPMonitorFields) Type() MonitorType { + return Http +} + type SyntheticsMonitorConfig struct { Name string `json:"name"` Schedule MonitorSchedule `json:"schedule,omitempty"` @@ -178,12 +189,23 @@ type SyntheticsMonitor struct { Url string `json:"url,omitempty"` Ui struct { IsTlsEnabled bool `json:"is_tls_enabled"` - } `json:"__ui,omitempty"` + } `json:"__ui,omitempty"` //TODO: JsonObject + //TODO: - add following http monitor fields + //check.response.body.positive - array of strings + //check.response.status - array of strings + //check.request.body - object + //check.request.headers - object + //revision - int + //username - string + //password - string + //proxy_url - string + //proxy_headers - object + } -type KibanaSyntheticsMonitorAdd func(config SyntheticsMonitorConfig, fields HTTPMonitorFields, namespace string) (*SyntheticsMonitor, error) +type KibanaSyntheticsMonitorAdd func(config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) -type KibanaSyntheticsMonitorUpdate func(id MonitorID, config SyntheticsMonitorConfig, fields HTTPMonitorFields, namespace string) (*SyntheticsMonitor, error) +type KibanaSyntheticsMonitorUpdate func(id MonitorID, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) type KibanaSyntheticsMonitorGet func(id MonitorID, namespace string) (*SyntheticsMonitor, error) @@ -269,7 +291,7 @@ func newKibanaSyntheticsPrivateLocationCreateFunc(c *resty.Client) KibanaSynthet } func newKibanaSyntheticsMonitorUpdateFunc(c *resty.Client) KibanaSyntheticsMonitorUpdate { - return func(id MonitorID, config SyntheticsMonitorConfig, fields HTTPMonitorFields, namespace string) (*SyntheticsMonitor, error) { + return func(id MonitorID, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) { path := basePathWithId(namespace, monitorsSuffix, id) log.Debugf("URL to update monitor: %s", path) @@ -283,7 +305,7 @@ func newKibanaSyntheticsMonitorUpdateFunc(c *resty.Client) KibanaSyntheticsMonit } func newKibanaSyntheticsMonitorAddFunc(c *resty.Client) KibanaSyntheticsMonitorAdd { - return func(config SyntheticsMonitorConfig, fields HTTPMonitorFields, namespace string) (*SyntheticsMonitor, error) { + return func(config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) { path := basePath(namespace, monitorsSuffix) log.Debugf("URL to create monitor: %s", path) @@ -298,13 +320,13 @@ func newKibanaSyntheticsMonitorAddFunc(c *resty.Client) KibanaSyntheticsMonitorA // current idea here is to switch fields HTTPMonitorFields to interface{} and to // type switch in the function for future monitor types -func buildMonitorJson(config SyntheticsMonitorConfig, fields HTTPMonitorFields) interface{} { +func buildMonitorJson(config SyntheticsMonitorConfig, fields MonitorFields) interface{} { type MonitorTypeConfig struct { Type MonitorType `json:"type"` } - mType := MonitorTypeConfig{Type: Http} + mType := MonitorTypeConfig{Type: fields.Type()} return struct { SyntheticsMonitorConfig @@ -313,7 +335,7 @@ func buildMonitorJson(config SyntheticsMonitorConfig, fields HTTPMonitorFields) }{ config, mType, - fields, + fields.(HTTPMonitorFields), // TODO: is there a better way to do this? } } From dc8e1a92e5c8f2ef1cece2079733d4b6c2a966de Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 6 Aug 2024 15:30:37 +0200 Subject: [PATCH 02/72] tcp and http monitor schema --- internal/kibana/synthetics/resource.go | 5 + internal/kibana/synthetics/schema.go | 243 ++++++++++--------------- 2 files changed, 101 insertions(+), 147 deletions(-) diff --git a/internal/kibana/synthetics/resource.go b/internal/kibana/synthetics/resource.go index b8278ff2a..10d9f1c4f 100644 --- a/internal/kibana/synthetics/resource.go +++ b/internal/kibana/synthetics/resource.go @@ -17,6 +17,7 @@ var _ resource.Resource = &Resource{} var _ resource.ResourceWithConfigure = &Resource{} var _ resource.ResourceWithImportState = &Resource{} var _ resource.ResourceWithConfigValidators = &Resource{} +var _ ESApiClient = &Resource{} type ESApiClient interface { GetClient() *clients.ApiClient @@ -56,6 +57,10 @@ func (r *Resource) ConfigValidators(ctx context.Context) []resource.ConfigValida path.MatchRoot("tcp"), // other monitor config types: icmp, browser ), + resourcevalidator.AtLeastOneOf( + path.MatchRoot("locations"), + path.MatchRoot("private_locations"), + ), } } diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index c2e6a4c1f..80b8ba3af 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -1,11 +1,12 @@ package synthetics import ( + "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -18,59 +19,9 @@ const ( MetadataPrefix = "_kibana_synthetics_" ) -func MonitorScheduleSchema() schema.Attribute { - return schema.Int64Attribute{ - Optional: true, - Computed: true, - MarkdownDescription: "(Optional, number): The monitor’s schedule in minutes. Supported values are 1, 3, 5, 10, 15, 30, 60, 120 and 240.", - Validators: []validator.Int64{ - int64validator.OneOf(1, 3, 5, 10, 15, 30, 60, 120, 240), - }, - PlanModifiers: []planmodifier.Int64{ - int64planmodifier.UseStateForUnknown(), - }, - } -} - -func JsonObjectSchema() schema.Attribute { - return schema.StringAttribute{ - Computed: true, - Optional: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - MarkdownDescription: "Raw JSON object, use `jsonencode` function to represent JSON", - CustomType: jsontypes.NormalizedType{}, - } -} - -func StatusConfigSchema() schema.Attribute { - return schema.SingleNestedAttribute{ - Optional: true, - Description: "", - Attributes: map[string]schema.Attribute{ - "enabled": schema.BoolAttribute{ - Optional: true, - Description: "", - }, - }, - } -} - -func MonitorAlertConfigSchema() schema.Attribute { - return schema.SingleNestedAttribute{ - Optional: true, - Description: "", - Attributes: map[string]schema.Attribute{ - "status": StatusConfigSchema(), - "tls": StatusConfigSchema(), - }, - } -} - func monitorConfigSchema() schema.Schema { return schema.Schema{ - MarkdownDescription: "Synthetics monitor config, see https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html for more details", + MarkdownDescription: "Synthetics monitor config, see https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html for more details. The monitor must have one of the following: http, tcp, icmp or browser.", Attributes: map[string]schema.Attribute{ "id": schema.StringAttribute{ Computed: true, @@ -80,163 +31,161 @@ func monitorConfigSchema() schema.Schema { }, }, "name": schema.StringAttribute{ - Optional: false, - Description: "", + Optional: false, + MarkdownDescription: "The monitor’s name.", }, "space_id": schema.StringAttribute{ - MarkdownDescription: "An identifier for the space. If space_id is not provided, the default space is used.", + MarkdownDescription: "The namespace field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \\, /, ?, \", <, >, |, whitespace, ,, #, :, or -. Default: `default`", Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "schedule": schema.Int64Attribute{ + Optional: true, + Computed: true, + MarkdownDescription: "The monitor’s schedule in minutes. Supported values are 1, 3, 5, 10, 15, 30, 60, 120 and 240.", + Validators: []validator.Int64{ + int64validator.OneOf(1, 3, 5, 10, 15, 30, 60, 120, 240), + }, }, - "schedule": MonitorScheduleSchema(), "locations": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - Description: "", + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "Where to deploy the monitor. Monitors can be deployed in multiple locations so that you can detect differences in availability and response times across those locations.", }, "private_locations": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - Description: "", + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "These Private Locations refer to locations hosted and managed by you, whereas locations are hosted by Elastic. You can specify a Private Location using the location’s name.", }, "enabled": schema.BoolAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "Whether the monitor is enabled. Default: `true`", }, "tags": schema.ListAttribute{ - ElementType: types.StringType, - Optional: true, - Description: "", + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "An array of tags.", }, - "alert": MonitorAlertConfigSchema(), + "alert": monitorAlertConfigSchema(), "service_name": schema.StringAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "The APM service name.", }, "timeout": schema.Int64Attribute{ - Optional: true, - Description: "", - }, - "namespace": schema.StringAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "The monitor timeout in seconds, monitor will fail if it doesn’t complete within this time. Default: `16`", }, - "params": JsonObjectSchema(), + "params": jsonObjectSchema("Monitor parameters"), "retest_on_failure": schema.BoolAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from \"up\" to \"down\". If the result of the retest is also \"down\", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`.", }, - "http": HTTPMonitorFieldsSchema(), - "tcp": TCPPMonitorFieldsSchema(), + "http": httpMonitorFieldsSchema(), + "tcp": tcpMonitorFieldsSchema(), }, } } -func MonitorScheduleConfigSchema() schema.Attribute { - return schema.SingleNestedAttribute{ - Optional: false, - Description: "", - Attributes: map[string]schema.Attribute{ - "number": schema.StringAttribute{ - Optional: false, - Description: "", - }, - "unit": schema.StringAttribute{ - Optional: false, - Description: "", - }, - }, +func jsonObjectSchema(doc string) schema.Attribute { + return schema.StringAttribute{ + Optional: true, + MarkdownDescription: fmt.Sprintf("%s. Raw JSON object, use `jsonencode` function to represent JSON", doc), + CustomType: jsontypes.NormalizedType{}, } } -func MonitorLocationConfigSchema() schema.Attribute { +func statusConfigSchema() schema.Attribute { return schema.SingleNestedAttribute{ - Optional: false, - Description: "", + Optional: true, Attributes: map[string]schema.Attribute{ - "id": schema.StringAttribute{ - Optional: false, - Description: "", - }, - "label": schema.StringAttribute{ - Optional: false, - Description: "", - }, - "geo": GeoConfigSchema(), - "is_service_managed": schema.BoolAttribute{ - Optional: false, - Description: "", + "enabled": schema.BoolAttribute{ + Optional: true, }, }, } } -func HttpMonitorModeSchema() schema.Attribute { - return schema.StringAttribute{ - Optional: true, - Description: "", +func monitorAlertConfigSchema() schema.Attribute { + return schema.SingleNestedAttribute{ + Optional: true, + MarkdownDescription: "Alert configuration. Default: `{ status: { enabled: true }, tls: { enabled: true } }`.", + Attributes: map[string]schema.Attribute{ + "status": statusConfigSchema(), + "tls": statusConfigSchema(), + }, } } -func HTTPMonitorFieldsSchema() schema.Attribute { +func httpMonitorFieldsSchema() schema.Attribute { return schema.SingleNestedAttribute{ - Optional: false, - Description: "", + Optional: true, + MarkdownDescription: "", Attributes: map[string]schema.Attribute{ "url": schema.StringAttribute{ - Optional: false, - Description: "", + Optional: false, + Required: true, + MarkdownDescription: "URL to monitor.", }, - "ssl_setting": JsonObjectSchema(), + "ssl_setting": jsonObjectSchema("The TLS/SSL connection settings for use with the HTTPS endpoint. If you don’t specify settings, the system defaults are used. See https://www.elastic.co/guide/en/beats/heartbeat/current/configuration-ssl.html for full SSL Options."), "max_redirects": schema.StringAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "The maximum number of redirects to follow. Default: `0`", + }, + "mode": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "The mode of the monitor. Can be \"all\" or \"any\". If you’re using a DNS-load balancer and want to ping every IP address for the specified hostname, you should use all.", + Validators: []validator.String{ + stringvalidator.OneOf("any", "all"), + }, }, - "mode": HttpMonitorModeSchema(), "ipv4": schema.BoolAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "Whether to ping using the ipv4 protocol.", }, "ipv6": schema.BoolAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "Whether to ping using the ipv6 protocol.", }, "username": schema.StringAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "The username for authenticating with the server. The credentials are passed with the request.", }, "password": schema.StringAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "The password for authenticating with the server. The credentials are passed with the request.", }, - "proxy_header": JsonObjectSchema(), + "proxy_header": jsonObjectSchema("Additional headers to send to proxies during CONNECT requests."), "proxy_url": schema.StringAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "The URL of the proxy to use for this monitor.", }, - "response": JsonObjectSchema(), - "check": JsonObjectSchema(), + "response": jsonObjectSchema("Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field."), + "check": jsonObjectSchema("The check request settings."), }, } } -func TCPPMonitorFieldsSchema() schema.Attribute { +func tcpMonitorFieldsSchema() schema.Attribute { return schema.SingleNestedAttribute{ - Optional: false, - Description: "", + Optional: true, + MarkdownDescription: "", Attributes: map[string]schema.Attribute{ "host": schema.StringAttribute{ - Optional: false, - Description: "", + Optional: false, + Required: true, + MarkdownDescription: "The host to monitor; it can be an IP address or a hostname. The host can include the port using a colon (e.g., \"example.com:9200\").", }, - "ssh": JsonObjectSchema(), - "check": JsonObjectSchema(), + "ssl": jsonObjectSchema(" The TLS/SSL connection settings for use with the HTTPS endpoint. If you don’t specify settings, the system defaults are used. See https://www.elastic.co/guide/en/beats/heartbeat/current/configuration-ssl.html for full SSL Options."), + "check": jsonObjectSchema("An optional payload string to send to the remote host and the expected answer. If no payload is specified, the endpoint is assumed to be available if the connection attempt was successful. If send is specified without receive, any response is accepted as OK. If receive is specified without send, no payload is sent, but the client expects to receive a payload in the form of a \"hello message\" or \"banner\" on connect."), "proxy_url": schema.StringAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: "The URL of the SOCKS5 proxy to use when connecting to the server. The value must be a URL with a scheme of `socks5://`. If the SOCKS5 proxy server requires client authentication, then a username and password can be embedded in the URL. When using a proxy, hostnames are resolved on the proxy server instead of on the client. You can change this behavior by setting the `proxy_use_local_resolver` option.", }, "proxy_use_local_resolver": schema.BoolAttribute{ - Optional: true, - Description: "", + Optional: true, + MarkdownDescription: " A Boolean value that determines whether hostnames are resolved locally instead of being resolved on the proxy server. The default value is false, which means that name resolution occurs on the proxy server.", }, }, } From a76a5a0b2ec25ef0acaf9e545c5f342de80a6812 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 6 Aug 2024 17:55:15 +0200 Subject: [PATCH 03/72] implement kibana api for tcp monitor --- .../kbapi/api.kibana_synthetics.go | 90 ++++++++++--------- .../kbapi/api.kibana_synthetics_test.go | 79 +++++++++++++++- 2 files changed, 126 insertions(+), 43 deletions(-) diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go index 9f3041d73..23312e33f 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go @@ -47,7 +47,7 @@ const ( ) type MonitorFields interface { - Type() MonitorType + APIRequest(cfg SyntheticsMonitorConfig) interface{} } type KibanaError struct { @@ -86,7 +86,13 @@ type MonitorAlertConfig struct { Tls *SyntheticsStatusConfig `json:"tls,omitempty"` } -// TODO: TCPMonitorFields +type TCPMonitorFields struct { + Host string `json:"host"` + SslSetting JsonObject `json:"ssl,omitempty"` //https://www.elastic.co/guide/en/beats/heartbeat/current/configuration-ssl.html + Check JsonObject `json:"check,omitempty"` + ProxyUrl string `json:"proxy_url,omitempty"` + ProxyUseLocalResolver *bool `json:"proxy_use_local_resolver,omitempty"` +} type HTTPMonitorFields struct { Url string `json:"url"` @@ -103,8 +109,38 @@ type HTTPMonitorFields struct { Check JsonObject `json:"check,omitempty"` } -func (f HTTPMonitorFields) Type() MonitorType { - return Http +type MonitorTypeConfig struct { + Type MonitorType `json:"type"` +} + +func (f HTTPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} { + + mType := MonitorTypeConfig{Type: Http} + + return struct { + SyntheticsMonitorConfig + MonitorTypeConfig + HTTPMonitorFields + }{ + config, + mType, + f, + } +} + +func (f TCPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} { + + mType := MonitorTypeConfig{Type: Tcp} + + return struct { + SyntheticsMonitorConfig + MonitorTypeConfig + TCPMonitorFields + }{ + config, + mType, + f, + } } type SyntheticsMonitorConfig struct { @@ -179,7 +215,6 @@ type SyntheticsMonitor struct { MaxRedirects string `json:"max_redirects"` ResponseIncludeBody string `json:"response.include_body"` ResponseIncludeHeaders bool `json:"response.include_headers"` - CheckRequestMethod string `json:"check.request.method"` ResponseIncludeBodyMaxBytes string `json:"response.include_body_max_bytes,omitempty"` Ipv4 bool `json:"ipv4,omitempty"` Ipv6 bool `json:"ipv6,omitempty"` @@ -189,18 +224,14 @@ type SyntheticsMonitor struct { Url string `json:"url,omitempty"` Ui struct { IsTlsEnabled bool `json:"is_tls_enabled"` - } `json:"__ui,omitempty"` //TODO: JsonObject - //TODO: - add following http monitor fields - //check.response.body.positive - array of strings - //check.response.status - array of strings - //check.request.body - object - //check.request.headers - object - //revision - int - //username - string - //password - string - //proxy_url - string - //proxy_headers - object - + } `json:"__ui,omitempty"` + ProxyUrl string `json:"proxy_url,omitempty"` + ProxyUseLocalResolver bool `json:"proxy_use_local_resolver,omitempty"` + Host string `json:"host,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + ProxyHeaders JsonObject `json:"proxy_headers,omitempty"` + Check JsonObject `json:"check,omitempty"` } type KibanaSyntheticsMonitorAdd func(config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) @@ -295,7 +326,7 @@ func newKibanaSyntheticsMonitorUpdateFunc(c *resty.Client) KibanaSyntheticsMonit path := basePathWithId(namespace, monitorsSuffix, id) log.Debugf("URL to update monitor: %s", path) - data := buildMonitorJson(config, fields) + data := fields.APIRequest(config) resp, err := c.R().SetBody(data).Put(path) if err := handleKibanaError(err, resp); err != nil { return nil, err @@ -309,7 +340,7 @@ func newKibanaSyntheticsMonitorAddFunc(c *resty.Client) KibanaSyntheticsMonitorA path := basePath(namespace, monitorsSuffix) log.Debugf("URL to create monitor: %s", path) - data := buildMonitorJson(config, fields) + data := fields.APIRequest(config) resp, err := c.R().SetBody(data).Post(path) if err := handleKibanaError(err, resp); err != nil { return nil, err @@ -318,27 +349,6 @@ func newKibanaSyntheticsMonitorAddFunc(c *resty.Client) KibanaSyntheticsMonitorA } } -// current idea here is to switch fields HTTPMonitorFields to interface{} and to -// type switch in the function for future monitor types -func buildMonitorJson(config SyntheticsMonitorConfig, fields MonitorFields) interface{} { - - type MonitorTypeConfig struct { - Type MonitorType `json:"type"` - } - - mType := MonitorTypeConfig{Type: fields.Type()} - - return struct { - SyntheticsMonitorConfig - MonitorTypeConfig - HTTPMonitorFields - }{ - config, - mType, - fields.(HTTPMonitorFields), // TODO: is there a better way to do this? - } -} - func unmarshal[T interface{}](resp *resty.Response, result T) (*T, error) { respBody := resp.Body() err := json.Unmarshal(respBody, &result) diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go index b3341653d..15bfe0495 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go @@ -54,7 +54,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { type TestConfig struct { config SyntheticsMonitorConfig - fields HTTPMonitorFields + fields MonitorFields } for _, n := range namespaces { @@ -101,6 +101,24 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { }, }, }, + { + name: "bare minimum tcp monitor", + input: TestConfig{ + config: SyntheticsMonitorConfig{ + Name: fmt.Sprintf("test synthetics tcp monitor %s", testUuid), + PrivateLocations: []string{location.Label}, + }, + fields: TCPMonitorFields{ + Host: "localhost:5601", + }, + }, + update: TestConfig{ + config: SyntheticsMonitorConfig{}, + fields: TCPMonitorFields{ + Host: "localhost:9200", + }, + }, + }, { name: "all fields http monitor", input: TestConfig{ @@ -170,6 +188,53 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { }, }, }, + { + name: "all fields tcp monitor", + input: TestConfig{ + config: SyntheticsMonitorConfig{ + Name: fmt.Sprintf("test all fields tcp monitor %s", testUuid), + Schedule: Every10Minutes, + PrivateLocations: []string{location.Label}, + Enabled: f, + Tags: []string{"aaa", "bbb"}, + Alert: &MonitorAlertConfig{ + Status: &SyntheticsStatusConfig{Enabled: t}, + Tls: &SyntheticsStatusConfig{Enabled: f}, + }, + APMServiceName: "APMServiceName", + TimeoutSeconds: 42, + Namespace: space, + Params: map[string]interface{}{ + "param1": "some-params", + "my_url": "http://localhost:8080", + }, + RetestOnFailure: f, + }, + fields: TCPMonitorFields{ + Host: "localhost:5601", + SslSetting: map[string]interface{}{ + "supported_protocols": []string{"TLSv1.0", "TLSv1.1", "TLSv1.2"}, + }, + ProxyUseLocalResolver: t, + ProxyUrl: "http://localhost", + Check: map[string]interface{}{ + "send": "Hello World", + "receive": "Hello", + }, + }, + }, + update: TestConfig{ + config: SyntheticsMonitorConfig{ + Name: fmt.Sprintf("update all fields tcp monitor %s", testUuid), + Schedule: Every30Minutes, + }, + fields: TCPMonitorFields{ + Host: "localhost:9200", + ProxyUrl: "http://127.0.0.1", + ProxyUseLocalResolver: f, + }, + }, + }, } for _, tc := range testCases { @@ -180,7 +245,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { monitor, err := syntheticsAPI.Monitor.Add(config, fields, space) assert.NoError(s.T(), err) assert.NotNil(s.T(), monitor) - monitor.Params = nil //kibana API doesn't return params for GET request + updateDueToKibanaAPIDiff(monitor) get, err := syntheticsAPI.Monitor.Get(monitor.Id, space) assert.NoError(s.T(), err) @@ -193,7 +258,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { update, err := syntheticsAPI.Monitor.Update(monitor.Id, tc.update.config, tc.update.fields, space) assert.NoError(s.T(), err) assert.NotNil(s.T(), update) - update.Params = nil //kibana API doesn't return params for GET request + updateDueToKibanaAPIDiff(update) get, err = syntheticsAPI.Monitor.Get(monitor.ConfigId, space) assert.NoError(s.T(), err) @@ -217,6 +282,14 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { } } +// see https://github.com/elastic/kibana/issues/189906 +func updateDueToKibanaAPIDiff(m *SyntheticsMonitor) { + m.Params = nil + m.Username = "" + m.Password = "" + m.ProxyHeaders = nil +} + func (s *KBAPITestSuite) TestKibanaSyntheticsPrivateLocationAPI() { for _, n := range namespaces { From 3d747c1ff05989b5449a20d88467e61d421be501 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 6 Aug 2024 18:28:05 +0200 Subject: [PATCH 04/72] add tfModelV0 model for synthetic monitors --- internal/kibana/synthetics/schema.go | 66 ++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index 80b8ba3af..a032dfa26 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -19,6 +19,61 @@ const ( MetadataPrefix = "_kibana_synthetics_" ) +type kibanaAPIRequest struct { + fields kbapi.MonitorFields + config kbapi.SyntheticsMonitorConfig +} + +type tfStatusConfigV0 struct { + Enabled types.Bool `tfsdk:"enabled"` +} + +type tfAlertConfigV0 struct { + Status *tfStatusConfigV0 `tfsdk:"status"` + TLS *tfStatusConfigV0 `tfsdk:"tls"` +} + +type tfHTTPMonitorFieldsV0 struct { + URL types.String `tfsdk:"url"` + SSLSetting jsontypes.Normalized `tfsdk:"ssl_setting"` + MaxRedirects types.String `tfsdk:"max_redirects"` + Mode types.String `tfsdk:"mode"` + IPv4 types.Bool `tfsdk:"ipv4"` + IPv6 types.Bool `tfsdk:"ipv6"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` + ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` + ProxyURL types.String `tfsdk:"proxy_url"` + Response jsontypes.Normalized `tfsdk:"response"` + Check jsontypes.Normalized `tfsdk:"check"` +} + +type tfTCPMonitorFieldsV0 struct { + Host types.String `tfsdk:"host"` + SSL jsontypes.Normalized `tfsdk:"ssl"` + Check jsontypes.Normalized `tfsdk:"check"` + ProxyURL types.String `tfsdk:"proxy_url"` + ProxyUseLocalResolver types.Bool `tfsdk:"proxy_use_local_resolver"` +} + +type tfModelV0 struct { + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + SpaceID types.String `tfsdk:"space_id"` + Schedule types.Int64 `tfsdk:"schedule"` + Locations []types.String `tfsdk:"locations"` + PrivateLocations []types.String `tfsdk:"private_locations"` + Enabled types.Bool `tfsdk:"enabled"` + Tags []types.String `tfsdk:"tags"` + Alert *tfAlertConfigV0 `tfsdk:"alert"` + ServiceName types.String `tfsdk:"service_name"` + Timeout types.Int64 `tfsdk:"timeout"` + Params jsontypes.Normalized `tfsdk:"params"` + RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` + HTTP *tfHTTPMonitorFieldsV0 `tfsdk:"http"` + TCP *tfTCPMonitorFieldsV0 `tfsdk:"tcp"` +} + func monitorConfigSchema() schema.Schema { return schema.Schema{ MarkdownDescription: "Synthetics monitor config, see https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html for more details. The monitor must have one of the following: http, tcp, icmp or browser.", @@ -231,3 +286,14 @@ func FromSyntheticGeoConfig(v *kbapi.SyntheticGeoConfig) *TFGeoConfigV0 { Lon: types.Float64Value(v.Lon), } } + +func (m *tfModelV0) toPrivateLocation() kibanaAPIRequest { + return kibanaAPIRequest{ + fields: kbapi.HTTPMonitorFields{}, + config: kbapi.SyntheticsMonitorConfig{}, + } +} + +func toModelV0(api kbapi.SyntheticsMonitor) tfModelV0 { + return tfModelV0{} +} From ed50e445c68a7bb02ff3d63335494d13ea021b83 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Wed, 7 Aug 2024 12:41:54 +0200 Subject: [PATCH 05/72] allign monitor tf schema to API --- .../synthetics/private_location/schema.go | 12 +- internal/kibana/synthetics/schema.go | 197 ++++++++++++++++-- .../kbapi/api.kibana_synthetics.go | 143 +++++++------ .../kbapi/api.kibana_synthetics_test.go | 45 ++-- 4 files changed, 275 insertions(+), 122 deletions(-) diff --git a/internal/kibana/synthetics/private_location/schema.go b/internal/kibana/synthetics/private_location/schema.go index ebe1fabfc..35648f57f 100644 --- a/internal/kibana/synthetics/private_location/schema.go +++ b/internal/kibana/synthetics/private_location/schema.go @@ -77,14 +77,10 @@ func (m *tfModelV0) toPrivateLocation() kbapi.PrivateLocation { geoConfig = m.Geo.ToSyntheticGeoConfig() } - var tags []string - for _, tag := range m.Tags { - tags = append(tags, tag.ValueString()) - } pLoc := kbapi.PrivateLocationConfig{ Label: m.Label.ValueString(), AgentPolicyId: m.AgentPolicyId.ValueString(), - Tags: tags, + Tags: synthetics.ValueStringSlice(m.Tags), Geo: geoConfig, } @@ -96,16 +92,12 @@ func (m *tfModelV0) toPrivateLocation() kbapi.PrivateLocation { } func toModelV0(pLoc kbapi.PrivateLocation) tfModelV0 { - var tags []types.String - for _, tag := range pLoc.Tags { - tags = append(tags, types.StringValue(tag)) - } return tfModelV0{ ID: types.StringValue(pLoc.Id), Label: types.StringValue(pLoc.Label), SpaceID: types.StringValue(pLoc.Namespace), AgentPolicyId: types.StringValue(pLoc.AgentPolicyId), - Tags: tags, + Tags: synthetics.StringSliceValue(pLoc.Tags), Geo: synthetics.FromSyntheticGeoConfig(pLoc.Geo), } } diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index a032dfa26..35ad53ffb 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -1,16 +1,19 @@ package synthetics import ( + "encoding/json" "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" + "strconv" ) //TODO: monitor support is from 8.14.0 @@ -34,26 +37,29 @@ type tfAlertConfigV0 struct { } type tfHTTPMonitorFieldsV0 struct { - URL types.String `tfsdk:"url"` - SSLSetting jsontypes.Normalized `tfsdk:"ssl_setting"` - MaxRedirects types.String `tfsdk:"max_redirects"` - Mode types.String `tfsdk:"mode"` - IPv4 types.Bool `tfsdk:"ipv4"` - IPv6 types.Bool `tfsdk:"ipv6"` - Username types.String `tfsdk:"username"` - Password types.String `tfsdk:"password"` - ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` - ProxyURL types.String `tfsdk:"proxy_url"` - Response jsontypes.Normalized `tfsdk:"response"` - Check jsontypes.Normalized `tfsdk:"check"` + URL types.String `tfsdk:"url"` + SslVerificationMode types.String `tfsdk:"ssl.verification_mode"` + SslSupportedProtocols []types.String `tfsdk:"ssl.supported_protocols"` + MaxRedirects types.String `tfsdk:"max_redirects"` + Mode types.String `tfsdk:"mode"` + IPv4 types.Bool `tfsdk:"ipv4"` + IPv6 types.Bool `tfsdk:"ipv6"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` + ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` + ProxyURL types.String `tfsdk:"proxy_url"` + Response jsontypes.Normalized `tfsdk:"response"` + Check jsontypes.Normalized `tfsdk:"check"` } type tfTCPMonitorFieldsV0 struct { - Host types.String `tfsdk:"host"` - SSL jsontypes.Normalized `tfsdk:"ssl"` - Check jsontypes.Normalized `tfsdk:"check"` - ProxyURL types.String `tfsdk:"proxy_url"` - ProxyUseLocalResolver types.Bool `tfsdk:"proxy_use_local_resolver"` + Host types.String `tfsdk:"host"` + SslVerificationMode types.String `tfsdk:"ssl.verification_mode"` + SslSupportedProtocols []types.String `tfsdk:"ssl.supported_protocols"` + CheckSend types.String `tfsdk:"check.send"` + CheckReceive types.String `tfsdk:"check.receive"` + ProxyURL types.String `tfsdk:"proxy_url"` + ProxyUseLocalResolver types.Bool `tfsdk:"proxy_use_local_resolver"` } type tfModelV0 struct { @@ -66,12 +72,12 @@ type tfModelV0 struct { Enabled types.Bool `tfsdk:"enabled"` Tags []types.String `tfsdk:"tags"` Alert *tfAlertConfigV0 `tfsdk:"alert"` - ServiceName types.String `tfsdk:"service_name"` - Timeout types.Int64 `tfsdk:"timeout"` + APMServiceName types.String `tfsdk:"service_name"` + TimeoutSeconds types.Int64 `tfsdk:"timeout"` Params jsontypes.Normalized `tfsdk:"params"` - RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` HTTP *tfHTTPMonitorFieldsV0 `tfsdk:"http"` TCP *tfTCPMonitorFieldsV0 `tfsdk:"tcp"` + RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` } func monitorConfigSchema() schema.Schema { @@ -108,6 +114,22 @@ func monitorConfigSchema() schema.Schema { ElementType: types.StringType, Optional: true, MarkdownDescription: "Where to deploy the monitor. Monitors can be deployed in multiple locations so that you can detect differences in availability and response times across those locations.", + Validators: []validator.List{ + listvalidator.ValueStringsAre( + stringvalidator.OneOf( + "japan", + "india", + "singapore", + "australia_east", + "united_kingdom", + "germany", + "canada_east", + "brazil", + "us_east", + "us_west", + ), + ), + }, }, "private_locations": schema.ListAttribute{ ElementType: types.StringType, @@ -287,6 +309,22 @@ func FromSyntheticGeoConfig(v *kbapi.SyntheticGeoConfig) *TFGeoConfigV0 { } } +func ValueStringSlice(v []types.String) []string { + var res []string + for _, s := range v { + res = append(res, s.ValueString()) + } + return res +} + +func StringSliceValue(v []string) []types.String { + var res []types.String + for _, s := range v { + res = append(res, types.StringValue(s)) + } + return res +} + func (m *tfModelV0) toPrivateLocation() kibanaAPIRequest { return kibanaAPIRequest{ fields: kbapi.HTTPMonitorFields{}, @@ -294,6 +332,121 @@ func (m *tfModelV0) toPrivateLocation() kibanaAPIRequest { } } -func toModelV0(api kbapi.SyntheticsMonitor) tfModelV0 { - return tfModelV0{} +func toNormalizedValue(jsObj kbapi.JsonObject) (jsontypes.Normalized, error) { + res, err := json.Marshal(jsObj) + if err != nil { + return jsontypes.NewNormalizedUnknown(), err + } + return jsontypes.NewNormalizedValue(string(res)), nil +} + +func toModelV0(api kbapi.SyntheticsMonitor) (*tfModelV0, error) { + schedule, err := strconv.ParseInt(api.Schedule.Number, 10, 34) + if err != nil { + return nil, err + } + var locLabels []string + var privateLocLabels []string + for _, l := range api.Locations { + if l.IsServiceManaged { + locLabels = append(locLabels, l.Label) + } else { + privateLocLabels = append(privateLocLabels, l.Label) + } + } + + timeout, err := api.Timeout.Int64() + if err != nil { + return nil, err + } + + var http *tfHTTPMonitorFieldsV0 + var tcp *tfTCPMonitorFieldsV0 + switch mType := api.Type; mType { + case kbapi.Http: + http, err = toTfHTTPMonitorFieldsV0(api) + case kbapi.Tcp: + tcp, err = toTfTCPMonitorFieldsV0(api) + default: + err = fmt.Errorf("unsupported monitor type: %s", mType) + } + + if err != nil { + return nil, err + } + + params, err := toNormalizedValue(api.Params) + if err != nil { + return nil, err + } + + return &tfModelV0{ + ID: types.StringValue(string(api.Id)), + Name: types.StringValue(api.Name), + SpaceID: types.StringValue(api.Namespace), + Schedule: types.Int64Value(schedule), + Locations: StringSliceValue(locLabels), + PrivateLocations: StringSliceValue(privateLocLabels), + Enabled: types.BoolPointerValue(api.Enabled), + Tags: StringSliceValue(api.Tags), + Alert: toTfAlertConfigV0(api.Alert), + APMServiceName: types.StringValue(api.APMServiceName), + TimeoutSeconds: types.Int64Value(timeout), + Params: params, + HTTP: http, + TCP: tcp, + }, nil +} + +func toTfTCPMonitorFieldsV0(api kbapi.SyntheticsMonitor) (*tfTCPMonitorFieldsV0, error) { + return &tfTCPMonitorFieldsV0{ + Host: types.StringValue(api.Host), + SslVerificationMode: types.StringValue(api.SslVerificationMode), + SslSupportedProtocols: StringSliceValue(api.SslSupportedProtocols), + CheckSend: types.StringValue(api.CheckSend), + CheckReceive: types.StringValue(api.CheckReceive), + ProxyURL: types.StringValue(api.ProxyUrl), + ProxyUseLocalResolver: types.BoolPointerValue(api.ProxyUseLocalResolver), + }, nil +} + +func toTfHTTPMonitorFieldsV0(api kbapi.SyntheticsMonitor) (*tfHTTPMonitorFieldsV0, error) { + + proxyHeaders, err := toNormalizedValue(api.ProxyHeaders) + if err != nil { + return nil, err + } + + return &tfHTTPMonitorFieldsV0{ + URL: types.StringValue(api.Url), + SslVerificationMode: types.StringValue(api.SslVerificationMode), + SslSupportedProtocols: StringSliceValue(api.SslSupportedProtocols), + MaxRedirects: types.StringValue(api.MaxRedirects), + Mode: types.StringValue(string(api.Mode)), + IPv4: types.BoolPointerValue(api.Ipv4), + IPv6: types.BoolPointerValue(api.Ipv6), + Username: types.StringValue(api.Username), + Password: types.StringValue(api.Password), + ProxyHeader: proxyHeaders, + ProxyURL: types.StringValue(api.ProxyUrl), + }, nil +} + +func toTfAlertConfigV0(alert *kbapi.MonitorAlertConfig) *tfAlertConfigV0 { + if alert == nil { + return nil + } + return &tfAlertConfigV0{ + Status: toTfStatusConfigV0(alert.Status), + TLS: toTfStatusConfigV0(alert.Tls), + } +} + +func toTfStatusConfigV0(status *kbapi.SyntheticsStatusConfig) *tfStatusConfigV0 { + if status == nil { + return nil + } + return &tfStatusConfigV0{ + Enabled: types.BoolPointerValue(status.Enabled), + } } diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go index 23312e33f..cafd68e3b 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go @@ -87,60 +87,29 @@ type MonitorAlertConfig struct { } type TCPMonitorFields struct { - Host string `json:"host"` - SslSetting JsonObject `json:"ssl,omitempty"` //https://www.elastic.co/guide/en/beats/heartbeat/current/configuration-ssl.html - Check JsonObject `json:"check,omitempty"` - ProxyUrl string `json:"proxy_url,omitempty"` - ProxyUseLocalResolver *bool `json:"proxy_use_local_resolver,omitempty"` + Host string `json:"host"` + SslVerificationMode string `json:"ssl.verification_mode,omitempty"` + SslSupportedProtocols []string `json:"ssl.supported_protocols,omitempty"` + CheckSend string `json:"check.send,omitempty"` + CheckReceive string `json:"check.receive,omitempty"` + ProxyUrl string `json:"proxy_url,omitempty"` + ProxyUseLocalResolver *bool `json:"proxy_use_local_resolver,omitempty"` } type HTTPMonitorFields struct { - Url string `json:"url"` - SslSetting JsonObject `json:"ssl,omitempty"` //https://www.elastic.co/guide/en/beats/heartbeat/current/configuration-ssl.html - MaxRedirects string `json:"max_redirects,omitempty"` - Mode HttpMonitorMode `json:"mode,omitempty"` - Ipv4 *bool `json:"ipv4,omitempty"` - Ipv6 *bool `json:"ipv6,omitempty"` - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` - ProxyHeader JsonObject `json:"proxy_headers,omitempty"` - ProxyUrl string `json:"proxy_url,omitempty"` - Response JsonObject `json:"response,omitempty"` - Check JsonObject `json:"check,omitempty"` -} - -type MonitorTypeConfig struct { - Type MonitorType `json:"type"` -} - -func (f HTTPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} { - - mType := MonitorTypeConfig{Type: Http} - - return struct { - SyntheticsMonitorConfig - MonitorTypeConfig - HTTPMonitorFields - }{ - config, - mType, - f, - } -} - -func (f TCPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} { - - mType := MonitorTypeConfig{Type: Tcp} - - return struct { - SyntheticsMonitorConfig - MonitorTypeConfig - TCPMonitorFields - }{ - config, - mType, - f, - } + Url string `json:"url"` + SslVerificationMode string `json:"ssl.verification_mode,omitempty"` + SslSupportedProtocols []string `json:"ssl.supported_protocols,omitempty"` + MaxRedirects string `json:"max_redirects,omitempty"` + Mode HttpMonitorMode `json:"mode,omitempty"` + Ipv4 *bool `json:"ipv4,omitempty"` + Ipv6 *bool `json:"ipv6,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + ProxyHeader JsonObject `json:"proxy_headers,omitempty"` + ProxyUrl string `json:"proxy_url,omitempty"` + Response JsonObject `json:"response,omitempty"` + Check JsonObject `json:"check,omitempty"` } type SyntheticsMonitorConfig struct { @@ -213,25 +182,63 @@ type SyntheticsMonitor struct { Params JsonObject `json:"params,omitempty"` MaxAttempts int `json:"max_attempts"` MaxRedirects string `json:"max_redirects"` - ResponseIncludeBody string `json:"response.include_body"` - ResponseIncludeHeaders bool `json:"response.include_headers"` - ResponseIncludeBodyMaxBytes string `json:"response.include_body_max_bytes,omitempty"` - Ipv4 bool `json:"ipv4,omitempty"` - Ipv6 bool `json:"ipv6,omitempty"` - SslVerificationMode string `json:"ssl.verification_mode,omitempty"` - SslSupportedProtocols []string `json:"ssl.supported_protocols,omitempty"` + Ipv4 *bool `json:"ipv4,omitempty"` + Ipv6 *bool `json:"ipv6,omitempty"` + SslVerificationMode string `json:"ssl.verification_mode"` + SslSupportedProtocols []string `json:"ssl.supported_protocols"` Revision int `json:"revision,omitempty"` Url string `json:"url,omitempty"` - Ui struct { - IsTlsEnabled bool `json:"is_tls_enabled"` - } `json:"__ui,omitempty"` - ProxyUrl string `json:"proxy_url,omitempty"` - ProxyUseLocalResolver bool `json:"proxy_use_local_resolver,omitempty"` - Host string `json:"host,omitempty"` - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` - ProxyHeaders JsonObject `json:"proxy_headers,omitempty"` - Check JsonObject `json:"check,omitempty"` + Ui JsonObject `json:"__ui,omitempty"` + ProxyUrl string `json:"proxy_url,omitempty"` + ProxyUseLocalResolver *bool `json:"proxy_use_local_resolver,omitempty"` + Host string `json:"host,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + ProxyHeaders JsonObject `json:"proxy_headers,omitempty"` + CheckSend string `json:"check.send,omitempty"` + CheckReceive string `json:"check.receive,omitempty"` + CheckResponseBodyPositive []string `json:"check.response.body.positive,omitempty"` + CheckResponseStatus []string `json:"check.response.status,omitempty"` + ResponseIncludeBody string `json:"response.include_body,omitempty"` + ResponseIncludeHeaders bool `json:"response.include_headers,omitempty"` + ResponseIncludeBodyMaxBytes string `json:"response.include_body_max_bytes,omitempty"` + CheckRequestBody JsonObject `json:"check.request.body,omitempty"` + CheckRequestHeaders JsonObject `json:"check.request.headers,omitempty"` + CheckRequestMethod string `json:"check.request.method,omitempty"` +} + +type MonitorTypeConfig struct { + Type MonitorType `json:"type"` +} + +func (f HTTPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} { + + mType := MonitorTypeConfig{Type: Http} + + return struct { + SyntheticsMonitorConfig + MonitorTypeConfig + HTTPMonitorFields + }{ + config, + mType, + f, + } +} + +func (f TCPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} { + + mType := MonitorTypeConfig{Type: Tcp} + + return struct { + SyntheticsMonitorConfig + MonitorTypeConfig + TCPMonitorFields + }{ + config, + mType, + f, + } } type KibanaSyntheticsMonitorAdd func(config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go index 15bfe0495..f5ebb7ab6 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go @@ -57,7 +57,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { fields MonitorFields } - for _, n := range namespaces { + for _, n := range []string{""} { //namespaces testUuid := uuid.New().String() space := n syntheticsAPI := s.API.KibanaSynthetics @@ -142,16 +142,15 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { RetestOnFailure: f, }, fields: HTTPMonitorFields{ - Url: "http://localhost:5601", - SslSetting: map[string]interface{}{ - "supported_protocols": []string{"TLSv1.0", "TLSv1.1", "TLSv1.2"}, - }, - MaxRedirects: "2", - Mode: ModeAny, - Ipv4: t, - Ipv6: f, - Username: "test-user-name", - Password: "test-password", + Url: "http://localhost:5601", + SslSupportedProtocols: []string{"TLSv1.0", "TLSv1.1", "TLSv1.2"}, + SslVerificationMode: "full", + MaxRedirects: "2", + Mode: ModeAny, + Ipv4: t, + Ipv6: f, + Username: "test-user-name", + Password: "test-password", ProxyHeader: map[string]interface{}{ "User-Agent": "test", }, @@ -211,16 +210,13 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { RetestOnFailure: f, }, fields: TCPMonitorFields{ - Host: "localhost:5601", - SslSetting: map[string]interface{}{ - "supported_protocols": []string{"TLSv1.0", "TLSv1.1", "TLSv1.2"}, - }, + Host: "localhost:5601", + SslSupportedProtocols: []string{"TLSv1.0", "TLSv1.1", "TLSv1.2"}, + SslVerificationMode: "full", ProxyUseLocalResolver: t, ProxyUrl: "http://localhost", - Check: map[string]interface{}{ - "send": "Hello World", - "receive": "Hello", - }, + CheckSend: "Hello World", + CheckReceive: "Hello", }, }, update: TestConfig{ @@ -245,7 +241,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { monitor, err := syntheticsAPI.Monitor.Add(config, fields, space) assert.NoError(s.T(), err) assert.NotNil(s.T(), monitor) - updateDueToKibanaAPIDiff(monitor) + updateDueToKibanaAPIDiff(monitor, fields) get, err := syntheticsAPI.Monitor.Get(monitor.Id, space) assert.NoError(s.T(), err) @@ -258,7 +254,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { update, err := syntheticsAPI.Monitor.Update(monitor.Id, tc.update.config, tc.update.fields, space) assert.NoError(s.T(), err) assert.NotNil(s.T(), update) - updateDueToKibanaAPIDiff(update) + updateDueToKibanaAPIDiff(update, fields) get, err = syntheticsAPI.Monitor.Get(monitor.ConfigId, space) assert.NoError(s.T(), err) @@ -283,11 +279,16 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { } // see https://github.com/elastic/kibana/issues/189906 -func updateDueToKibanaAPIDiff(m *SyntheticsMonitor) { +func updateDueToKibanaAPIDiff(m *SyntheticsMonitor, f MonitorFields) { m.Params = nil m.Username = "" m.Password = "" m.ProxyHeaders = nil + m.CheckResponseBodyPositive = nil + m.CheckRequestBody = nil + m.CheckRequestHeaders = nil + m.CheckSend = "" + m.CheckReceive = "" } func (s *KBAPITestSuite) TestKibanaSyntheticsPrivateLocationAPI() { From 4a2419ca4b1c7ca34a7a2a0ad77b49bf06efac8e Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Wed, 7 Aug 2024 12:48:22 +0200 Subject: [PATCH 06/72] implement read resource --- internal/kibana/synthetics/read.go | 51 ++++++++++++++++++++++++++ internal/kibana/synthetics/resource.go | 5 --- internal/kibana/synthetics/schema.go | 13 ++----- 3 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 internal/kibana/synthetics/read.go diff --git a/internal/kibana/synthetics/read.go b/internal/kibana/synthetics/read.go new file mode 100644 index 000000000..898966006 --- /dev/null +++ b/internal/kibana/synthetics/read.go @@ -0,0 +1,51 @@ +package synthetics + +import ( + "context" + "errors" + "fmt" + "github.com/disaster37/go-kibana-rest/v8/kbapi" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + + kibanaClient := GetKibanaClient(r, response.Diagnostics) + if kibanaClient == nil { + return + } + + var state *tfModelV0 + diags := request.State.Get(ctx, state) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + namespace := state.SpaceID.ValueString() + monitorId := state.ID.ValueString() + result, err := kibanaClient.KibanaSynthetics.Monitor.Get(kbapi.MonitorID(monitorId), namespace) + if err != nil { + var apiError *kbapi.APIError + if errors.As(err, &apiError) && apiError.Code == 404 { + response.State.RemoveResource(ctx) + return + } + + response.Diagnostics.AddError(fmt.Sprintf("Failed to get monitor `%s`, namespace %s", monitorId, namespace), err.Error()) + return + } + + state, err = toModelV0(result) + if err != nil { + response.Diagnostics.AddError("Failed to convert Kibana monitor API to TF state", err.Error()) + return + } + + // Set refreshed state + diags = response.State.Set(ctx, state) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } +} diff --git a/internal/kibana/synthetics/resource.go b/internal/kibana/synthetics/resource.go index 10d9f1c4f..23970b7db 100644 --- a/internal/kibana/synthetics/resource.go +++ b/internal/kibana/synthetics/resource.go @@ -88,11 +88,6 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r panic("implement me") } -func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { - //TODO implement me - panic("implement me") -} - func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { //TODO implement me panic("implement me") diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index 35ad53ffb..58807e8f7 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -325,13 +325,6 @@ func StringSliceValue(v []string) []types.String { return res } -func (m *tfModelV0) toPrivateLocation() kibanaAPIRequest { - return kibanaAPIRequest{ - fields: kbapi.HTTPMonitorFields{}, - config: kbapi.SyntheticsMonitorConfig{}, - } -} - func toNormalizedValue(jsObj kbapi.JsonObject) (jsontypes.Normalized, error) { res, err := json.Marshal(jsObj) if err != nil { @@ -340,7 +333,7 @@ func toNormalizedValue(jsObj kbapi.JsonObject) (jsontypes.Normalized, error) { return jsontypes.NewNormalizedValue(string(res)), nil } -func toModelV0(api kbapi.SyntheticsMonitor) (*tfModelV0, error) { +func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { schedule, err := strconv.ParseInt(api.Schedule.Number, 10, 34) if err != nil { return nil, err @@ -398,7 +391,7 @@ func toModelV0(api kbapi.SyntheticsMonitor) (*tfModelV0, error) { }, nil } -func toTfTCPMonitorFieldsV0(api kbapi.SyntheticsMonitor) (*tfTCPMonitorFieldsV0, error) { +func toTfTCPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfTCPMonitorFieldsV0, error) { return &tfTCPMonitorFieldsV0{ Host: types.StringValue(api.Host), SslVerificationMode: types.StringValue(api.SslVerificationMode), @@ -410,7 +403,7 @@ func toTfTCPMonitorFieldsV0(api kbapi.SyntheticsMonitor) (*tfTCPMonitorFieldsV0, }, nil } -func toTfHTTPMonitorFieldsV0(api kbapi.SyntheticsMonitor) (*tfHTTPMonitorFieldsV0, error) { +func toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFieldsV0, error) { proxyHeaders, err := toNormalizedValue(api.ProxyHeaders) if err != nil { From aa09407873930bdca915059b7936b431905bd9f8 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Wed, 7 Aug 2024 15:03:04 +0200 Subject: [PATCH 07/72] first version --- internal/kibana/synthetics/create.go | 46 +++++++++ internal/kibana/synthetics/delete.go | 31 +++++++ internal/kibana/synthetics/read.go | 4 +- internal/kibana/synthetics/resource.go | 15 --- internal/kibana/synthetics/schema.go | 124 +++++++++++++++++++++++++ internal/kibana/synthetics/update.go | 48 ++++++++++ 6 files changed, 251 insertions(+), 17 deletions(-) create mode 100644 internal/kibana/synthetics/create.go create mode 100644 internal/kibana/synthetics/delete.go create mode 100644 internal/kibana/synthetics/update.go diff --git a/internal/kibana/synthetics/create.go b/internal/kibana/synthetics/create.go new file mode 100644 index 000000000..c15ca0703 --- /dev/null +++ b/internal/kibana/synthetics/create.go @@ -0,0 +1,46 @@ +package synthetics + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + kibanaClient := GetKibanaClient(r, response.Diagnostics) + if kibanaClient == nil { + return + } + + var plan *tfModelV0 + diags := request.Plan.Get(ctx, plan) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + input, diags := plan.toKibanaAPIRequest() + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + namespace := plan.SpaceID.ValueString() + result, err := kibanaClient.KibanaSynthetics.Monitor.Add(input.config, input.fields, namespace) + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("Failed to create Kibana monitor `%s`, namespace %s", input.config.Name, namespace), err.Error()) + return + } + + plan, err = toModelV0(result) + if err != nil { + response.Diagnostics.AddError("Failed to convert Kibana monitor API to TF state", err.Error()) + return + } + + diags = response.State.Set(ctx, plan) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } +} diff --git a/internal/kibana/synthetics/delete.go b/internal/kibana/synthetics/delete.go new file mode 100644 index 000000000..85bed7206 --- /dev/null +++ b/internal/kibana/synthetics/delete.go @@ -0,0 +1,31 @@ +package synthetics + +import ( + "context" + "fmt" + "github.com/disaster37/go-kibana-rest/v8/kbapi" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + kibanaClient := GetKibanaClient(r, response.Diagnostics) + if kibanaClient == nil { + return + } + + var plan tfModelV0 + diags := request.State.Get(ctx, &plan) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + monitorId := kbapi.MonitorID(plan.ID.ValueString()) + namespace := plan.SpaceID.ValueString() + _, err := kibanaClient.KibanaSynthetics.Monitor.Delete(namespace, monitorId) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", monitorId, namespace), err.Error()) + return + } +} diff --git a/internal/kibana/synthetics/read.go b/internal/kibana/synthetics/read.go index 898966006..0ab8eca9f 100644 --- a/internal/kibana/synthetics/read.go +++ b/internal/kibana/synthetics/read.go @@ -23,8 +23,8 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo } namespace := state.SpaceID.ValueString() - monitorId := state.ID.ValueString() - result, err := kibanaClient.KibanaSynthetics.Monitor.Get(kbapi.MonitorID(monitorId), namespace) + monitorId := kbapi.MonitorID(state.ID.ValueString()) + result, err := kibanaClient.KibanaSynthetics.Monitor.Get(monitorId, namespace) if err != nil { var apiError *kbapi.APIError if errors.As(err, &apiError) && apiError.Code == 404 { diff --git a/internal/kibana/synthetics/resource.go b/internal/kibana/synthetics/resource.go index 23970b7db..1925ff521 100644 --- a/internal/kibana/synthetics/resource.go +++ b/internal/kibana/synthetics/resource.go @@ -82,18 +82,3 @@ func (r *Resource) Schema(ctx context.Context, request resource.SchemaRequest, r response.Schema = monitorConfigSchema() } - -func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { - //TODO implement me - panic("implement me") -} - -func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { - //TODO implement me - panic("implement me") -} - -func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { - //TODO implement me - panic("implement me") -} diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index 58807e8f7..efd509dbb 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" @@ -333,6 +334,15 @@ func toNormalizedValue(jsObj kbapi.JsonObject) (jsontypes.Normalized, error) { return jsontypes.NewNormalizedValue(string(res)), nil } +func toJsonObject(v jsontypes.Normalized) (kbapi.JsonObject, diag.Diagnostics) { + var res kbapi.JsonObject + dg := v.Unmarshal(res) + if dg.HasError() { + return nil, dg + } + return res, diag.Diagnostics{} +} + func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { schedule, err := strconv.ParseInt(api.Schedule.Number, 10, 34) if err != nil { @@ -443,3 +453,117 @@ func toTfStatusConfigV0(status *kbapi.SyntheticsStatusConfig) *tfStatusConfigV0 Enabled: types.BoolPointerValue(status.Enabled), } } + +func (v *tfModelV0) toKibanaAPIRequest() (*kibanaAPIRequest, diag.Diagnostics) { + + fields, dg := v.toMonitorFields() + if dg.HasError() { + return nil, dg + } + config, dg := v.toSyntheticsMonitorConfig() + if dg.HasError() { + return nil, dg + } + return &kibanaAPIRequest{ + fields: fields, + config: *config, + }, dg +} + +func (v *tfModelV0) toMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { + var dg diag.Diagnostics + + if v.HTTP != nil { + return v.toHttpMonitorFields() + } else if v.TCP != nil { + return v.toTCPMonitorFields(), dg + } + + dg.Append(diag.NewErrorDiagnostic("Unsupported monitor type config", "one of http,tcp monitor fields is required")) + return nil, dg +} + +func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, diag.Diagnostics) { + locations := Map[types.String, kbapi.MonitorLocation](v.Locations, func(s types.String) kbapi.MonitorLocation { return kbapi.MonitorLocation(s.ValueString()) }) + params, dg := toJsonObject(v.Params) + if dg.HasError() { + return nil, dg + } + return &kbapi.SyntheticsMonitorConfig{ + Name: v.Name.ValueString(), + Schedule: kbapi.MonitorSchedule(v.Schedule.ValueInt64()), + Locations: locations, + PrivateLocations: ValueStringSlice(v.PrivateLocations), + Enabled: v.Enabled.ValueBoolPointer(), + Tags: ValueStringSlice(v.Tags), + Alert: v.Alert.toTfAlertConfigV0(), + APMServiceName: v.APMServiceName.ValueString(), + TimeoutSeconds: int(v.TimeoutSeconds.ValueInt64()), + Namespace: v.SpaceID.ValueString(), + Params: params, + RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(), + }, dg +} + +func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { + proxyHeaders, dg := toJsonObject(v.HTTP.ProxyHeader) + if dg.HasError() { + return nil, dg + } + response, dg := toJsonObject(v.HTTP.Response) + if dg.HasError() { + return nil, dg + } + check, dg := toJsonObject(v.HTTP.Check) + if dg.HasError() { + return nil, dg + } + return kbapi.HTTPMonitorFields{ + Url: v.HTTP.URL.ValueString(), + SslVerificationMode: v.HTTP.SslVerificationMode.ValueString(), + SslSupportedProtocols: ValueStringSlice(v.HTTP.SslSupportedProtocols), + MaxRedirects: v.HTTP.MaxRedirects.ValueString(), + Mode: kbapi.HttpMonitorMode(v.HTTP.Mode.ValueString()), + Ipv4: v.HTTP.IPv4.ValueBoolPointer(), + Ipv6: v.HTTP.IPv6.ValueBoolPointer(), + Username: v.HTTP.Username.ValueString(), + Password: v.HTTP.Password.ValueString(), + ProxyHeader: proxyHeaders, + ProxyUrl: v.HTTP.ProxyURL.ValueString(), + Response: response, + Check: check, + }, dg +} + +func (v *tfModelV0) toTCPMonitorFields() kbapi.MonitorFields { + return kbapi.TCPMonitorFields{ + Host: v.TCP.Host.ValueString(), + SslVerificationMode: v.TCP.SslVerificationMode.ValueString(), + SslSupportedProtocols: ValueStringSlice(v.TCP.SslSupportedProtocols), + CheckSend: v.TCP.CheckSend.ValueString(), + CheckReceive: v.TCP.CheckReceive.ValueString(), + ProxyUrl: v.TCP.ProxyURL.ValueString(), + ProxyUseLocalResolver: v.TCP.ProxyUseLocalResolver.ValueBoolPointer(), + } +} + +func Map[T, U any](ts []T, f func(T) U) []U { + us := make([]U, len(ts)) + for i := range ts { + us[i] = f(ts[i]) + } + return us +} + +func (v tfAlertConfigV0) toTfAlertConfigV0() *kbapi.MonitorAlertConfig { + return &kbapi.MonitorAlertConfig{ + Status: v.Status.toTfStatusConfigV0(), + Tls: v.TLS.toTfStatusConfigV0(), + } +} + +func (v tfStatusConfigV0) toTfStatusConfigV0() *kbapi.SyntheticsStatusConfig { + return &kbapi.SyntheticsStatusConfig{ + Enabled: v.Enabled.ValueBoolPointer(), + } +} diff --git a/internal/kibana/synthetics/update.go b/internal/kibana/synthetics/update.go new file mode 100644 index 000000000..7956de019 --- /dev/null +++ b/internal/kibana/synthetics/update.go @@ -0,0 +1,48 @@ +package synthetics + +import ( + "context" + "fmt" + "github.com/disaster37/go-kibana-rest/v8/kbapi" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + kibanaClient := GetKibanaClient(r, response.Diagnostics) + if kibanaClient == nil { + return + } + + var plan *tfModelV0 + diags := request.Plan.Get(ctx, plan) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + input, diags := plan.toKibanaAPIRequest() + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + namespace := plan.SpaceID.ValueString() + monitorId := kbapi.MonitorID(plan.ID.ValueString()) + result, err := kibanaClient.KibanaSynthetics.Monitor.Update(monitorId, input.config, input.fields, namespace) + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("Failed to update Kibana monitor `%s`, namespace %s", input.config.Name, namespace), err.Error()) + return + } + + plan, err = toModelV0(result) + if err != nil { + response.Diagnostics.AddError("Failed to convert Kibana monitor API to TF state", err.Error()) + return + } + + diags = response.State.Set(ctx, plan) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } +} From 6c31d0a6ba69aa8ce650c5e916849759bce9a153 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Wed, 7 Aug 2024 18:51:02 +0200 Subject: [PATCH 08/72] add first test --- internal/kibana/synthetics/schema_test.go | 259 ++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 internal/kibana/synthetics/schema_test.go diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go new file mode 100644 index 000000000..a82cfd782 --- /dev/null +++ b/internal/kibana/synthetics/schema_test.go @@ -0,0 +1,259 @@ +package synthetics + +import ( + "encoding/json" + "github.com/disaster37/go-kibana-rest/v8/kbapi" + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/stretchr/testify/assert" + "testing" +) + +var ( + fBool = boolPointer(false) + tBool = boolPointer(true) +) + +func boolPointer(v bool) *bool { + var res = new(bool) + *res = v + return res +} + +func TestToModelV0(t *testing.T) { + apiMonitorHTTP := &kbapi.SyntheticsMonitor{ + Id: "test-id-http", + Name: "test-name-http", + Namespace: "default", + Schedule: &kbapi.MonitorScheduleConfig{Number: "5", Unit: "m"}, + Locations: []kbapi.MonitorLocationConfig{ + {Label: "us_east", IsServiceManaged: true}, + {Label: "test private location", IsServiceManaged: false}, + }, + Enabled: tBool, + Tags: []string{"tag1", "tag2"}, + Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}, Tls: &kbapi.SyntheticsStatusConfig{Enabled: fBool}}, + APMServiceName: "test-service-http", + Timeout: json.Number("30"), + Params: kbapi.JsonObject{"param1": "value1"}, + Type: kbapi.Http, + Url: "https://example.com", + SslVerificationMode: "full", + SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, + MaxRedirects: "5", + Mode: kbapi.HttpMonitorMode("all"), + Ipv4: tBool, + Ipv6: fBool, + Username: "user", + Password: "pass", + ProxyHeaders: kbapi.JsonObject{"header1": "value1"}, + ProxyUrl: "https://proxy.com", + } + + expectedModelHTTP := &tfModelV0{ + ID: types.StringValue("test-id-http"), + Name: types.StringValue("test-name-http"), + SpaceID: types.StringValue("default"), + Schedule: types.Int64Value(5), + Locations: []types.String{types.StringValue("us_east")}, + PrivateLocations: []types.String{types.StringValue("test private location")}, + Enabled: types.BoolPointerValue(tBool), + Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")}, + Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, + APMServiceName: types.StringValue("test-service-http"), + TimeoutSeconds: types.Int64Value(30), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + HTTP: &tfHTTPMonitorFieldsV0{ + URL: types.StringValue("https://example.com"), + SslVerificationMode: types.StringValue("full"), + SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, + MaxRedirects: types.StringValue("5"), + Mode: types.StringValue("all"), + IPv4: types.BoolPointerValue(tBool), + IPv6: types.BoolPointerValue(fBool), + Username: types.StringValue("user"), + Password: types.StringValue("pass"), + ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), + }, + } + + modelHTTP, err := toModelV0(apiMonitorHTTP) + assert.NoError(t, err) + assert.Equal(t, expectedModelHTTP, modelHTTP) + + // Test case for TCP fields + apiMonitorTCP := &kbapi.SyntheticsMonitor{ + Id: "test-id-tcp", + Name: "test-name-tcp", + Namespace: "default", + Schedule: &kbapi.MonitorScheduleConfig{Number: "5", Unit: "m"}, + Locations: []kbapi.MonitorLocationConfig{ + {Label: "test private location", IsServiceManaged: false}, + }, + Enabled: tBool, + Tags: nil, + Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}}, + APMServiceName: "test-service-tcp", + Timeout: json.Number("30"), + Params: kbapi.JsonObject{"param1": "value1"}, + Type: kbapi.Tcp, + Host: "example.com:9200", + SslVerificationMode: "full", + SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, + CheckSend: "hello", + CheckReceive: "world", + ProxyUrl: "http://proxy.com", + ProxyUseLocalResolver: tBool, + } + + expectedModelTCP := &tfModelV0{ + ID: types.StringValue("test-id-tcp"), + Name: types.StringValue("test-name-tcp"), + SpaceID: types.StringValue("default"), + Schedule: types.Int64Value(5), + Locations: nil, + PrivateLocations: []types.String{types.StringValue("test private location")}, + Enabled: types.BoolPointerValue(tBool), + Tags: nil, + Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, + APMServiceName: types.StringValue("test-service-tcp"), + TimeoutSeconds: types.Int64Value(30), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + TCP: &tfTCPMonitorFieldsV0{ + Host: types.StringValue("example.com:9200"), + SslVerificationMode: types.StringValue("full"), + SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, + CheckSend: types.StringValue("hello"), + CheckReceive: types.StringValue("world"), + ProxyURL: types.StringValue("http://proxy.com"), + ProxyUseLocalResolver: types.BoolPointerValue(tBool), + }, + } + + modelTCP, err := toModelV0(apiMonitorTCP) + assert.NoError(t, err) + assert.Equal(t, expectedModelTCP, modelTCP) +} + +/* +func TestToKibanaAPIRequest(t *testing.T) { + // Test case for HTTP monitor config + modelHTTP := &tfModelV0{ + ID: types.StringValue("test-id-http"), + Name: types.StringValue("test-name-http"), + SpaceID: types.StringValue("default"), + Schedule: types.Int64Value(5), + Locations: []types.String{types.StringValue("us_east")}, + PrivateLocations: []types.String{}, + //Enabled: types.BoolPointerValue(true), + Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")}, + //Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(true)}}, + APMServiceName: types.StringValue("test-service-http"), + TimeoutSeconds: types.Int64Value(30), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + HTTP: &tfHTTPMonitorFieldsV0{ + URL: types.StringValue("http://example.com"), + SslVerificationMode: types.StringValue("full"), + SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, + MaxRedirects: types.StringValue("5"), + Mode: types.StringValue("all"), + //IPv4: types.BoolPointerValue(true), + //IPv6: types.BoolPointerValue(false), + Username: types.StringValue("user"), + Password: types.StringValue("pass"), + ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("http://proxy.com"), + Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), + Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), + }, + } + + expectedAPIRequestHTTP := &kibanaAPIRequest{ + fields: &kbapi.HTTPMonitorFields{ + //URL: "http://example.com", + SslVerificationMode: "full", + SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, + MaxRedirects: "5", + Mode: "all", + //IPv4: true, + //IPv6: false, + Username: "user", + Password: "pass", + ProxyHeader: kbapi.JsonObject{"header1": "value1"}, + //ProxyURL: "http://proxy.com", + Response: kbapi.JsonObject{"response1": "value1"}, + Check: kbapi.JsonObject{"check1": "value1"}, + }, + config: kbapi.SyntheticsMonitorConfig{ + Name: "test-name-http", + Schedule: kbapi.MonitorSchedule(5), + //Locations: []kbapi.MonitorLocation{{Label: "us_east", IsServiceManaged: true}}, + PrivateLocations: []string{}, + //Enabled: true, + Tags: []string{"tag1", "tag2"}, + //Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: true}}, + APMServiceName: "test-service-http", + TimeoutSeconds: 30, + Params: kbapi.JsonObject{"param1": "value1"}, + }, + } + + apiRequestHTTP, dg := modelHTTP.toKibanaAPIRequest() + assert.False(t, dg.HasError()) + assert.Equal(t, expectedAPIRequestHTTP, apiRequestHTTP) + + // Test case for TCP monitor config + modelTCP := &tfModelV0{ + ID: types.StringValue("test-id-tcp"), + Name: types.StringValue("test-name-tcp"), + SpaceID: types.StringValue("default"), + Schedule: types.Int64Value(5), + Locations: []types.String{types.StringValue("us_east")}, + PrivateLocations: []types.String{}, + //Enabled: types.BoolPointerValue(true), + Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")}, + //Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(true)}}, + APMServiceName: types.StringValue("test-service-tcp"), + TimeoutSeconds: types.Int64Value(30), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + TCP: &tfTCPMonitorFieldsV0{ + Host: types.StringValue("example.com:9200"), + SslVerificationMode: types.StringValue("full"), + SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, + CheckSend: types.StringValue("hello"), + CheckReceive: types.StringValue("world"), + ProxyURL: types.StringValue("http://proxy.com"), + ProxyUseLocalResolver: types.BoolPointerValue(true), + }, + } + + expectedAPIRequestTCP := &kibanaAPIRequest{ + fields: &kbapi.TCPMonitorFields{ + Host: "example.com:9200", + SslVerificationMode: "full", + SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, + CheckSend: "hello", + CheckReceive: "world", + //ProxyURL: "http://proxy.com", + //ProxyUseLocalResolver: true, + }, + config: kbapi.SyntheticsMonitorConfig{ + Name: "test-name-tcp", + Schedule: kbapi.MonitorSchedule(5), + //Locations: []kbapi.MonitorLocation{{Label: "us_east", IsServiceManaged: true}}, + PrivateLocations: []string{}, + //Enabled: true, + Tags: []string{"tag1", "tag2"}, + //Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: true}}, + APMServiceName: "test-service-tcp", + TimeoutSeconds: 30, + Params: kbapi.JsonObject{"param1": "value1"}, + }, + } + + apiRequestTCP, dg := modelTCP.toKibanaAPIRequest() + assert.False(t, dg.HasError()) + assert.Equal(t, expectedAPIRequestTCP, apiRequestTCP) +} +*/ From 218f01f45a753de897947ee1882afc3d8780554e Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Thu, 8 Aug 2024 12:25:20 +0200 Subject: [PATCH 09/72] add schema transformer tests --- internal/kibana/synthetics/schema.go | 54 +- internal/kibana/synthetics/schema_test.go | 536 +++++++++++------- .../kbapi/api.kibana_synthetics.go | 87 +-- .../kbapi/api.kibana_synthetics_test.go | 12 +- 4 files changed, 412 insertions(+), 277 deletions(-) diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index efd509dbb..b535146f9 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -335,18 +335,34 @@ func toNormalizedValue(jsObj kbapi.JsonObject) (jsontypes.Normalized, error) { } func toJsonObject(v jsontypes.Normalized) (kbapi.JsonObject, diag.Diagnostics) { + if v.IsNull() { + return nil, diag.Diagnostics{} + } var res kbapi.JsonObject - dg := v.Unmarshal(res) + dg := v.Unmarshal(&res) if dg.HasError() { return nil, dg } return res, diag.Diagnostics{} } +func stringToInt64(v string) (int64, error) { + var res int64 + var err error + if v != "" { + res, err = strconv.ParseInt(v, 10, 64) + } + return res, err +} + func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { - schedule, err := strconv.ParseInt(api.Schedule.Number, 10, 34) - if err != nil { - return nil, err + var schedule int64 + var err error + if api.Schedule != nil { + schedule, err = stringToInt64(api.Schedule.Number) + if err != nil { + return nil, err + } } var locLabels []string var privateLocLabels []string @@ -358,7 +374,7 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { } } - timeout, err := api.Timeout.Int64() + timeout, err := stringToInt64(string(api.Timeout)) if err != nil { return nil, err } @@ -479,7 +495,7 @@ func (v *tfModelV0) toMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { return v.toTCPMonitorFields(), dg } - dg.Append(diag.NewErrorDiagnostic("Unsupported monitor type config", "one of http,tcp monitor fields is required")) + dg.AddError("Unsupported monitor type config", "one of http,tcp monitor fields is required") return nil, dg } @@ -489,6 +505,12 @@ func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, if dg.HasError() { return nil, dg } + + var alert *kbapi.MonitorAlertConfig + if v.Alert != nil { + alert = v.Alert.toTfAlertConfigV0() + } + return &kbapi.SyntheticsMonitorConfig{ Name: v.Name.ValueString(), Schedule: kbapi.MonitorSchedule(v.Schedule.ValueInt64()), @@ -496,7 +518,7 @@ func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, PrivateLocations: ValueStringSlice(v.PrivateLocations), Enabled: v.Enabled.ValueBoolPointer(), Tags: ValueStringSlice(v.Tags), - Alert: v.Alert.toTfAlertConfigV0(), + Alert: alert, APMServiceName: v.APMServiceName.ValueString(), TimeoutSeconds: int(v.TimeoutSeconds.ValueInt64()), Namespace: v.SpaceID.ValueString(), @@ -548,17 +570,25 @@ func (v *tfModelV0) toTCPMonitorFields() kbapi.MonitorFields { } func Map[T, U any](ts []T, f func(T) U) []U { - us := make([]U, len(ts)) - for i := range ts { - us[i] = f(ts[i]) + var us []U + for _, v := range ts { + us = append(us, f(v)) } return us } func (v tfAlertConfigV0) toTfAlertConfigV0() *kbapi.MonitorAlertConfig { + var status *kbapi.SyntheticsStatusConfig + if v.Status != nil { + status = v.Status.toTfStatusConfigV0() + } + var tls *kbapi.SyntheticsStatusConfig + if v.TLS != nil { + tls = v.TLS.toTfStatusConfigV0() + } return &kbapi.MonitorAlertConfig{ - Status: v.Status.toTfStatusConfigV0(), - Tls: v.TLS.toTfStatusConfigV0(), + Status: status, + Tls: tls, } } diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go index a82cfd782..e1bcd483a 100644 --- a/internal/kibana/synthetics/schema_test.go +++ b/internal/kibana/synthetics/schema_test.go @@ -21,239 +21,337 @@ func boolPointer(v bool) *bool { } func TestToModelV0(t *testing.T) { - apiMonitorHTTP := &kbapi.SyntheticsMonitor{ - Id: "test-id-http", - Name: "test-name-http", - Namespace: "default", - Schedule: &kbapi.MonitorScheduleConfig{Number: "5", Unit: "m"}, - Locations: []kbapi.MonitorLocationConfig{ - {Label: "us_east", IsServiceManaged: true}, - {Label: "test private location", IsServiceManaged: false}, + testcases := []struct { + name string + input kbapi.SyntheticsMonitor + expected tfModelV0 + }{ + { + name: "HTTP monitor empty data", + input: kbapi.SyntheticsMonitor{ + Type: kbapi.Http, + }, + expected: tfModelV0{ + ID: types.StringValue(""), + Name: types.StringValue(""), + SpaceID: types.StringValue(""), + Schedule: types.Int64Value(0), + APMServiceName: types.StringValue(""), + TimeoutSeconds: types.Int64Value(0), + Params: jsontypes.NewNormalizedValue("null"), + HTTP: &tfHTTPMonitorFieldsV0{ + URL: types.StringValue(""), + SslVerificationMode: types.StringValue(""), + MaxRedirects: types.StringValue(""), + Mode: types.StringValue(""), + Username: types.StringValue(""), + Password: types.StringValue(""), + ProxyHeader: jsontypes.NewNormalizedValue("null"), + ProxyURL: types.StringValue(""), + }, + }, }, - Enabled: tBool, - Tags: []string{"tag1", "tag2"}, - Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}, Tls: &kbapi.SyntheticsStatusConfig{Enabled: fBool}}, - APMServiceName: "test-service-http", - Timeout: json.Number("30"), - Params: kbapi.JsonObject{"param1": "value1"}, - Type: kbapi.Http, - Url: "https://example.com", - SslVerificationMode: "full", - SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, - MaxRedirects: "5", - Mode: kbapi.HttpMonitorMode("all"), - Ipv4: tBool, - Ipv6: fBool, - Username: "user", - Password: "pass", - ProxyHeaders: kbapi.JsonObject{"header1": "value1"}, - ProxyUrl: "https://proxy.com", - } - - expectedModelHTTP := &tfModelV0{ - ID: types.StringValue("test-id-http"), - Name: types.StringValue("test-name-http"), - SpaceID: types.StringValue("default"), - Schedule: types.Int64Value(5), - Locations: []types.String{types.StringValue("us_east")}, - PrivateLocations: []types.String{types.StringValue("test private location")}, - Enabled: types.BoolPointerValue(tBool), - Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")}, - Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, - APMServiceName: types.StringValue("test-service-http"), - TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), - HTTP: &tfHTTPMonitorFieldsV0{ - URL: types.StringValue("https://example.com"), - SslVerificationMode: types.StringValue("full"), - SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, - MaxRedirects: types.StringValue("5"), - Mode: types.StringValue("all"), - IPv4: types.BoolPointerValue(tBool), - IPv6: types.BoolPointerValue(fBool), - Username: types.StringValue("user"), - Password: types.StringValue("pass"), - ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), - ProxyURL: types.StringValue("https://proxy.com"), + { + name: "TCP monitor empty data", + input: kbapi.SyntheticsMonitor{ + Type: kbapi.Tcp, + }, + expected: tfModelV0{ + ID: types.StringValue(""), + Name: types.StringValue(""), + SpaceID: types.StringValue(""), + Schedule: types.Int64Value(0), + APMServiceName: types.StringValue(""), + TimeoutSeconds: types.Int64Value(0), + Params: jsontypes.NewNormalizedValue("null"), + TCP: &tfTCPMonitorFieldsV0{ + Host: types.StringValue(""), + SslVerificationMode: types.StringValue(""), + CheckSend: types.StringValue(""), + CheckReceive: types.StringValue(""), + ProxyURL: types.StringValue(""), + }, + }, }, - } - - modelHTTP, err := toModelV0(apiMonitorHTTP) - assert.NoError(t, err) - assert.Equal(t, expectedModelHTTP, modelHTTP) - - // Test case for TCP fields - apiMonitorTCP := &kbapi.SyntheticsMonitor{ - Id: "test-id-tcp", - Name: "test-name-tcp", - Namespace: "default", - Schedule: &kbapi.MonitorScheduleConfig{Number: "5", Unit: "m"}, - Locations: []kbapi.MonitorLocationConfig{ - {Label: "test private location", IsServiceManaged: false}, + { + name: "HTTP monitor", + input: kbapi.SyntheticsMonitor{ + Id: "test-id-http", + Name: "test-name-http", + Namespace: "default", + Enabled: tBool, + Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}, Tls: &kbapi.SyntheticsStatusConfig{Enabled: fBool}}, + Schedule: &kbapi.MonitorScheduleConfig{Number: "5", Unit: "m"}, + Tags: []string{"tag1", "tag2"}, + APMServiceName: "test-service-http", + Timeout: json.Number("30"), + Locations: []kbapi.MonitorLocationConfig{ + {Label: "us_east", IsServiceManaged: true}, + {Label: "test private location", IsServiceManaged: false}, + }, + Origin: "origin", + Params: kbapi.JsonObject{"param1": "value1"}, + MaxAttempts: 3, + Revision: 1, + Ui: kbapi.JsonObject{"is_tls_enabled": false}, + Type: kbapi.Http, + Url: "https://example.com", + Mode: kbapi.HttpMonitorMode("all"), + MaxRedirects: "5", + Ipv4: tBool, + Ipv6: fBool, + Username: "user", + Password: "pass", + ProxyHeaders: kbapi.JsonObject{"header1": "value1"}, + ProxyUrl: "https://proxy.com", + CheckResponseBodyPositive: []string{"foo", "bar"}, + CheckResponseStatus: []string{"200", "201"}, + ResponseIncludeBody: "always", + ResponseIncludeHeaders: true, + ResponseIncludeBodyMaxBytes: "1024", + CheckRequestBody: kbapi.JsonObject{"type": "text", "value": "name=first&email=someemail%40someemailprovider.com"}, + CheckRequestHeaders: kbapi.JsonObject{"Content-Type": "application/x-www-form-urlencoded"}, + CheckRequestMethod: "POST", + SslVerificationMode: "full", + SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, + }, + expected: tfModelV0{ + ID: types.StringValue("test-id-http"), + Name: types.StringValue("test-name-http"), + SpaceID: types.StringValue("default"), + Schedule: types.Int64Value(5), + Locations: []types.String{types.StringValue("us_east")}, + PrivateLocations: []types.String{types.StringValue("test private location")}, + Enabled: types.BoolPointerValue(tBool), + Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")}, + Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, + APMServiceName: types.StringValue("test-service-http"), + TimeoutSeconds: types.Int64Value(30), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + HTTP: &tfHTTPMonitorFieldsV0{ + URL: types.StringValue("https://example.com"), + SslVerificationMode: types.StringValue("full"), + SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, + MaxRedirects: types.StringValue("5"), + Mode: types.StringValue("all"), + IPv4: types.BoolPointerValue(tBool), + IPv6: types.BoolPointerValue(fBool), + Username: types.StringValue("user"), + Password: types.StringValue("pass"), + ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), + }, + }, }, - Enabled: tBool, - Tags: nil, - Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}}, - APMServiceName: "test-service-tcp", - Timeout: json.Number("30"), - Params: kbapi.JsonObject{"param1": "value1"}, - Type: kbapi.Tcp, - Host: "example.com:9200", - SslVerificationMode: "full", - SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, - CheckSend: "hello", - CheckReceive: "world", - ProxyUrl: "http://proxy.com", - ProxyUseLocalResolver: tBool, - } - - expectedModelTCP := &tfModelV0{ - ID: types.StringValue("test-id-tcp"), - Name: types.StringValue("test-name-tcp"), - SpaceID: types.StringValue("default"), - Schedule: types.Int64Value(5), - Locations: nil, - PrivateLocations: []types.String{types.StringValue("test private location")}, - Enabled: types.BoolPointerValue(tBool), - Tags: nil, - Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, - APMServiceName: types.StringValue("test-service-tcp"), - TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), - TCP: &tfTCPMonitorFieldsV0{ - Host: types.StringValue("example.com:9200"), - SslVerificationMode: types.StringValue("full"), - SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, - CheckSend: types.StringValue("hello"), - CheckReceive: types.StringValue("world"), - ProxyURL: types.StringValue("http://proxy.com"), - ProxyUseLocalResolver: types.BoolPointerValue(tBool), + { + name: "TCP monitor", + input: kbapi.SyntheticsMonitor{ + Id: "test-id-tcp", + Name: "test-name-tcp", + Namespace: "default", + Enabled: tBool, + Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}}, + Schedule: &kbapi.MonitorScheduleConfig{Number: "5", Unit: "m"}, + Tags: nil, + APMServiceName: "test-service-tcp", + Timeout: json.Number("30"), + Locations: []kbapi.MonitorLocationConfig{ + {Label: "test private location", IsServiceManaged: false}, + }, + Origin: "origin", + Params: kbapi.JsonObject{"param1": "value1"}, + MaxAttempts: 3, + Revision: 1, + Ui: kbapi.JsonObject{"is_tls_enabled": false}, + Type: kbapi.Tcp, + SslVerificationMode: "full", + SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, + ProxyUrl: "http://proxy.com", + Host: "example.com:9200", + CheckSend: "hello", + CheckReceive: "world", + ProxyUseLocalResolver: tBool, + }, + expected: tfModelV0{ + ID: types.StringValue("test-id-tcp"), + Name: types.StringValue("test-name-tcp"), + SpaceID: types.StringValue("default"), + Schedule: types.Int64Value(5), + Locations: nil, + PrivateLocations: []types.String{types.StringValue("test private location")}, + Enabled: types.BoolPointerValue(tBool), + Tags: nil, + Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, + APMServiceName: types.StringValue("test-service-tcp"), + TimeoutSeconds: types.Int64Value(30), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + TCP: &tfTCPMonitorFieldsV0{ + Host: types.StringValue("example.com:9200"), + SslVerificationMode: types.StringValue("full"), + SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, + CheckSend: types.StringValue("hello"), + CheckReceive: types.StringValue("world"), + ProxyURL: types.StringValue("http://proxy.com"), + ProxyUseLocalResolver: types.BoolPointerValue(tBool), + }, + }, }, } - modelTCP, err := toModelV0(apiMonitorTCP) - assert.NoError(t, err) - assert.Equal(t, expectedModelTCP, modelTCP) + for _, tt := range testcases { + t.Run(tt.name, func(t *testing.T) { + model, err := toModelV0(&tt.input) + assert.NoError(t, err) + assert.Equal(t, &tt.expected, model) + }) + } } -/* func TestToKibanaAPIRequest(t *testing.T) { - // Test case for HTTP monitor config - modelHTTP := &tfModelV0{ - ID: types.StringValue("test-id-http"), - Name: types.StringValue("test-name-http"), - SpaceID: types.StringValue("default"), - Schedule: types.Int64Value(5), - Locations: []types.String{types.StringValue("us_east")}, - PrivateLocations: []types.String{}, - //Enabled: types.BoolPointerValue(true), - Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")}, - //Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(true)}}, - APMServiceName: types.StringValue("test-service-http"), - TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), - HTTP: &tfHTTPMonitorFieldsV0{ - URL: types.StringValue("http://example.com"), - SslVerificationMode: types.StringValue("full"), - SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, - MaxRedirects: types.StringValue("5"), - Mode: types.StringValue("all"), - //IPv4: types.BoolPointerValue(true), - //IPv6: types.BoolPointerValue(false), - Username: types.StringValue("user"), - Password: types.StringValue("pass"), - ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), - ProxyURL: types.StringValue("http://proxy.com"), - Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), - Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), + testcases := []struct { + name string + input tfModelV0 + expected kibanaAPIRequest + }{ + { + name: "Empty HTTP monitor", + input: tfModelV0{ + HTTP: &tfHTTPMonitorFieldsV0{}, + }, + expected: kibanaAPIRequest{ + fields: kbapi.HTTPMonitorFields{}, + config: kbapi.SyntheticsMonitorConfig{}, + }, }, - } - - expectedAPIRequestHTTP := &kibanaAPIRequest{ - fields: &kbapi.HTTPMonitorFields{ - //URL: "http://example.com", - SslVerificationMode: "full", - SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, - MaxRedirects: "5", - Mode: "all", - //IPv4: true, - //IPv6: false, - Username: "user", - Password: "pass", - ProxyHeader: kbapi.JsonObject{"header1": "value1"}, - //ProxyURL: "http://proxy.com", - Response: kbapi.JsonObject{"response1": "value1"}, - Check: kbapi.JsonObject{"check1": "value1"}, + { + name: "Empty TCP monitor", + input: tfModelV0{ + TCP: &tfTCPMonitorFieldsV0{}, + }, + expected: kibanaAPIRequest{ + fields: kbapi.TCPMonitorFields{}, + config: kbapi.SyntheticsMonitorConfig{}, + }, }, - config: kbapi.SyntheticsMonitorConfig{ - Name: "test-name-http", - Schedule: kbapi.MonitorSchedule(5), - //Locations: []kbapi.MonitorLocation{{Label: "us_east", IsServiceManaged: true}}, - PrivateLocations: []string{}, - //Enabled: true, - Tags: []string{"tag1", "tag2"}, - //Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: true}}, - APMServiceName: "test-service-http", - TimeoutSeconds: 30, - Params: kbapi.JsonObject{"param1": "value1"}, + { + name: "HTTP monitor", + input: tfModelV0{ + ID: types.StringValue("test-id-http"), + Name: types.StringValue("test-name-http"), + SpaceID: types.StringValue("default"), + Schedule: types.Int64Value(5), + Locations: []types.String{types.StringValue("us_east")}, + PrivateLocations: []types.String{types.StringValue("test private location")}, + Enabled: types.BoolPointerValue(tBool), + Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")}, + Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, + APMServiceName: types.StringValue("test-service-http"), + TimeoutSeconds: types.Int64Value(30), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + HTTP: &tfHTTPMonitorFieldsV0{ + URL: types.StringValue("https://example.com"), + SslVerificationMode: types.StringValue("full"), + SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, + MaxRedirects: types.StringValue("5"), + Mode: types.StringValue("all"), + IPv4: types.BoolPointerValue(tBool), + IPv6: types.BoolPointerValue(fBool), + Username: types.StringValue("user"), + Password: types.StringValue("pass"), + ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), + Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), + Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), + }, + }, + expected: kibanaAPIRequest{ + config: kbapi.SyntheticsMonitorConfig{ + Name: "test-name-http", + Schedule: kbapi.MonitorSchedule(5), + Locations: []kbapi.MonitorLocation{"us_east"}, + PrivateLocations: []string{"test private location"}, + Enabled: tBool, + Tags: []string{"tag1", "tag2"}, + Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}, Tls: &kbapi.SyntheticsStatusConfig{Enabled: fBool}}, + APMServiceName: "test-service-http", + Namespace: "default", + TimeoutSeconds: 30, + Params: kbapi.JsonObject{"param1": "value1"}, + }, + fields: kbapi.HTTPMonitorFields{ + Url: "https://example.com", + SslVerificationMode: "full", + SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, + MaxRedirects: "5", + Mode: "all", + Ipv4: tBool, + Ipv6: fBool, + Username: "user", + Password: "pass", + ProxyHeader: kbapi.JsonObject{"header1": "value1"}, + ProxyUrl: "https://proxy.com", + Response: kbapi.JsonObject{"response1": "value1"}, + Check: kbapi.JsonObject{"check1": "value1"}, + }, + }, }, - } - - apiRequestHTTP, dg := modelHTTP.toKibanaAPIRequest() - assert.False(t, dg.HasError()) - assert.Equal(t, expectedAPIRequestHTTP, apiRequestHTTP) - - // Test case for TCP monitor config - modelTCP := &tfModelV0{ - ID: types.StringValue("test-id-tcp"), - Name: types.StringValue("test-name-tcp"), - SpaceID: types.StringValue("default"), - Schedule: types.Int64Value(5), - Locations: []types.String{types.StringValue("us_east")}, - PrivateLocations: []types.String{}, - //Enabled: types.BoolPointerValue(true), - Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")}, - //Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(true)}}, - APMServiceName: types.StringValue("test-service-tcp"), - TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), - TCP: &tfTCPMonitorFieldsV0{ - Host: types.StringValue("example.com:9200"), - SslVerificationMode: types.StringValue("full"), - SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, - CheckSend: types.StringValue("hello"), - CheckReceive: types.StringValue("world"), - ProxyURL: types.StringValue("http://proxy.com"), - ProxyUseLocalResolver: types.BoolPointerValue(true), + { + name: "TCP monitor", + input: tfModelV0{ + ID: types.StringValue("test-id-tcp"), + Name: types.StringValue("test-name-tcp"), + SpaceID: types.StringValue("default"), + Schedule: types.Int64Value(5), + Locations: []types.String{types.StringValue("us_east")}, + PrivateLocations: nil, + Enabled: types.BoolPointerValue(tBool), + Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")}, + Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, + APMServiceName: types.StringValue("test-service-tcp"), + TimeoutSeconds: types.Int64Value(30), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + TCP: &tfTCPMonitorFieldsV0{ + Host: types.StringValue("example.com:9200"), + SslVerificationMode: types.StringValue("full"), + SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, + CheckSend: types.StringValue("hello"), + CheckReceive: types.StringValue("world"), + ProxyURL: types.StringValue("http://proxy.com"), + ProxyUseLocalResolver: types.BoolPointerValue(tBool), + }, + }, + expected: kibanaAPIRequest{ + config: kbapi.SyntheticsMonitorConfig{ + Name: "test-name-tcp", + Schedule: kbapi.MonitorSchedule(5), + Locations: []kbapi.MonitorLocation{"us_east"}, + PrivateLocations: nil, + Enabled: tBool, + Tags: []string{"tag1", "tag2"}, + Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}}, + APMServiceName: "test-service-tcp", + Namespace: "default", + TimeoutSeconds: 30, + Params: kbapi.JsonObject{"param1": "value1"}, + }, + fields: kbapi.TCPMonitorFields{ + Host: "example.com:9200", + SslVerificationMode: "full", + SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, + CheckSend: "hello", + CheckReceive: "world", + ProxyUrl: "http://proxy.com", + ProxyUseLocalResolver: tBool, + }, + }, }, } - expectedAPIRequestTCP := &kibanaAPIRequest{ - fields: &kbapi.TCPMonitorFields{ - Host: "example.com:9200", - SslVerificationMode: "full", - SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, - CheckSend: "hello", - CheckReceive: "world", - //ProxyURL: "http://proxy.com", - //ProxyUseLocalResolver: true, - }, - config: kbapi.SyntheticsMonitorConfig{ - Name: "test-name-tcp", - Schedule: kbapi.MonitorSchedule(5), - //Locations: []kbapi.MonitorLocation{{Label: "us_east", IsServiceManaged: true}}, - PrivateLocations: []string{}, - //Enabled: true, - Tags: []string{"tag1", "tag2"}, - //Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: true}}, - APMServiceName: "test-service-tcp", - TimeoutSeconds: 30, - Params: kbapi.JsonObject{"param1": "value1"}, - }, + for _, tt := range testcases { + t.Run(tt.name, func(t *testing.T) { + apiRequest, dg := tt.input.toKibanaAPIRequest() + assert.False(t, dg.HasError(), dg.Errors()) + assert.Equal(t, &tt.expected, apiRequest) + }) } - - apiRequestTCP, dg := modelTCP.toKibanaAPIRequest() - assert.False(t, dg.HasError()) - assert.Equal(t, expectedAPIRequestTCP, apiRequestTCP) } -*/ diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go index cafd68e3b..e4acafd18 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go @@ -163,48 +163,51 @@ type MonitorDeleteStatus struct { } type SyntheticsMonitor struct { - Name string `json:"name"` - Type MonitorType `json:"type"` - ConfigId MonitorID `json:"config_id"` - Id MonitorID `json:"id"` - Mode HttpMonitorMode `json:"mode"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Namespace string `json:"namespace"` - Enabled *bool `json:"enabled,omitempty"` - Alert *MonitorAlertConfig `json:"alert,omitempty"` - Schedule *MonitorScheduleConfig `json:"schedule,omitempty"` - Tags []string `json:"tags,omitempty"` - APMServiceName string `json:"service.name,omitempty"` - Timeout json.Number `json:"timeout,omitempty"` - Locations []MonitorLocationConfig `json:"locations,omitempty"` - Origin string `json:"origin,omitempty"` - Params JsonObject `json:"params,omitempty"` - MaxAttempts int `json:"max_attempts"` - MaxRedirects string `json:"max_redirects"` - Ipv4 *bool `json:"ipv4,omitempty"` - Ipv6 *bool `json:"ipv6,omitempty"` - SslVerificationMode string `json:"ssl.verification_mode"` - SslSupportedProtocols []string `json:"ssl.supported_protocols"` - Revision int `json:"revision,omitempty"` - Url string `json:"url,omitempty"` - Ui JsonObject `json:"__ui,omitempty"` - ProxyUrl string `json:"proxy_url,omitempty"` - ProxyUseLocalResolver *bool `json:"proxy_use_local_resolver,omitempty"` - Host string `json:"host,omitempty"` - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` - ProxyHeaders JsonObject `json:"proxy_headers,omitempty"` - CheckSend string `json:"check.send,omitempty"` - CheckReceive string `json:"check.receive,omitempty"` - CheckResponseBodyPositive []string `json:"check.response.body.positive,omitempty"` - CheckResponseStatus []string `json:"check.response.status,omitempty"` - ResponseIncludeBody string `json:"response.include_body,omitempty"` - ResponseIncludeHeaders bool `json:"response.include_headers,omitempty"` - ResponseIncludeBodyMaxBytes string `json:"response.include_body_max_bytes,omitempty"` - CheckRequestBody JsonObject `json:"check.request.body,omitempty"` - CheckRequestHeaders JsonObject `json:"check.request.headers,omitempty"` - CheckRequestMethod string `json:"check.request.method,omitempty"` + Name string `json:"name"` + Type MonitorType `json:"type"` + ConfigId MonitorID `json:"config_id"` + Id MonitorID `json:"id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + Namespace string `json:"namespace"` + Enabled *bool `json:"enabled,omitempty"` + Alert *MonitorAlertConfig `json:"alert,omitempty"` + Schedule *MonitorScheduleConfig `json:"schedule,omitempty"` + Tags []string `json:"tags,omitempty"` + APMServiceName string `json:"service.name,omitempty"` + Timeout json.Number `json:"timeout,omitempty"` + Locations []MonitorLocationConfig `json:"locations,omitempty"` + Origin string `json:"origin,omitempty"` + Params JsonObject `json:"params,omitempty"` + MaxAttempts int `json:"max_attempts"` + Revision int `json:"revision,omitempty"` + Ui JsonObject `json:"__ui,omitempty"` + //http + Url string `json:"url,omitempty"` + Mode HttpMonitorMode `json:"mode"` + MaxRedirects string `json:"max_redirects"` + Ipv4 *bool `json:"ipv4,omitempty"` + Ipv6 *bool `json:"ipv6,omitempty"` + Username string `json:"username,omitempty"` + Password string `json:"password,omitempty"` + ProxyHeaders JsonObject `json:"proxy_headers,omitempty"` + CheckResponseBodyPositive []string `json:"check.response.body.positive,omitempty"` + CheckResponseStatus []string `json:"check.response.status,omitempty"` + ResponseIncludeBody string `json:"response.include_body,omitempty"` + ResponseIncludeHeaders bool `json:"response.include_headers,omitempty"` + ResponseIncludeBodyMaxBytes string `json:"response.include_body_max_bytes,omitempty"` + CheckRequestBody JsonObject `json:"check.request.body,omitempty"` + CheckRequestHeaders JsonObject `json:"check.request.headers,omitempty"` + CheckRequestMethod string `json:"check.request.method,omitempty"` + //http and tcp + ProxyUrl string `json:"proxy_url,omitempty"` + SslVerificationMode string `json:"ssl.verification_mode"` + SslSupportedProtocols []string `json:"ssl.supported_protocols"` + //tcp + Host string `json:"host,omitempty"` + ProxyUseLocalResolver *bool `json:"proxy_use_local_resolver,omitempty"` + CheckSend string `json:"check.send,omitempty"` + CheckReceive string `json:"check.receive,omitempty"` } type MonitorTypeConfig struct { diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go index f5ebb7ab6..58d461906 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go @@ -57,7 +57,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { fields MonitorFields } - for _, n := range []string{""} { //namespaces + for _, n := range namespaces { testUuid := uuid.New().String() space := n syntheticsAPI := s.API.KibanaSynthetics @@ -241,7 +241,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { monitor, err := syntheticsAPI.Monitor.Add(config, fields, space) assert.NoError(s.T(), err) assert.NotNil(s.T(), monitor) - updateDueToKibanaAPIDiff(monitor, fields) + updateDueToKibanaAPIDiff(monitor) get, err := syntheticsAPI.Monitor.Get(monitor.Id, space) assert.NoError(s.T(), err) @@ -254,7 +254,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { update, err := syntheticsAPI.Monitor.Update(monitor.Id, tc.update.config, tc.update.fields, space) assert.NoError(s.T(), err) assert.NotNil(s.T(), update) - updateDueToKibanaAPIDiff(update, fields) + updateDueToKibanaAPIDiff(update) get, err = syntheticsAPI.Monitor.Get(monitor.ConfigId, space) assert.NoError(s.T(), err) @@ -272,6 +272,10 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { for _, d := range deleted { assert.False(s.T(), d.Deleted) } + _, err = syntheticsAPI.Monitor.Get(monitor.Id, space) + assert.Error(s.T(), err) + assert.IsType(s.T(), APIError{}, err) + assert.Equal(s.T(), 404, err.(APIError).Code) }) } }) @@ -279,7 +283,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { } // see https://github.com/elastic/kibana/issues/189906 -func updateDueToKibanaAPIDiff(m *SyntheticsMonitor, f MonitorFields) { +func updateDueToKibanaAPIDiff(m *SyntheticsMonitor) { m.Params = nil m.Username = "" m.Password = "" From 02c86bad01ca4a532bd33aee27d4b14a28506b62 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Thu, 8 Aug 2024 17:19:17 +0200 Subject: [PATCH 10/72] testing synthetics --- Makefile | 2 +- internal/kibana/synthetics/acc_test.go | 147 ++++++++++++++++++ internal/kibana/synthetics/schema.go | 181 ++++++++++++---------- internal/kibana/synthetics/schema_test.go | 32 ++-- libs/go-kibana-rest/kibana.go | 1 + provider/plugin_framework.go | 2 + 6 files changed, 268 insertions(+), 97 deletions(-) create mode 100644 internal/kibana/synthetics/acc_test.go diff --git a/Makefile b/Makefile index 3235784d5..a0143c5e7 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ SWAGGER_VERSION ?= 8.7 GOVERSION ?= $(shell grep -e '^go' go.mod | cut -f 2 -d ' ') -STACK_VERSION ?= 8.13.4 +STACK_VERSION ?= 8.14.3 ELASTICSEARCH_NAME ?= terraform-elasticstack-es ELASTICSEARCH_ENDPOINTS ?= http://$(ELASTICSEARCH_NAME):9200 diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go new file mode 100644 index 000000000..2c4f3ad5b --- /dev/null +++ b/internal/kibana/synthetics/acc_test.go @@ -0,0 +1,147 @@ +package synthetics_test + +import ( + "testing" + + "github.com/elastic/terraform-provider-elasticstack/internal/acctest" + "github.com/elastic/terraform-provider-elasticstack/internal/versionutils" + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +var ( + minKibanaVersion = version.Must(version.NewVersion("8.14.0")) +) + +const ( + providerConfig = ` +provider "elasticstack" { + elasticsearch {} + kibana {} + fleet{} +} + +resource "elasticstack_fleet_agent_policy" "test" { + name = "TestMonitorResource Agent Policy - test" + namespace = "default" + description = "TestMonitorResource Agent Policy" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} + +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "TestMonitorResource-label" + space_id = "default" + agent_policy_id = elasticstack_fleet_agent_policy.test.policy_id +} +` + + resourceId = "elasticstack_kibana_synthetics_monitor.test" + + httpMonitorConfig = ` +resource "elasticstack_kibana_synthetics_monitor" "test" { + name = "TestMonitorResource" + space_id = "default" + schedule = 5 + private_locations = [elasticstack_kibana_synthetics_private_location.test.label] + enabled = true + tags = ["a", "b"] + service_name = "test apm service" + timeout = 30 + retest_on_failure = true + http = { + url = "http://localhost:5601" + ssl_verification_mode = "full" + ssl_supported_protocols = ["TLSv1.0", "TLSv1.1", "TLSv1.2"] + max_redirects = "10" + mode = "any" + ipv4 = true + ipv6 = false + username = "test" + password = "test" + proxy_url = "http://localhost:8080" + } +} +` + + /* + alert = { + status = { + enabled = true + } + tls = { + enabled = true + } + } + + # locations = [] + # params = jsonencode({ + # "param-name" = "param-value" + # }) + + # proxy_header = jsonencode({"header-name" = "header-value"}) + # response = jsonencode({ + # "include_body": "always", + # "include_body_max_bytes": "1024", + # }) + # check = jsonencode({ + # "request": { + # "method": "POST", + # "headers": { + # "Content-Type": "application/x-www-form-urlencoded", + # }, + # "body": "name=first&email=someemail%40someemailprovider.com", + # }, + # "response": { + # "status": [200, 201], + # "body": { + # "positive": ["foo", "bar"], + # }, + # }, + # }) + + + check.send = "Hello" + check.receive = "World" + proxy_use_local_resolver = true + + */ +) + +func TestSyntheticMonitorResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + // Create and Read testing + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + Config: providerConfig + httpMonitorConfig, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(resourceId, "id"), + //resource.TestCheckResourceAttrSet(resourceId, "id"), + ), + }, + // ImportState testing + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + ResourceName: resourceId, + ImportState: true, + ImportStateVerify: true, + Config: providerConfig + httpMonitorConfig, + }, + // Update and Read testing + /* + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + Config: "", // TODO + Check: resource.ComposeAggregateTestCheckFunc( + // TODO + ), + }, + */ + // Delete testing automatically occurs in TestCase + }, + }) +} diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index b535146f9..65181632e 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -17,8 +17,6 @@ import ( "strconv" ) -//TODO: monitor support is from 8.14.0 - const ( MetadataPrefix = "_kibana_synthetics_" ) @@ -38,47 +36,47 @@ type tfAlertConfigV0 struct { } type tfHTTPMonitorFieldsV0 struct { - URL types.String `tfsdk:"url"` - SslVerificationMode types.String `tfsdk:"ssl.verification_mode"` - SslSupportedProtocols []types.String `tfsdk:"ssl.supported_protocols"` - MaxRedirects types.String `tfsdk:"max_redirects"` - Mode types.String `tfsdk:"mode"` - IPv4 types.Bool `tfsdk:"ipv4"` - IPv6 types.Bool `tfsdk:"ipv6"` - Username types.String `tfsdk:"username"` - Password types.String `tfsdk:"password"` - ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` - ProxyURL types.String `tfsdk:"proxy_url"` - Response jsontypes.Normalized `tfsdk:"response"` - Check jsontypes.Normalized `tfsdk:"check"` + URL types.String `tfsdk:"url"` + SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` + SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` + MaxRedirects types.String `tfsdk:"max_redirects"` + Mode types.String `tfsdk:"mode"` + IPv4 types.Bool `tfsdk:"ipv4"` + IPv6 types.Bool `tfsdk:"ipv6"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` + //ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` + ProxyURL types.String `tfsdk:"proxy_url"` + //Response jsontypes.Normalized `tfsdk:"response"` + //Check jsontypes.Normalized `tfsdk:"check"` } type tfTCPMonitorFieldsV0 struct { Host types.String `tfsdk:"host"` - SslVerificationMode types.String `tfsdk:"ssl.verification_mode"` - SslSupportedProtocols []types.String `tfsdk:"ssl.supported_protocols"` - CheckSend types.String `tfsdk:"check.send"` - CheckReceive types.String `tfsdk:"check.receive"` + SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` + SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` + CheckSend types.String `tfsdk:"check_send"` + CheckReceive types.String `tfsdk:"check_receive"` ProxyURL types.String `tfsdk:"proxy_url"` ProxyUseLocalResolver types.Bool `tfsdk:"proxy_use_local_resolver"` } type tfModelV0 struct { - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - SpaceID types.String `tfsdk:"space_id"` - Schedule types.Int64 `tfsdk:"schedule"` - Locations []types.String `tfsdk:"locations"` - PrivateLocations []types.String `tfsdk:"private_locations"` - Enabled types.Bool `tfsdk:"enabled"` - Tags []types.String `tfsdk:"tags"` - Alert *tfAlertConfigV0 `tfsdk:"alert"` - APMServiceName types.String `tfsdk:"service_name"` - TimeoutSeconds types.Int64 `tfsdk:"timeout"` - Params jsontypes.Normalized `tfsdk:"params"` - HTTP *tfHTTPMonitorFieldsV0 `tfsdk:"http"` - TCP *tfTCPMonitorFieldsV0 `tfsdk:"tcp"` - RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + SpaceID types.String `tfsdk:"space_id"` + Schedule types.Int64 `tfsdk:"schedule"` + Locations []types.String `tfsdk:"locations"` + PrivateLocations []types.String `tfsdk:"private_locations"` + Enabled types.Bool `tfsdk:"enabled"` + Tags []types.String `tfsdk:"tags"` + Alert *tfAlertConfigV0 `tfsdk:"alert"` + APMServiceName types.String `tfsdk:"service_name"` + TimeoutSeconds types.Int64 `tfsdk:"timeout"` + //Params jsontypes.Normalized `tfsdk:"params"` + HTTP *tfHTTPMonitorFieldsV0 `tfsdk:"http"` + TCP *tfTCPMonitorFieldsV0 `tfsdk:"tcp"` + RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` } func monitorConfigSchema() schema.Schema { @@ -90,10 +88,12 @@ func monitorConfigSchema() schema.Schema { MarkdownDescription: "Generated identifier for the monitor", PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), + stringplanmodifier.RequiresReplace(), }, }, "name": schema.StringAttribute{ Optional: false, + Required: true, MarkdownDescription: "The monitor’s name.", }, "space_id": schema.StringAttribute{ @@ -105,7 +105,6 @@ func monitorConfigSchema() schema.Schema { }, "schedule": schema.Int64Attribute{ Optional: true, - Computed: true, MarkdownDescription: "The monitor’s schedule in minutes. Supported values are 1, 3, 5, 10, 15, 30, 60, 120 and 240.", Validators: []validator.Int64{ int64validator.OneOf(1, 3, 5, 10, 15, 30, 60, 120, 240), @@ -155,7 +154,7 @@ func monitorConfigSchema() schema.Schema { Optional: true, MarkdownDescription: "The monitor timeout in seconds, monitor will fail if it doesn’t complete within this time. Default: `16`", }, - "params": jsonObjectSchema("Monitor parameters"), + //"params": jsonObjectSchema("Monitor parameters"), "retest_on_failure": schema.BoolAttribute{ Optional: true, MarkdownDescription: "Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from \"up\" to \"down\". If the result of the retest is also \"down\", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`.", @@ -198,16 +197,23 @@ func monitorAlertConfigSchema() schema.Attribute { func httpMonitorFieldsSchema() schema.Attribute { return schema.SingleNestedAttribute{ - Optional: true, - MarkdownDescription: "", + Optional: true, Attributes: map[string]schema.Attribute{ "url": schema.StringAttribute{ Optional: false, Required: true, MarkdownDescription: "URL to monitor.", }, - "ssl_setting": jsonObjectSchema("The TLS/SSL connection settings for use with the HTTPS endpoint. If you don’t specify settings, the system defaults are used. See https://www.elastic.co/guide/en/beats/heartbeat/current/configuration-ssl.html for full SSL Options."), - "max_redirects": schema.StringAttribute{ + "ssl_verification_mode": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Controls the verification of server certificates. ", + }, + "ssl_supported_protocols": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of allowed SSL/TLS versions.", + }, + "max_redirects": schema.StringAttribute{ //TODO: make int64?? Optional: true, MarkdownDescription: "The maximum number of redirects to follow. Default: `0`", }, @@ -234,13 +240,13 @@ func httpMonitorFieldsSchema() schema.Attribute { Optional: true, MarkdownDescription: "The password for authenticating with the server. The credentials are passed with the request.", }, - "proxy_header": jsonObjectSchema("Additional headers to send to proxies during CONNECT requests."), + //"proxy_header": jsonObjectSchema("Additional headers to send to proxies during CONNECT requests."), "proxy_url": schema.StringAttribute{ Optional: true, MarkdownDescription: "The URL of the proxy to use for this monitor.", }, - "response": jsonObjectSchema("Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field."), - "check": jsonObjectSchema("The check request settings."), + //"response": jsonObjectSchema("Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field."), + //"check": jsonObjectSchema("The check request settings."), }, } } @@ -255,8 +261,23 @@ func tcpMonitorFieldsSchema() schema.Attribute { Required: true, MarkdownDescription: "The host to monitor; it can be an IP address or a hostname. The host can include the port using a colon (e.g., \"example.com:9200\").", }, - "ssl": jsonObjectSchema(" The TLS/SSL connection settings for use with the HTTPS endpoint. If you don’t specify settings, the system defaults are used. See https://www.elastic.co/guide/en/beats/heartbeat/current/configuration-ssl.html for full SSL Options."), - "check": jsonObjectSchema("An optional payload string to send to the remote host and the expected answer. If no payload is specified, the endpoint is assumed to be available if the connection attempt was successful. If send is specified without receive, any response is accepted as OK. If receive is specified without send, no payload is sent, but the client expects to receive a payload in the form of a \"hello message\" or \"banner\" on connect."), + "ssl_verification_mode": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "Controls the verification of server certificates. ", + }, + "ssl_supported_protocols": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + MarkdownDescription: "List of allowed SSL/TLS versions.", + }, + "check_send": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "An optional payload string to send to the remote host.", + }, + "check_receive": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "The expected answer. ", + }, "proxy_url": schema.StringAttribute{ Optional: true, MarkdownDescription: "The URL of the SOCKS5 proxy to use when connecting to the server. The value must be a URL with a scheme of `socks5://`. If the SOCKS5 proxy server requires client authentication, then a username and password can be embedded in the URL. When using a proxy, hostnames are resolved on the proxy server instead of on the client. You can change this behavior by setting the `proxy_use_local_resolver` option.", @@ -394,7 +415,7 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { return nil, err } - params, err := toNormalizedValue(api.Params) + //params, err := toNormalizedValue(api.Params) if err != nil { return nil, err } @@ -411,9 +432,9 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { Alert: toTfAlertConfigV0(api.Alert), APMServiceName: types.StringValue(api.APMServiceName), TimeoutSeconds: types.Int64Value(timeout), - Params: params, - HTTP: http, - TCP: tcp, + //Params: params, + HTTP: http, + TCP: tcp, }, nil } @@ -431,10 +452,10 @@ func toTfTCPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfTCPMonitorFieldsV0 func toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFieldsV0, error) { - proxyHeaders, err := toNormalizedValue(api.ProxyHeaders) - if err != nil { - return nil, err - } + //proxyHeaders, err := toNormalizedValue(api.ProxyHeaders) + //if err != nil { + // return nil, err + //} return &tfHTTPMonitorFieldsV0{ URL: types.StringValue(api.Url), @@ -446,8 +467,8 @@ func toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFields IPv6: types.BoolPointerValue(api.Ipv6), Username: types.StringValue(api.Username), Password: types.StringValue(api.Password), - ProxyHeader: proxyHeaders, - ProxyURL: types.StringValue(api.ProxyUrl), + //ProxyHeader: proxyHeaders, + ProxyURL: types.StringValue(api.ProxyUrl), }, nil } @@ -501,10 +522,10 @@ func (v *tfModelV0) toMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, diag.Diagnostics) { locations := Map[types.String, kbapi.MonitorLocation](v.Locations, func(s types.String) kbapi.MonitorLocation { return kbapi.MonitorLocation(s.ValueString()) }) - params, dg := toJsonObject(v.Params) - if dg.HasError() { - return nil, dg - } + //params, dg := toJsonObject(v.Params) + //if dg.HasError() { + // return nil, dg + //} var alert *kbapi.MonitorAlertConfig if v.Alert != nil { @@ -522,25 +543,25 @@ func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, APMServiceName: v.APMServiceName.ValueString(), TimeoutSeconds: int(v.TimeoutSeconds.ValueInt64()), Namespace: v.SpaceID.ValueString(), - Params: params, - RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(), - }, dg + //Params: params, + RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(), + }, diag.Diagnostics{} //dg } func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { - proxyHeaders, dg := toJsonObject(v.HTTP.ProxyHeader) - if dg.HasError() { - return nil, dg - } - response, dg := toJsonObject(v.HTTP.Response) - if dg.HasError() { - return nil, dg - } - check, dg := toJsonObject(v.HTTP.Check) - if dg.HasError() { - return nil, dg - } - return kbapi.HTTPMonitorFields{ + /* proxyHeaders, dg := toJsonObject(v.HTTP.ProxyHeader) + if dg.HasError() { + return nil, dg + } + response, dg := toJsonObject(v.HTTP.Response) + if dg.HasError() { + return nil, dg + } + check, dg := toJsonObject(v.HTTP.Check) + if dg.HasError() { + return nil, dg + } + */return kbapi.HTTPMonitorFields{ Url: v.HTTP.URL.ValueString(), SslVerificationMode: v.HTTP.SslVerificationMode.ValueString(), SslSupportedProtocols: ValueStringSlice(v.HTTP.SslSupportedProtocols), @@ -550,11 +571,11 @@ func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics Ipv6: v.HTTP.IPv6.ValueBoolPointer(), Username: v.HTTP.Username.ValueString(), Password: v.HTTP.Password.ValueString(), - ProxyHeader: proxyHeaders, - ProxyUrl: v.HTTP.ProxyURL.ValueString(), - Response: response, - Check: check, - }, dg + //ProxyHeader: proxyHeaders, + ProxyUrl: v.HTTP.ProxyURL.ValueString(), + //Response: response, + //Check: check, + }, diag.Diagnostics{} //dg } func (v *tfModelV0) toTCPMonitorFields() kbapi.MonitorFields { diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go index e1bcd483a..395cd61c4 100644 --- a/internal/kibana/synthetics/schema_test.go +++ b/internal/kibana/synthetics/schema_test.go @@ -2,11 +2,11 @@ package synthetics import ( "encoding/json" + "testing" + "github.com/disaster37/go-kibana-rest/v8/kbapi" - "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stretchr/testify/assert" - "testing" ) var ( @@ -38,7 +38,7 @@ func TestToModelV0(t *testing.T) { Schedule: types.Int64Value(0), APMServiceName: types.StringValue(""), TimeoutSeconds: types.Int64Value(0), - Params: jsontypes.NewNormalizedValue("null"), + //Params: jsontypes.NewNormalizedValue("null"), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue(""), SslVerificationMode: types.StringValue(""), @@ -46,8 +46,8 @@ func TestToModelV0(t *testing.T) { Mode: types.StringValue(""), Username: types.StringValue(""), Password: types.StringValue(""), - ProxyHeader: jsontypes.NewNormalizedValue("null"), - ProxyURL: types.StringValue(""), + //ProxyHeader: jsontypes.NewNormalizedValue("null"), + ProxyURL: types.StringValue(""), }, }, }, @@ -63,7 +63,7 @@ func TestToModelV0(t *testing.T) { Schedule: types.Int64Value(0), APMServiceName: types.StringValue(""), TimeoutSeconds: types.Int64Value(0), - Params: jsontypes.NewNormalizedValue("null"), + //Params: jsontypes.NewNormalizedValue("null"), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue(""), SslVerificationMode: types.StringValue(""), @@ -127,7 +127,7 @@ func TestToModelV0(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, APMServiceName: types.StringValue("test-service-http"), TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue("https://example.com"), SslVerificationMode: types.StringValue("full"), @@ -138,8 +138,8 @@ func TestToModelV0(t *testing.T) { IPv6: types.BoolPointerValue(fBool), Username: types.StringValue("user"), Password: types.StringValue("pass"), - ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), - ProxyURL: types.StringValue("https://proxy.com"), + //ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), }, }, }, @@ -184,7 +184,7 @@ func TestToModelV0(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, APMServiceName: types.StringValue("test-service-tcp"), TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue("example.com:9200"), SslVerificationMode: types.StringValue("full"), @@ -247,7 +247,7 @@ func TestToKibanaAPIRequest(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, APMServiceName: types.StringValue("test-service-http"), TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue("https://example.com"), SslVerificationMode: types.StringValue("full"), @@ -258,10 +258,10 @@ func TestToKibanaAPIRequest(t *testing.T) { IPv6: types.BoolPointerValue(fBool), Username: types.StringValue("user"), Password: types.StringValue("pass"), - ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), - ProxyURL: types.StringValue("https://proxy.com"), - Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), - Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), + //ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), + //Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), + //Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), }, }, expected: kibanaAPIRequest{ @@ -309,7 +309,7 @@ func TestToKibanaAPIRequest(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, APMServiceName: types.StringValue("test-service-tcp"), TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue("example.com:9200"), SslVerificationMode: types.StringValue("full"), diff --git a/libs/go-kibana-rest/kibana.go b/libs/go-kibana-rest/kibana.go index 560820a4b..bb8e62948 100644 --- a/libs/go-kibana-rest/kibana.go +++ b/libs/go-kibana-rest/kibana.go @@ -35,6 +35,7 @@ func NewClient(cfg Config) (*Client, error) { } restyClient := resty.New(). + SetDebug(true). SetBaseURL(cfg.Address). SetHeader("kbn-xsrf", "true"). SetHeader("Content-Type", "application/json") diff --git a/provider/plugin_framework.go b/provider/plugin_framework.go index 5a6b7f5f0..182c4fa54 100644 --- a/provider/plugin_framework.go +++ b/provider/plugin_framework.go @@ -2,6 +2,7 @@ package provider import ( "context" + "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "github.com/elastic/terraform-provider-elasticstack/internal/clients" "github.com/elastic/terraform-provider-elasticstack/internal/clients/config" @@ -73,5 +74,6 @@ func (p *Provider) Resources(ctx context.Context) []func() resource.Resource { func() resource.Resource { return &import_saved_objects.Resource{} }, func() resource.Resource { return &data_view.Resource{} }, func() resource.Resource { return &private_location.Resource{} }, + func() resource.Resource { return &synthetics.Resource{} }, } } From 8a372ccb2c0e2f91a6559cccaeed0e774eecedc5 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 9 Aug 2024 10:27:24 +0200 Subject: [PATCH 11/72] fix null pointer --- internal/kibana/synthetics/acc_test.go | 85 ++++++------- internal/kibana/synthetics/create.go | 6 +- internal/kibana/synthetics/delete.go | 4 + internal/kibana/synthetics/read.go | 5 +- internal/kibana/synthetics/schema.go | 143 +++++++++++----------- internal/kibana/synthetics/schema_test.go | 29 ++--- internal/kibana/synthetics/update.go | 6 +- provider/plugin_framework.go | 2 +- 8 files changed, 145 insertions(+), 135 deletions(-) diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index 2c4f3ad5b..85849218d 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -14,6 +14,8 @@ var ( ) const ( + resourceId = "elasticstack_kibana_synthetics_monitor.test-monitor" + providerConfig = ` provider "elasticstack" { elasticsearch {} @@ -35,21 +37,28 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { space_id = "default" agent_policy_id = elasticstack_fleet_agent_policy.test.policy_id } -` - resourceId = "elasticstack_kibana_synthetics_monitor.test" - - httpMonitorConfig = ` -resource "elasticstack_kibana_synthetics_monitor" "test" { +resource "elasticstack_kibana_synthetics_monitor" "test-monitor" { name = "TestMonitorResource" space_id = "default" schedule = 5 private_locations = [elasticstack_kibana_synthetics_private_location.test.label] enabled = true tags = ["a", "b"] + alert = { + status = { + enabled = true + } + tls = { + enabled = true + } + } service_name = "test apm service" timeout = 30 retest_on_failure = true + params = jsonencode({ + "param-name" = "param-value" + }) http = { url = "http://localhost:5601" ssl_verification_mode = "full" @@ -60,51 +69,35 @@ resource "elasticstack_kibana_synthetics_monitor" "test" { ipv6 = false username = "test" password = "test" + proxy_header = jsonencode({"header-name" = "header-value"}) proxy_url = "http://localhost:8080" + response = jsonencode({ + "include_body": "always", + "include_body_max_bytes": "1024", + }) + check = jsonencode({ + "request": { + "method": "POST", + "headers": { + "Content-Type": "application/x-www-form-urlencoded", + }, + "body": "name=first&email=someemail%40someemailprovider.com", + }, + "response": { + "status": [200, 201], + "body": { + "positive": ["foo", "bar"], + }, + }, + }) } } ` /* - alert = { - status = { - enabled = true - } - tls = { - enabled = true - } - } - - # locations = [] - # params = jsonencode({ - # "param-name" = "param-value" - # }) - - # proxy_header = jsonencode({"header-name" = "header-value"}) - # response = jsonencode({ - # "include_body": "always", - # "include_body_max_bytes": "1024", - # }) - # check = jsonencode({ - # "request": { - # "method": "POST", - # "headers": { - # "Content-Type": "application/x-www-form-urlencoded", - # }, - # "body": "name=first&email=someemail%40someemailprovider.com", - # }, - # "response": { - # "status": [200, 201], - # "body": { - # "positive": ["foo", "bar"], - # }, - # }, - # }) - - - check.send = "Hello" - check.receive = "World" - proxy_use_local_resolver = true + check.send = "Hello" + check.receive = "World" + proxy_use_local_resolver = true */ ) @@ -117,7 +110,7 @@ func TestSyntheticMonitorResource(t *testing.T) { // Create and Read testing { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), - Config: providerConfig + httpMonitorConfig, + Config: providerConfig, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet(resourceId, "id"), //resource.TestCheckResourceAttrSet(resourceId, "id"), @@ -129,7 +122,7 @@ func TestSyntheticMonitorResource(t *testing.T) { ResourceName: resourceId, ImportState: true, ImportStateVerify: true, - Config: providerConfig + httpMonitorConfig, + Config: providerConfig, }, // Update and Read testing /* diff --git a/internal/kibana/synthetics/create.go b/internal/kibana/synthetics/create.go index c15ca0703..63b536435 100644 --- a/internal/kibana/synthetics/create.go +++ b/internal/kibana/synthetics/create.go @@ -4,15 +4,19 @@ import ( "context" "fmt" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + + tflog.Info(ctx, "### Create monitor") + kibanaClient := GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return } - var plan *tfModelV0 + var plan *tfModelV0 = new(tfModelV0) diags := request.Plan.Get(ctx, plan) response.Diagnostics.Append(diags...) if response.Diagnostics.HasError() { diff --git a/internal/kibana/synthetics/delete.go b/internal/kibana/synthetics/delete.go index 85bed7206..42f69f33c 100644 --- a/internal/kibana/synthetics/delete.go +++ b/internal/kibana/synthetics/delete.go @@ -5,9 +5,13 @@ import ( "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + + tflog.Info(ctx, "### Delete monitor") + kibanaClient := GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return diff --git a/internal/kibana/synthetics/read.go b/internal/kibana/synthetics/read.go index 0ab8eca9f..1f8ede191 100644 --- a/internal/kibana/synthetics/read.go +++ b/internal/kibana/synthetics/read.go @@ -6,16 +6,19 @@ import ( "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + tflog.Info(ctx, "### Read monitor") + kibanaClient := GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return } - var state *tfModelV0 + var state *tfModelV0 = new(tfModelV0) diags := request.State.Get(ctx, state) response.Diagnostics.Append(diags...) if response.Diagnostics.HasError() { diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index 65181632e..addfbd4e1 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -36,19 +36,19 @@ type tfAlertConfigV0 struct { } type tfHTTPMonitorFieldsV0 struct { - URL types.String `tfsdk:"url"` - SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` - SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` - MaxRedirects types.String `tfsdk:"max_redirects"` - Mode types.String `tfsdk:"mode"` - IPv4 types.Bool `tfsdk:"ipv4"` - IPv6 types.Bool `tfsdk:"ipv6"` - Username types.String `tfsdk:"username"` - Password types.String `tfsdk:"password"` - //ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` - ProxyURL types.String `tfsdk:"proxy_url"` - //Response jsontypes.Normalized `tfsdk:"response"` - //Check jsontypes.Normalized `tfsdk:"check"` + URL types.String `tfsdk:"url"` + SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` + SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` + MaxRedirects types.String `tfsdk:"max_redirects"` + Mode types.String `tfsdk:"mode"` + IPv4 types.Bool `tfsdk:"ipv4"` + IPv6 types.Bool `tfsdk:"ipv6"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` + ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` + ProxyURL types.String `tfsdk:"proxy_url"` + Response jsontypes.Normalized `tfsdk:"response"` + Check jsontypes.Normalized `tfsdk:"check"` } type tfTCPMonitorFieldsV0 struct { @@ -62,21 +62,21 @@ type tfTCPMonitorFieldsV0 struct { } type tfModelV0 struct { - ID types.String `tfsdk:"id"` - Name types.String `tfsdk:"name"` - SpaceID types.String `tfsdk:"space_id"` - Schedule types.Int64 `tfsdk:"schedule"` - Locations []types.String `tfsdk:"locations"` - PrivateLocations []types.String `tfsdk:"private_locations"` - Enabled types.Bool `tfsdk:"enabled"` - Tags []types.String `tfsdk:"tags"` - Alert *tfAlertConfigV0 `tfsdk:"alert"` - APMServiceName types.String `tfsdk:"service_name"` - TimeoutSeconds types.Int64 `tfsdk:"timeout"` - //Params jsontypes.Normalized `tfsdk:"params"` - HTTP *tfHTTPMonitorFieldsV0 `tfsdk:"http"` - TCP *tfTCPMonitorFieldsV0 `tfsdk:"tcp"` - RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + SpaceID types.String `tfsdk:"space_id"` + Schedule types.Int64 `tfsdk:"schedule"` + Locations []types.String `tfsdk:"locations"` + PrivateLocations []types.String `tfsdk:"private_locations"` + Enabled types.Bool `tfsdk:"enabled"` + Tags []types.String `tfsdk:"tags"` + Alert *tfAlertConfigV0 `tfsdk:"alert"` + APMServiceName types.String `tfsdk:"service_name"` + TimeoutSeconds types.Int64 `tfsdk:"timeout"` + Params jsontypes.Normalized `tfsdk:"params"` + HTTP *tfHTTPMonitorFieldsV0 `tfsdk:"http"` + TCP *tfTCPMonitorFieldsV0 `tfsdk:"tcp"` + RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` } func monitorConfigSchema() schema.Schema { @@ -154,13 +154,13 @@ func monitorConfigSchema() schema.Schema { Optional: true, MarkdownDescription: "The monitor timeout in seconds, monitor will fail if it doesn’t complete within this time. Default: `16`", }, - //"params": jsonObjectSchema("Monitor parameters"), + "params": jsonObjectSchema("Monitor parameters"), + "http": httpMonitorFieldsSchema(), + "tcp": tcpMonitorFieldsSchema(), "retest_on_failure": schema.BoolAttribute{ Optional: true, MarkdownDescription: "Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from \"up\" to \"down\". If the result of the retest is also \"down\", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`.", }, - "http": httpMonitorFieldsSchema(), - "tcp": tcpMonitorFieldsSchema(), }, } } @@ -197,7 +197,8 @@ func monitorAlertConfigSchema() schema.Attribute { func httpMonitorFieldsSchema() schema.Attribute { return schema.SingleNestedAttribute{ - Optional: true, + Optional: true, + MarkdownDescription: "TODO", Attributes: map[string]schema.Attribute{ "url": schema.StringAttribute{ Optional: false, @@ -240,13 +241,13 @@ func httpMonitorFieldsSchema() schema.Attribute { Optional: true, MarkdownDescription: "The password for authenticating with the server. The credentials are passed with the request.", }, - //"proxy_header": jsonObjectSchema("Additional headers to send to proxies during CONNECT requests."), + "proxy_header": jsonObjectSchema("Additional headers to send to proxies during CONNECT requests."), "proxy_url": schema.StringAttribute{ Optional: true, MarkdownDescription: "The URL of the proxy to use for this monitor.", }, - //"response": jsonObjectSchema("Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field."), - //"check": jsonObjectSchema("The check request settings."), + "response": jsonObjectSchema("Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field."), + "check": jsonObjectSchema("The check request settings."), }, } } @@ -254,7 +255,7 @@ func httpMonitorFieldsSchema() schema.Attribute { func tcpMonitorFieldsSchema() schema.Attribute { return schema.SingleNestedAttribute{ Optional: true, - MarkdownDescription: "", + MarkdownDescription: "TODO", Attributes: map[string]schema.Attribute{ "host": schema.StringAttribute{ Optional: false, @@ -415,7 +416,7 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { return nil, err } - //params, err := toNormalizedValue(api.Params) + params, err := toNormalizedValue(api.Params) if err != nil { return nil, err } @@ -432,9 +433,9 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { Alert: toTfAlertConfigV0(api.Alert), APMServiceName: types.StringValue(api.APMServiceName), TimeoutSeconds: types.Int64Value(timeout), - //Params: params, - HTTP: http, - TCP: tcp, + Params: params, + HTTP: http, + TCP: tcp, }, nil } @@ -452,10 +453,10 @@ func toTfTCPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfTCPMonitorFieldsV0 func toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFieldsV0, error) { - //proxyHeaders, err := toNormalizedValue(api.ProxyHeaders) - //if err != nil { - // return nil, err - //} + proxyHeaders, err := toNormalizedValue(api.ProxyHeaders) + if err != nil { + return nil, err + } return &tfHTTPMonitorFieldsV0{ URL: types.StringValue(api.Url), @@ -467,8 +468,8 @@ func toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFields IPv6: types.BoolPointerValue(api.Ipv6), Username: types.StringValue(api.Username), Password: types.StringValue(api.Password), - //ProxyHeader: proxyHeaders, - ProxyURL: types.StringValue(api.ProxyUrl), + ProxyHeader: proxyHeaders, + ProxyURL: types.StringValue(api.ProxyUrl), }, nil } @@ -522,10 +523,10 @@ func (v *tfModelV0) toMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, diag.Diagnostics) { locations := Map[types.String, kbapi.MonitorLocation](v.Locations, func(s types.String) kbapi.MonitorLocation { return kbapi.MonitorLocation(s.ValueString()) }) - //params, dg := toJsonObject(v.Params) - //if dg.HasError() { - // return nil, dg - //} + params, dg := toJsonObject(v.Params) + if dg.HasError() { + return nil, dg + } var alert *kbapi.MonitorAlertConfig if v.Alert != nil { @@ -543,25 +544,25 @@ func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, APMServiceName: v.APMServiceName.ValueString(), TimeoutSeconds: int(v.TimeoutSeconds.ValueInt64()), Namespace: v.SpaceID.ValueString(), - //Params: params, - RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(), - }, diag.Diagnostics{} //dg + Params: params, + RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(), + }, dg } func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { - /* proxyHeaders, dg := toJsonObject(v.HTTP.ProxyHeader) - if dg.HasError() { - return nil, dg - } - response, dg := toJsonObject(v.HTTP.Response) - if dg.HasError() { - return nil, dg - } - check, dg := toJsonObject(v.HTTP.Check) - if dg.HasError() { - return nil, dg - } - */return kbapi.HTTPMonitorFields{ + proxyHeaders, dg := toJsonObject(v.HTTP.ProxyHeader) + if dg.HasError() { + return nil, dg + } + response, dg := toJsonObject(v.HTTP.Response) + if dg.HasError() { + return nil, dg + } + check, dg := toJsonObject(v.HTTP.Check) + if dg.HasError() { + return nil, dg + } + return kbapi.HTTPMonitorFields{ Url: v.HTTP.URL.ValueString(), SslVerificationMode: v.HTTP.SslVerificationMode.ValueString(), SslSupportedProtocols: ValueStringSlice(v.HTTP.SslSupportedProtocols), @@ -571,11 +572,11 @@ func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics Ipv6: v.HTTP.IPv6.ValueBoolPointer(), Username: v.HTTP.Username.ValueString(), Password: v.HTTP.Password.ValueString(), - //ProxyHeader: proxyHeaders, - ProxyUrl: v.HTTP.ProxyURL.ValueString(), - //Response: response, - //Check: check, - }, diag.Diagnostics{} //dg + ProxyHeader: proxyHeaders, + ProxyUrl: v.HTTP.ProxyURL.ValueString(), + Response: response, + Check: check, + }, dg } func (v *tfModelV0) toTCPMonitorFields() kbapi.MonitorFields { diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go index 395cd61c4..b8bcd3c5b 100644 --- a/internal/kibana/synthetics/schema_test.go +++ b/internal/kibana/synthetics/schema_test.go @@ -2,6 +2,7 @@ package synthetics import ( "encoding/json" + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" "testing" "github.com/disaster37/go-kibana-rest/v8/kbapi" @@ -38,7 +39,7 @@ func TestToModelV0(t *testing.T) { Schedule: types.Int64Value(0), APMServiceName: types.StringValue(""), TimeoutSeconds: types.Int64Value(0), - //Params: jsontypes.NewNormalizedValue("null"), + Params: jsontypes.NewNormalizedValue("null"), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue(""), SslVerificationMode: types.StringValue(""), @@ -46,8 +47,8 @@ func TestToModelV0(t *testing.T) { Mode: types.StringValue(""), Username: types.StringValue(""), Password: types.StringValue(""), - //ProxyHeader: jsontypes.NewNormalizedValue("null"), - ProxyURL: types.StringValue(""), + ProxyHeader: jsontypes.NewNormalizedValue("null"), + ProxyURL: types.StringValue(""), }, }, }, @@ -63,7 +64,7 @@ func TestToModelV0(t *testing.T) { Schedule: types.Int64Value(0), APMServiceName: types.StringValue(""), TimeoutSeconds: types.Int64Value(0), - //Params: jsontypes.NewNormalizedValue("null"), + Params: jsontypes.NewNormalizedValue("null"), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue(""), SslVerificationMode: types.StringValue(""), @@ -127,7 +128,7 @@ func TestToModelV0(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, APMServiceName: types.StringValue("test-service-http"), TimeoutSeconds: types.Int64Value(30), - //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue("https://example.com"), SslVerificationMode: types.StringValue("full"), @@ -138,8 +139,8 @@ func TestToModelV0(t *testing.T) { IPv6: types.BoolPointerValue(fBool), Username: types.StringValue("user"), Password: types.StringValue("pass"), - //ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), - ProxyURL: types.StringValue("https://proxy.com"), + ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), }, }, }, @@ -184,7 +185,7 @@ func TestToModelV0(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, APMServiceName: types.StringValue("test-service-tcp"), TimeoutSeconds: types.Int64Value(30), - //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue("example.com:9200"), SslVerificationMode: types.StringValue("full"), @@ -247,7 +248,7 @@ func TestToKibanaAPIRequest(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, APMServiceName: types.StringValue("test-service-http"), TimeoutSeconds: types.Int64Value(30), - //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue("https://example.com"), SslVerificationMode: types.StringValue("full"), @@ -258,10 +259,10 @@ func TestToKibanaAPIRequest(t *testing.T) { IPv6: types.BoolPointerValue(fBool), Username: types.StringValue("user"), Password: types.StringValue("pass"), - //ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), - ProxyURL: types.StringValue("https://proxy.com"), - //Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), - //Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), + ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), + Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), + Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), }, }, expected: kibanaAPIRequest{ @@ -309,7 +310,7 @@ func TestToKibanaAPIRequest(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, APMServiceName: types.StringValue("test-service-tcp"), TimeoutSeconds: types.Int64Value(30), - //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue("example.com:9200"), SslVerificationMode: types.StringValue("full"), diff --git a/internal/kibana/synthetics/update.go b/internal/kibana/synthetics/update.go index 7956de019..1c34d060a 100644 --- a/internal/kibana/synthetics/update.go +++ b/internal/kibana/synthetics/update.go @@ -5,15 +5,19 @@ import ( "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { + + tflog.Info(ctx, "### Update monitor") + kibanaClient := GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return } - var plan *tfModelV0 + var plan *tfModelV0 = new(tfModelV0) diags := request.Plan.Get(ctx, plan) response.Diagnostics.Append(diags...) if response.Diagnostics.HasError() { diff --git a/provider/plugin_framework.go b/provider/plugin_framework.go index 182c4fa54..4e26ba8e8 100644 --- a/provider/plugin_framework.go +++ b/provider/plugin_framework.go @@ -2,12 +2,12 @@ package provider import ( "context" - "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "github.com/elastic/terraform-provider-elasticstack/internal/clients" "github.com/elastic/terraform-provider-elasticstack/internal/clients/config" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/data_view" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/import_saved_objects" + "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics/private_location" "github.com/elastic/terraform-provider-elasticstack/internal/schema" "github.com/hashicorp/terraform-plugin-framework/datasource" From 843890c05f6a7605081ca92bc38971e1fbe1dba8 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 9 Aug 2024 13:09:18 +0200 Subject: [PATCH 12/72] limit number of supported options due-to kibana api --- docs/resources/kibana_synthetics_monitor.md | 97 +++++++++ internal/kibana/synthetics/acc_test.go | 32 +-- internal/kibana/synthetics/schema.go | 206 ++++++++++---------- internal/kibana/synthetics/schema_test.go | 41 ++-- 4 files changed, 224 insertions(+), 152 deletions(-) create mode 100644 docs/resources/kibana_synthetics_monitor.md diff --git a/docs/resources/kibana_synthetics_monitor.md b/docs/resources/kibana_synthetics_monitor.md new file mode 100644 index 000000000..c3b8311ef --- /dev/null +++ b/docs/resources/kibana_synthetics_monitor.md @@ -0,0 +1,97 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "elasticstack_kibana_synthetics_monitor Resource - terraform-provider-elasticstack" +subcategory: "" +description: |- + Synthetics monitor config, see https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html for more details. The monitor must have one of the following: http, tcp, icmp or browser. +--- + +# elasticstack_kibana_synthetics_monitor (Resource) + +Synthetics monitor config, see https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html for more details. The monitor must have one of the following: http, tcp, icmp or browser. + + + + +## Schema + +### Required + +- `name` (String) The monitor’s name. + +### Optional + +- `alert` (Attributes) Alert configuration. Default: `{ status: { enabled: true }, tls: { enabled: true } }`. (see [below for nested schema](#nestedatt--alert)) +- `enabled` (Boolean) Whether the monitor is enabled. Default: `true` +- `http` (Attributes) TODO (see [below for nested schema](#nestedatt--http)) +- `locations` (List of String) Where to deploy the monitor. Monitors can be deployed in multiple locations so that you can detect differences in availability and response times across those locations. +- `private_locations` (List of String) These Private Locations refer to locations hosted and managed by you, whereas locations are hosted by Elastic. You can specify a Private Location using the location’s name. +- `schedule` (Number) The monitor’s schedule in minutes. Supported values are 1, 3, 5, 10, 15, 30, 60, 120 and 240. +- `service_name` (String) The APM service name. +- `space_id` (String) The namespace field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \, /, ?, ", <, >, |, whitespace, ,, #, :, or -. Default: `default` +- `tags` (List of String) An array of tags. +- `tcp` (Attributes) TODO (see [below for nested schema](#nestedatt--tcp)) +- `timeout` (Number) The monitor timeout in seconds, monitor will fail if it doesn’t complete within this time. Default: `16` + +### Read-Only + +- `id` (String) Generated identifier for the monitor + + +### Nested Schema for `alert` + +Optional: + +- `status` (Attributes) (see [below for nested schema](#nestedatt--alert--status)) +- `tls` (Attributes) (see [below for nested schema](#nestedatt--alert--tls)) + + +### Nested Schema for `alert.status` + +Optional: + +- `enabled` (Boolean) + + + +### Nested Schema for `alert.tls` + +Optional: + +- `enabled` (Boolean) + + + + +### Nested Schema for `http` + +Required: + +- `url` (String) URL to monitor. + +Optional: + +- `ipv4` (Boolean) Whether to ping using the ipv4 protocol. +- `ipv6` (Boolean) Whether to ping using the ipv6 protocol. +- `max_redirects` (String) The maximum number of redirects to follow. Default: `0` +- `mode` (String) The mode of the monitor. Can be "all" or "any". If you’re using a DNS-load balancer and want to ping every IP address for the specified hostname, you should use all. +- `proxy_url` (String) The URL of the proxy to use for this monitor. +- `ssl_supported_protocols` (List of String) List of allowed SSL/TLS versions. +- `ssl_verification_mode` (String) Controls the verification of server certificates. + + + +### Nested Schema for `tcp` + +Required: + +- `host` (String) The host to monitor; it can be an IP address or a hostname. The host can include the port using a colon (e.g., "example.com:9200"). + +Optional: + +- `check_receive` (String) The expected answer. +- `check_send` (String) An optional payload string to send to the remote host. +- `proxy_url` (String) The URL of the SOCKS5 proxy to use when connecting to the server. The value must be a URL with a scheme of `socks5://`. If the SOCKS5 proxy server requires client authentication, then a username and password can be embedded in the URL. When using a proxy, hostnames are resolved on the proxy server instead of on the client. You can change this behavior by setting the `proxy_use_local_resolver` option. +- `proxy_use_local_resolver` (Boolean) A Boolean value that determines whether hostnames are resolved locally instead of being resolved on the proxy server. The default value is false, which means that name resolution occurs on the proxy server. +- `ssl_supported_protocols` (List of String) List of allowed SSL/TLS versions. +- `ssl_verification_mode` (String) Controls the verification of server certificates. diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index 85849218d..f18384f9a 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -14,7 +14,7 @@ var ( ) const ( - resourceId = "elasticstack_kibana_synthetics_monitor.test-monitor" + resourceId = "elasticstack_kibana_synthetics_monitor.http-monitor" providerConfig = ` provider "elasticstack" { @@ -38,8 +38,8 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { agent_policy_id = elasticstack_fleet_agent_policy.test.policy_id } -resource "elasticstack_kibana_synthetics_monitor" "test-monitor" { - name = "TestMonitorResource" +resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { + name = "TestHttpMonitorResource" space_id = "default" schedule = 5 private_locations = [elasticstack_kibana_synthetics_private_location.test.label] @@ -55,10 +55,6 @@ resource "elasticstack_kibana_synthetics_monitor" "test-monitor" { } service_name = "test apm service" timeout = 30 - retest_on_failure = true - params = jsonencode({ - "param-name" = "param-value" - }) http = { url = "http://localhost:5601" ssl_verification_mode = "full" @@ -67,29 +63,7 @@ resource "elasticstack_kibana_synthetics_monitor" "test-monitor" { mode = "any" ipv4 = true ipv6 = false - username = "test" - password = "test" - proxy_header = jsonencode({"header-name" = "header-value"}) proxy_url = "http://localhost:8080" - response = jsonencode({ - "include_body": "always", - "include_body_max_bytes": "1024", - }) - check = jsonencode({ - "request": { - "method": "POST", - "headers": { - "Content-Type": "application/x-www-form-urlencoded", - }, - "body": "name=first&email=someemail%40someemailprovider.com", - }, - "response": { - "status": [200, 201], - "body": { - "positive": ["foo", "bar"], - }, - }, - }) } } ` diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index addfbd4e1..2a716b063 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -1,10 +1,8 @@ package synthetics import ( - "encoding/json" "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" - "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -36,19 +34,21 @@ type tfAlertConfigV0 struct { } type tfHTTPMonitorFieldsV0 struct { - URL types.String `tfsdk:"url"` - SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` - SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` - MaxRedirects types.String `tfsdk:"max_redirects"` - Mode types.String `tfsdk:"mode"` - IPv4 types.Bool `tfsdk:"ipv4"` - IPv6 types.Bool `tfsdk:"ipv6"` - Username types.String `tfsdk:"username"` - Password types.String `tfsdk:"password"` - ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` - ProxyURL types.String `tfsdk:"proxy_url"` - Response jsontypes.Normalized `tfsdk:"response"` - Check jsontypes.Normalized `tfsdk:"check"` + URL types.String `tfsdk:"url"` + SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` + SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` + MaxRedirects types.String `tfsdk:"max_redirects"` + Mode types.String `tfsdk:"mode"` + IPv4 types.Bool `tfsdk:"ipv4"` + IPv6 types.Bool `tfsdk:"ipv6"` + ProxyURL types.String `tfsdk:"proxy_url"` + // commented out due-to https://github.com/elastic/kibana/issues/189906 + // it leads to terraform Error: Provider produced inconsistent result after apply + //ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` + //Username types.String `tfsdk:"username"` + //Password types.String `tfsdk:"password"` + //Response jsontypes.Normalized `tfsdk:"response"` + //Check jsontypes.Normalized `tfsdk:"check"` } type tfTCPMonitorFieldsV0 struct { @@ -73,10 +73,12 @@ type tfModelV0 struct { Alert *tfAlertConfigV0 `tfsdk:"alert"` APMServiceName types.String `tfsdk:"service_name"` TimeoutSeconds types.Int64 `tfsdk:"timeout"` - Params jsontypes.Normalized `tfsdk:"params"` HTTP *tfHTTPMonitorFieldsV0 `tfsdk:"http"` TCP *tfTCPMonitorFieldsV0 `tfsdk:"tcp"` - RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` + // commented out due-to https://github.com/elastic/kibana/issues/189906 + // it leads to terraform Error: Provider produced inconsistent result after apply + //Params jsontypes.Normalized `tfsdk:"params"` + //RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` } func monitorConfigSchema() schema.Schema { @@ -154,24 +156,24 @@ func monitorConfigSchema() schema.Schema { Optional: true, MarkdownDescription: "The monitor timeout in seconds, monitor will fail if it doesn’t complete within this time. Default: `16`", }, - "params": jsonObjectSchema("Monitor parameters"), - "http": httpMonitorFieldsSchema(), - "tcp": tcpMonitorFieldsSchema(), - "retest_on_failure": schema.BoolAttribute{ - Optional: true, - MarkdownDescription: "Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from \"up\" to \"down\". If the result of the retest is also \"down\", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`.", - }, + //"params": jsonObjectSchema("Monitor parameters"), + "http": httpMonitorFieldsSchema(), + "tcp": tcpMonitorFieldsSchema(), + //"retest_on_failure": schema.BoolAttribute{ + // Optional: true, + // MarkdownDescription: "Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from \"up\" to \"down\". If the result of the retest is also \"down\", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`.", + //}, }, } } -func jsonObjectSchema(doc string) schema.Attribute { - return schema.StringAttribute{ - Optional: true, - MarkdownDescription: fmt.Sprintf("%s. Raw JSON object, use `jsonencode` function to represent JSON", doc), - CustomType: jsontypes.NormalizedType{}, - } -} +//func jsonObjectSchema(doc string) schema.Attribute { +// return schema.StringAttribute{ +// Optional: true, +// MarkdownDescription: fmt.Sprintf("%s. Raw JSON object, use `jsonencode` function to represent JSON", doc), +// CustomType: jsontypes.NormalizedType{}, +// } +//} func statusConfigSchema() schema.Attribute { return schema.SingleNestedAttribute{ @@ -233,21 +235,21 @@ func httpMonitorFieldsSchema() schema.Attribute { Optional: true, MarkdownDescription: "Whether to ping using the ipv6 protocol.", }, - "username": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "The username for authenticating with the server. The credentials are passed with the request.", - }, - "password": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "The password for authenticating with the server. The credentials are passed with the request.", - }, - "proxy_header": jsonObjectSchema("Additional headers to send to proxies during CONNECT requests."), + //"username": schema.StringAttribute{ + // Optional: true, + // MarkdownDescription: "The username for authenticating with the server. The credentials are passed with the request.", + //}, + //"password": schema.StringAttribute{ + // Optional: true, + // MarkdownDescription: "The password for authenticating with the server. The credentials are passed with the request.", + //}, + //"proxy_header": jsonObjectSchema("Additional headers to send to proxies during CONNECT requests."), "proxy_url": schema.StringAttribute{ Optional: true, MarkdownDescription: "The URL of the proxy to use for this monitor.", }, - "response": jsonObjectSchema("Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field."), - "check": jsonObjectSchema("The check request settings."), + //"response": jsonObjectSchema("Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field."), + //"check": jsonObjectSchema("The check request settings."), }, } } @@ -348,25 +350,25 @@ func StringSliceValue(v []string) []types.String { return res } -func toNormalizedValue(jsObj kbapi.JsonObject) (jsontypes.Normalized, error) { - res, err := json.Marshal(jsObj) - if err != nil { - return jsontypes.NewNormalizedUnknown(), err - } - return jsontypes.NewNormalizedValue(string(res)), nil -} - -func toJsonObject(v jsontypes.Normalized) (kbapi.JsonObject, diag.Diagnostics) { - if v.IsNull() { - return nil, diag.Diagnostics{} - } - var res kbapi.JsonObject - dg := v.Unmarshal(&res) - if dg.HasError() { - return nil, dg - } - return res, diag.Diagnostics{} -} +//func toNormalizedValue(jsObj kbapi.JsonObject) (jsontypes.Normalized, error) { +// res, err := json.Marshal(jsObj) +// if err != nil { +// return jsontypes.NewNormalizedUnknown(), err +// } +// return jsontypes.NewNormalizedValue(string(res)), nil +//} +// +//func toJsonObject(v jsontypes.Normalized) (kbapi.JsonObject, diag.Diagnostics) { +// if v.IsNull() { +// return nil, diag.Diagnostics{} +// } +// var res kbapi.JsonObject +// dg := v.Unmarshal(&res) +// if dg.HasError() { +// return nil, dg +// } +// return res, diag.Diagnostics{} +//} func stringToInt64(v string) (int64, error) { var res int64 @@ -416,10 +418,10 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { return nil, err } - params, err := toNormalizedValue(api.Params) - if err != nil { - return nil, err - } + //params, err := toNormalizedValue(api.Params) + //if err != nil { + // return nil, err + //} return &tfModelV0{ ID: types.StringValue(string(api.Id)), @@ -433,9 +435,9 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { Alert: toTfAlertConfigV0(api.Alert), APMServiceName: types.StringValue(api.APMServiceName), TimeoutSeconds: types.Int64Value(timeout), - Params: params, - HTTP: http, - TCP: tcp, + //Params: params, + HTTP: http, + TCP: tcp, }, nil } @@ -453,10 +455,10 @@ func toTfTCPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfTCPMonitorFieldsV0 func toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFieldsV0, error) { - proxyHeaders, err := toNormalizedValue(api.ProxyHeaders) - if err != nil { - return nil, err - } + //proxyHeaders, err := toNormalizedValue(api.ProxyHeaders) + //if err != nil { + // return nil, err + //} return &tfHTTPMonitorFieldsV0{ URL: types.StringValue(api.Url), @@ -466,10 +468,10 @@ func toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFields Mode: types.StringValue(string(api.Mode)), IPv4: types.BoolPointerValue(api.Ipv4), IPv6: types.BoolPointerValue(api.Ipv6), - Username: types.StringValue(api.Username), - Password: types.StringValue(api.Password), - ProxyHeader: proxyHeaders, - ProxyURL: types.StringValue(api.ProxyUrl), + //Username: types.StringValue(api.Username), + //Password: types.StringValue(api.Password), + //ProxyHeader: proxyHeaders, + ProxyURL: types.StringValue(api.ProxyUrl), }, nil } @@ -523,10 +525,10 @@ func (v *tfModelV0) toMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, diag.Diagnostics) { locations := Map[types.String, kbapi.MonitorLocation](v.Locations, func(s types.String) kbapi.MonitorLocation { return kbapi.MonitorLocation(s.ValueString()) }) - params, dg := toJsonObject(v.Params) - if dg.HasError() { - return nil, dg - } + //params, dg := toJsonObject(v.Params) + //if dg.HasError() { + // return nil, dg + //} var alert *kbapi.MonitorAlertConfig if v.Alert != nil { @@ -544,24 +546,24 @@ func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, APMServiceName: v.APMServiceName.ValueString(), TimeoutSeconds: int(v.TimeoutSeconds.ValueInt64()), Namespace: v.SpaceID.ValueString(), - Params: params, - RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(), - }, dg + //Params: params, + //RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(), + }, diag.Diagnostics{} //dg } func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { - proxyHeaders, dg := toJsonObject(v.HTTP.ProxyHeader) - if dg.HasError() { - return nil, dg - } - response, dg := toJsonObject(v.HTTP.Response) - if dg.HasError() { - return nil, dg - } - check, dg := toJsonObject(v.HTTP.Check) - if dg.HasError() { - return nil, dg - } + //proxyHeaders, dg := toJsonObject(v.HTTP.ProxyHeader) + //if dg.HasError() { + // return nil, dg + //} + //response, dg := toJsonObject(v.HTTP.Response) + //if dg.HasError() { + // return nil, dg + //} + //check, dg := toJsonObject(v.HTTP.Check) + //if dg.HasError() { + // return nil, dg + //} return kbapi.HTTPMonitorFields{ Url: v.HTTP.URL.ValueString(), SslVerificationMode: v.HTTP.SslVerificationMode.ValueString(), @@ -570,13 +572,13 @@ func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics Mode: kbapi.HttpMonitorMode(v.HTTP.Mode.ValueString()), Ipv4: v.HTTP.IPv4.ValueBoolPointer(), Ipv6: v.HTTP.IPv6.ValueBoolPointer(), - Username: v.HTTP.Username.ValueString(), - Password: v.HTTP.Password.ValueString(), - ProxyHeader: proxyHeaders, - ProxyUrl: v.HTTP.ProxyURL.ValueString(), - Response: response, - Check: check, - }, dg + //Username: v.HTTP.Username.ValueString(), + //Password: v.HTTP.Password.ValueString(), + //ProxyHeader: proxyHeaders, + ProxyUrl: v.HTTP.ProxyURL.ValueString(), + //Response: response, + //Check: check, + }, diag.Diagnostics{} //dg } func (v *tfModelV0) toTCPMonitorFields() kbapi.MonitorFields { diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go index b8bcd3c5b..f738ce7a5 100644 --- a/internal/kibana/synthetics/schema_test.go +++ b/internal/kibana/synthetics/schema_test.go @@ -2,7 +2,6 @@ package synthetics import ( "encoding/json" - "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" "testing" "github.com/disaster37/go-kibana-rest/v8/kbapi" @@ -39,16 +38,16 @@ func TestToModelV0(t *testing.T) { Schedule: types.Int64Value(0), APMServiceName: types.StringValue(""), TimeoutSeconds: types.Int64Value(0), - Params: jsontypes.NewNormalizedValue("null"), + //Params: jsontypes.NewNormalizedValue("null"), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue(""), SslVerificationMode: types.StringValue(""), MaxRedirects: types.StringValue(""), Mode: types.StringValue(""), - Username: types.StringValue(""), - Password: types.StringValue(""), - ProxyHeader: jsontypes.NewNormalizedValue("null"), - ProxyURL: types.StringValue(""), + //Username: types.StringValue(""), + //Password: types.StringValue(""), + //ProxyHeader: jsontypes.NewNormalizedValue("null"), + ProxyURL: types.StringValue(""), }, }, }, @@ -64,7 +63,7 @@ func TestToModelV0(t *testing.T) { Schedule: types.Int64Value(0), APMServiceName: types.StringValue(""), TimeoutSeconds: types.Int64Value(0), - Params: jsontypes.NewNormalizedValue("null"), + //Params: jsontypes.NewNormalizedValue("null"), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue(""), SslVerificationMode: types.StringValue(""), @@ -128,7 +127,7 @@ func TestToModelV0(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, APMServiceName: types.StringValue("test-service-http"), TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue("https://example.com"), SslVerificationMode: types.StringValue("full"), @@ -137,10 +136,10 @@ func TestToModelV0(t *testing.T) { Mode: types.StringValue("all"), IPv4: types.BoolPointerValue(tBool), IPv6: types.BoolPointerValue(fBool), - Username: types.StringValue("user"), - Password: types.StringValue("pass"), - ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), - ProxyURL: types.StringValue("https://proxy.com"), + //Username: types.StringValue("user"), + //Password: types.StringValue("pass"), + //ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), }, }, }, @@ -185,7 +184,7 @@ func TestToModelV0(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, APMServiceName: types.StringValue("test-service-tcp"), TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue("example.com:9200"), SslVerificationMode: types.StringValue("full"), @@ -248,7 +247,7 @@ func TestToKibanaAPIRequest(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, APMServiceName: types.StringValue("test-service-http"), TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue("https://example.com"), SslVerificationMode: types.StringValue("full"), @@ -257,12 +256,12 @@ func TestToKibanaAPIRequest(t *testing.T) { Mode: types.StringValue("all"), IPv4: types.BoolPointerValue(tBool), IPv6: types.BoolPointerValue(fBool), - Username: types.StringValue("user"), - Password: types.StringValue("pass"), - ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), - ProxyURL: types.StringValue("https://proxy.com"), - Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), - Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), + //Username: types.StringValue("user"), + //Password: types.StringValue("pass"), + //ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), + //Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), + //Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), }, }, expected: kibanaAPIRequest{ @@ -310,7 +309,7 @@ func TestToKibanaAPIRequest(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, APMServiceName: types.StringValue("test-service-tcp"), TimeoutSeconds: types.Int64Value(30), - Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue("example.com:9200"), SslVerificationMode: types.StringValue("full"), From f7199f866ab338e375b7c6b4652717c6c598a8be Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 9 Aug 2024 13:57:47 +0200 Subject: [PATCH 13/72] wip --- internal/kibana/synthetics/schema_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go index f738ce7a5..77c8521c5 100644 --- a/internal/kibana/synthetics/schema_test.go +++ b/internal/kibana/synthetics/schema_test.go @@ -276,7 +276,7 @@ func TestToKibanaAPIRequest(t *testing.T) { APMServiceName: "test-service-http", Namespace: "default", TimeoutSeconds: 30, - Params: kbapi.JsonObject{"param1": "value1"}, + //Params: kbapi.JsonObject{"param1": "value1"}, }, fields: kbapi.HTTPMonitorFields{ Url: "https://example.com", @@ -286,12 +286,12 @@ func TestToKibanaAPIRequest(t *testing.T) { Mode: "all", Ipv4: tBool, Ipv6: fBool, - Username: "user", - Password: "pass", - ProxyHeader: kbapi.JsonObject{"header1": "value1"}, - ProxyUrl: "https://proxy.com", - Response: kbapi.JsonObject{"response1": "value1"}, - Check: kbapi.JsonObject{"check1": "value1"}, + //Username: "user", + //Password: "pass", + //ProxyHeader: kbapi.JsonObject{"header1": "value1"}, + ProxyUrl: "https://proxy.com", + //Response: kbapi.JsonObject{"response1": "value1"}, + //Check: kbapi.JsonObject{"check1": "value1"}, }, }, }, @@ -332,7 +332,7 @@ func TestToKibanaAPIRequest(t *testing.T) { APMServiceName: "test-service-tcp", Namespace: "default", TimeoutSeconds: 30, - Params: kbapi.JsonObject{"param1": "value1"}, + //Params: kbapi.JsonObject{"param1": "value1"}, }, fields: kbapi.TCPMonitorFields{ Host: "example.com:9200", From 4103571e45813d0876fdbf209ba1be8abe071849 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 9 Aug 2024 17:41:06 +0200 Subject: [PATCH 14/72] increase es docker memory due-to es error process ended by code 137 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a0143c5e7..efd8f1b0e 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ ELASTICSEARCH_ENDPOINTS ?= http://$(ELASTICSEARCH_NAME):9200 ELASTICSEARCH_USERNAME ?= elastic ELASTICSEARCH_PASSWORD ?= password ELASTICSEARCH_NETWORK ?= elasticstack-network -ELASTICSEARCH_MEM ?= 1024m +ELASTICSEARCH_MEM ?= 2048m KIBANA_NAME ?= terraform-elasticstack-kb KIBANA_ENDPOINT ?= http://$(KIBANA_NAME):5601 From 43cd84446d0eaa83dc2273fc77f49f8eb7787429 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 9 Aug 2024 17:51:03 +0200 Subject: [PATCH 15/72] try to increase GH workflow service memory --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index da7f2fa21..19edf854a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -69,7 +69,7 @@ jobs: ELASTIC_PASSWORD: ${{ env.ELASTIC_PASSWORD }} ports: - 9200:9200 - options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 + options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 --memory=2048m kibana: image: docker.elastic.co/kibana/kibana:${{ matrix.version }} env: From 4171e755d01231d09650d7cda13ac972dfc8eef4 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 9 Aug 2024 18:09:45 +0200 Subject: [PATCH 16/72] wip --- .github/workflows/test.yml | 5 +++-- Makefile | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 19edf854a..94ee26a28 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -60,16 +60,17 @@ jobs: image: docker.elastic.co/elasticsearch/elasticsearch:${{ matrix.version }} env: discovery.type: single-node - xpack.license.self_generated.type: trial xpack.security.enabled: true xpack.security.authc.api_key.enabled: true + xpack.security.authc.token.enabled: true xpack.watcher.enabled: true + xpack.license.self_generated.type: trial repositories.url.allowed_urls: https://example.com/* path.repo: /tmp ELASTIC_PASSWORD: ${{ env.ELASTIC_PASSWORD }} ports: - 9200:9200 - options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 --memory=2048m + options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 kibana: image: docker.elastic.co/kibana/kibana:${{ matrix.version }} env: diff --git a/Makefile b/Makefile index efd8f1b0e..3d2aca2fd 100644 --- a/Makefile +++ b/Makefile @@ -131,6 +131,7 @@ docker-kibana: docker-network docker-elasticsearch set-kibana-password ## Start -e ELASTICSEARCH_USERNAME=$(KIBANA_SYSTEM_USERNAME) \ -e ELASTICSEARCH_PASSWORD=$(KIBANA_SYSTEM_PASSWORD) \ -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d \ + -e "xpack.fleet.enabled=true" \ -e "logging.root.level=debug" \ --name $(KIBANA_NAME) \ --network $(ELASTICSEARCH_NETWORK) \ From 797b32225860f841e79c1b3ee36dcc3f4209f510 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 09:16:34 +0200 Subject: [PATCH 17/72] test connectio to package registry --- .github/workflows/test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 94ee26a28..17cb452b6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,6 +47,9 @@ jobs: - name: Lint run: make lint + - name: Test conection + run: curl https://epr.elastic.co/ + test: name: Matrix Acceptance Test needs: build From 64036745dabaa7b0e2469cf2dd2d4e9e82877eec Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 09:25:40 +0200 Subject: [PATCH 18/72] test in docker --- .github/workflows/test.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 17cb452b6..5d5df0036 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,9 +47,6 @@ jobs: - name: Lint run: make lint - - name: Test conection - run: curl https://epr.elastic.co/ - test: name: Matrix Acceptance Test needs: build @@ -138,6 +135,11 @@ jobs: ELASTICSEARCH_USERNAME: "elastic" ELASTICSEARCH_PASSWORD: ${{ env.ELASTIC_PASSWORD }} + - id: test-package-registry-connection + name: Test Package Registry Connection + run: |- + curl https://epr.elastic.co/ + - name: TF acceptance tests timeout-minutes: 10 run: make testacc From f95a42b443ef1890e46485ea87f9d918ae73df9b Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 09:32:35 +0200 Subject: [PATCH 19/72] try to run package registry in docker --- .github/workflows/test.yml | 13 ++++++++----- libs/go-kibana-rest/docker-compose.yml | 12 +++++++++++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5d5df0036..613ce8c9f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,8 +80,16 @@ jobs: ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d xpack.fleet.enabled: true + xpack.fleet.registryUrl: https://package-registry:8080 ports: - 5601:5601 + package-registry: + image: docker.elastic.co/package-registry/package-registry:v1.24.0 + env: + EPR_FEATURE_PROXY_MODE: true + EPR_PROXY_TO: https://epr.elastic.co + ports: + - 8080:8080 timeout-minutes: 15 strategy: @@ -135,11 +143,6 @@ jobs: ELASTICSEARCH_USERNAME: "elastic" ELASTICSEARCH_PASSWORD: ${{ env.ELASTIC_PASSWORD }} - - id: test-package-registry-connection - name: Test Package Registry Connection - run: |- - curl https://epr.elastic.co/ - - name: TF acceptance tests timeout-minutes: 10 run: make testacc diff --git a/libs/go-kibana-rest/docker-compose.yml b/libs/go-kibana-rest/docker-compose.yml index fe3dbb953..ff717a079 100644 --- a/libs/go-kibana-rest/docker-compose.yml +++ b/libs/go-kibana-rest/docker-compose.yml @@ -31,10 +31,20 @@ services: xpack.security.http.ssl.enabled: false xpack.license.self_generated.type: trial XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: min-32-byte-long-strong-encryption-key + xpack.fleet.registryUrl: https://package-registry:8080 links: - elasticsearch:es ports: - "5601:5601/tcp" depends_on: set-kibana-password: - condition: service_completed_successfully \ No newline at end of file + condition: service_completed_successfully + package-registry: + condition: service_healthy + package-registry: + image: docker.elastic.co/package-registry/package-registry:v1.24.0 + environment: + EPR_FEATURE_PROXY_MODE: true + EPR_PROXY_TO: https://epr.elastic.co + ports: + - "8080:8080/tcp" \ No newline at end of file From 6a08e219d1870e19a3dd71e166340d81f8c47cf9 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 11:30:50 +0200 Subject: [PATCH 20/72] try xpack.task_manager.max_attempts --- .github/workflows/test.yml | 9 +-------- libs/go-kibana-rest/docker-compose.yml | 10 ---------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 613ce8c9f..33185641d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,16 +80,9 @@ jobs: ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d xpack.fleet.enabled: true - xpack.fleet.registryUrl: https://package-registry:8080 + xpack.task_manager.max_attempts: 10 ports: - 5601:5601 - package-registry: - image: docker.elastic.co/package-registry/package-registry:v1.24.0 - env: - EPR_FEATURE_PROXY_MODE: true - EPR_PROXY_TO: https://epr.elastic.co - ports: - - 8080:8080 timeout-minutes: 15 strategy: diff --git a/libs/go-kibana-rest/docker-compose.yml b/libs/go-kibana-rest/docker-compose.yml index ff717a079..7cba778d7 100644 --- a/libs/go-kibana-rest/docker-compose.yml +++ b/libs/go-kibana-rest/docker-compose.yml @@ -31,7 +31,6 @@ services: xpack.security.http.ssl.enabled: false xpack.license.self_generated.type: trial XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: min-32-byte-long-strong-encryption-key - xpack.fleet.registryUrl: https://package-registry:8080 links: - elasticsearch:es ports: @@ -39,12 +38,3 @@ services: depends_on: set-kibana-password: condition: service_completed_successfully - package-registry: - condition: service_healthy - package-registry: - image: docker.elastic.co/package-registry/package-registry:v1.24.0 - environment: - EPR_FEATURE_PROXY_MODE: true - EPR_PROXY_TO: https://epr.elastic.co - ports: - - "8080:8080/tcp" \ No newline at end of file From d0d32e57b48b302a2127ed0a9ef54cc5e2ca1759 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 12:13:15 +0200 Subject: [PATCH 21/72] debug kibana --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 33185641d..37a1633a1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,6 +81,7 @@ jobs: XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d xpack.fleet.enabled: true xpack.task_manager.max_attempts: 10 + logging.root.level: debug ports: - 5601:5601 From 1990615af2eae6f4fe9a3ce4f053d498ef2f6506 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 13:11:32 +0200 Subject: [PATCH 22/72] try xpack.fleet.isAirGapped: true --- .github/workflows/test.yml | 4 ++-- Makefile | 1 + libs/go-kibana-rest/docker-compose.yml | 9 +++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 37a1633a1..d73a52cee 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,6 +63,7 @@ jobs: xpack.security.enabled: true xpack.security.authc.api_key.enabled: true xpack.security.authc.token.enabled: true + xpack.security.http.ssl.enabled: false xpack.watcher.enabled: true xpack.license.self_generated.type: trial repositories.url.allowed_urls: https://example.com/* @@ -80,8 +81,7 @@ jobs: ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d xpack.fleet.enabled: true - xpack.task_manager.max_attempts: 10 - logging.root.level: debug + xpack.fleet.isAirGapped: true ports: - 5601:5601 diff --git a/Makefile b/Makefile index 3d2aca2fd..ab7dd6de6 100644 --- a/Makefile +++ b/Makefile @@ -132,6 +132,7 @@ docker-kibana: docker-network docker-elasticsearch set-kibana-password ## Start -e ELASTICSEARCH_PASSWORD=$(KIBANA_SYSTEM_PASSWORD) \ -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d \ -e "xpack.fleet.enabled=true" \ + -e "xpack.fleet.isAirGapped=true" \ -e "logging.root.level=debug" \ --name $(KIBANA_NAME) \ --network $(ELASTICSEARCH_NETWORK) \ diff --git a/libs/go-kibana-rest/docker-compose.yml b/libs/go-kibana-rest/docker-compose.yml index 7cba778d7..4ef04a2da 100644 --- a/libs/go-kibana-rest/docker-compose.yml +++ b/libs/go-kibana-rest/docker-compose.yml @@ -5,10 +5,10 @@ services: environment: cluster.name: test discovery.type: single-node - ELASTIC_PASSWORD: changeme - xpack.security.enabled: "true" + xpack.security.enabled: true xpack.security.http.ssl.enabled: false xpack.license.self_generated.type: trial + ELASTIC_PASSWORD: changeme ports: - "9200:9200/tcp" set-kibana-password: @@ -25,12 +25,13 @@ services: kibana: image: docker.elastic.co/kibana/kibana:8.14.3 environment: + SERVER_NAME: kibana ELASTICSEARCH_HOSTS: http://es:9200 ELASTICSEARCH_USERNAME: kibana_system ELASTICSEARCH_PASSWORD: changeme - xpack.security.http.ssl.enabled: false - xpack.license.self_generated.type: trial XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: min-32-byte-long-strong-encryption-key + xpack.fleet.enabled: true + xpack.fleet.isAirGapped: true links: - elasticsearch:es ports: From 7763a41e8c3f29837ee230f514961949e76e12fd Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 13:13:22 +0200 Subject: [PATCH 23/72] try 8.15.0 --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d73a52cee..2e89b8ec5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -106,6 +106,7 @@ jobs: - '8.12.2' - '8.13.4' - '8.14.3' + - '8.15.0' steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 From ccc25f9384d64576f2b6896882b5637f64c8640d Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 14:39:12 +0200 Subject: [PATCH 24/72] try package-registry distribution --- .github/workflows/test.yml | 6 +++++- Makefile | 3 +-- internal/kibana/synthetics/acc_test.go | 21 +++++++++++++++++---- libs/go-kibana-rest/docker-compose.yml | 1 - 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2e89b8ec5..a17b9ac3b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,9 +81,13 @@ jobs: ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d xpack.fleet.enabled: true - xpack.fleet.isAirGapped: true + xpack.fleet.registryUrl: https://package-registry:8080 ports: - 5601:5601 + package-registry: + image: docker.elastic.co/package-registry/distribution:${{ matrix.version }} + ports: + - 8080:8080 timeout-minutes: 15 strategy: diff --git a/Makefile b/Makefile index ab7dd6de6..62ce2adef 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ SWAGGER_VERSION ?= 8.7 GOVERSION ?= $(shell grep -e '^go' go.mod | cut -f 2 -d ' ') -STACK_VERSION ?= 8.14.3 +STACK_VERSION ?= 8.15.0 ELASTICSEARCH_NAME ?= terraform-elasticstack-es ELASTICSEARCH_ENDPOINTS ?= http://$(ELASTICSEARCH_NAME):9200 @@ -132,7 +132,6 @@ docker-kibana: docker-network docker-elasticsearch set-kibana-password ## Start -e ELASTICSEARCH_PASSWORD=$(KIBANA_SYSTEM_PASSWORD) \ -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d \ -e "xpack.fleet.enabled=true" \ - -e "xpack.fleet.isAirGapped=true" \ -e "logging.root.level=debug" \ --name $(KIBANA_NAME) \ --network $(ELASTICSEARCH_NETWORK) \ diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index f18384f9a..2f9e309c3 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -14,7 +14,7 @@ var ( ) const ( - resourceId = "elasticstack_kibana_synthetics_monitor.http-monitor" + httpMonitorId = "elasticstack_kibana_synthetics_monitor.http-monitor" providerConfig = ` provider "elasticstack" { @@ -86,14 +86,27 @@ func TestSyntheticMonitorResource(t *testing.T) { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), Config: providerConfig, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttrSet(resourceId, "id"), - //resource.TestCheckResourceAttrSet(resourceId, "id"), + resource.TestCheckResourceAttrSet(httpMonitorId, "id"), + resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource"), + resource.TestCheckResourceAttr(httpMonitorId, "space_id", "default"), + resource.TestCheckResourceAttr(httpMonitorId, "schedule", "5"), + resource.TestCheckResourceAttr(httpMonitorId, "private_locations.#", "1"), + resource.TestCheckResourceAttrSet(httpMonitorId, "private_locations.0"), + resource.TestCheckResourceAttr(httpMonitorId, "enabled", "true"), + resource.TestCheckResourceAttr(httpMonitorId, "tags.#", "2"), + resource.TestCheckResourceAttr(httpMonitorId, "tags.0", "a"), + resource.TestCheckResourceAttr(httpMonitorId, "tags.1", "b"), + resource.TestCheckResourceAttr(httpMonitorId, "alert.status.enabled", "true"), + resource.TestCheckResourceAttr(httpMonitorId, "alert.tls.enabled", "true"), + resource.TestCheckResourceAttr(httpMonitorId, "service_name", "test apm service"), + resource.TestCheckResourceAttr(httpMonitorId, "timeout", "30"), + resource.TestCheckResourceAttr(httpMonitorId, "http.url", "http://localhost:5601"), ), }, // ImportState testing { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), - ResourceName: resourceId, + ResourceName: httpMonitorId, ImportState: true, ImportStateVerify: true, Config: providerConfig, diff --git a/libs/go-kibana-rest/docker-compose.yml b/libs/go-kibana-rest/docker-compose.yml index 4ef04a2da..169867a57 100644 --- a/libs/go-kibana-rest/docker-compose.yml +++ b/libs/go-kibana-rest/docker-compose.yml @@ -31,7 +31,6 @@ services: ELASTICSEARCH_PASSWORD: changeme XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: min-32-byte-long-strong-encryption-key xpack.fleet.enabled: true - xpack.fleet.isAirGapped: true links: - elasticsearch:es ports: From 761003e1fcde1922d9bab89253bccf8968483642 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 14:49:17 +0200 Subject: [PATCH 25/72] try to use lite version --- .github/workflows/test.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a17b9ac3b..2cb46eea9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,13 +81,14 @@ jobs: ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d xpack.fleet.enabled: true - xpack.fleet.registryUrl: https://package-registry:8080 + xpack.fleet.registryUrl: http://package-registry:8080 ports: - 5601:5601 package-registry: - image: docker.elastic.co/package-registry/distribution:${{ matrix.version }} + image: docker.elastic.co/package-registry/distribution:lite ports: - 8080:8080 + options: --health-cmd="curl -f -L http://127.0.0.1:8080/health" timeout-minutes: 15 strategy: From 1a33422f46a198b39f3b1b683513eeaa7eef56d2 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 15:00:55 +0200 Subject: [PATCH 26/72] add re-try for package registry distribution health --- .github/workflows/test.yml | 3 +-- Makefile | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2cb46eea9..061949de3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -88,7 +88,7 @@ jobs: image: docker.elastic.co/package-registry/distribution:lite ports: - 8080:8080 - options: --health-cmd="curl -f -L http://127.0.0.1:8080/health" + options: --health-cmd="curl -f -L http://127.0.0.1:8080/health" --health-interval=10s --health-timeout=5s --health-retries=10 timeout-minutes: 15 strategy: @@ -111,7 +111,6 @@ jobs: - '8.12.2' - '8.13.4' - '8.14.3' - - '8.15.0' steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 diff --git a/Makefile b/Makefile index 62ce2adef..3d2aca2fd 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ SWAGGER_VERSION ?= 8.7 GOVERSION ?= $(shell grep -e '^go' go.mod | cut -f 2 -d ' ') -STACK_VERSION ?= 8.15.0 +STACK_VERSION ?= 8.14.3 ELASTICSEARCH_NAME ?= terraform-elasticstack-es ELASTICSEARCH_ENDPOINTS ?= http://$(ELASTICSEARCH_NAME):9200 From b5fdb4141a6456ba4ba86e2195a434371a4600cc Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 15:11:38 +0200 Subject: [PATCH 27/72] enable debug for plugins.fleet --- .github/workflows/test.yml | 14 ++++++++------ internal/kibana/synthetics/acc_test.go | 10 ++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 061949de3..96f282e2c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,14 +81,16 @@ jobs: ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d xpack.fleet.enabled: true - xpack.fleet.registryUrl: http://package-registry:8080 +# xpack.fleet.registryUrl: http://package-registry:8080 + logging.loggers[0].name: plugins.fleet + logging.loggers[0].level: debug ports: - 5601:5601 - package-registry: - image: docker.elastic.co/package-registry/distribution:lite - ports: - - 8080:8080 - options: --health-cmd="curl -f -L http://127.0.0.1:8080/health" --health-interval=10s --health-timeout=5s --health-retries=10 +# package-registry: +# image: docker.elastic.co/package-registry/distribution:lite +# ports: +# - 8080:8080 +# options: --health-cmd="curl -f -L http://127.0.0.1:8080/health" --health-interval=10s --health-timeout=5s --health-retries=10 timeout-minutes: 15 strategy: diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index 2f9e309c3..a46b5b553 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -101,6 +101,16 @@ func TestSyntheticMonitorResource(t *testing.T) { resource.TestCheckResourceAttr(httpMonitorId, "service_name", "test apm service"), resource.TestCheckResourceAttr(httpMonitorId, "timeout", "30"), resource.TestCheckResourceAttr(httpMonitorId, "http.url", "http://localhost:5601"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_verification_mode", "full"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.#", "3"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.0", "TLSv1.0"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.1", "TLSv1.1"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.2", "TLSv1.2"), + resource.TestCheckResourceAttr(httpMonitorId, "http.max_redirects", "10"), + resource.TestCheckResourceAttr(httpMonitorId, "http.mode", "any"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ipv4", "true"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ipv6", "false"), + resource.TestCheckResourceAttr(httpMonitorId, "http.proxy_url", "http://localhost:8080"), ), }, // ImportState testing From 7c499ca7cd2655def6e350505271fad0b2938e3c Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 16:12:00 +0200 Subject: [PATCH 28/72] try to set kibana loggin --- .github/workflows/test.yml | 10 +-- Makefile | 2 +- internal/kibana/synthetics/acc_test.go | 83 ++++++++++++++++--- kibana-config/kibana.yml | 11 +++ .../kbapi/api.kibana_synthetics_test.go | 2 + 5 files changed, 88 insertions(+), 20 deletions(-) create mode 100644 kibana-config/kibana.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 96f282e2c..3a0f71023 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,6 +74,8 @@ jobs: options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 kibana: image: docker.elastic.co/kibana/kibana:${{ matrix.version }} + volumes: + - /kibana-config/:/usr/share/kibana/config/ env: SERVER_NAME: kibana ELASTICSEARCH_HOSTS: http://elasticsearch:9200 @@ -81,16 +83,8 @@ jobs: ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d xpack.fleet.enabled: true -# xpack.fleet.registryUrl: http://package-registry:8080 - logging.loggers[0].name: plugins.fleet - logging.loggers[0].level: debug ports: - 5601:5601 -# package-registry: -# image: docker.elastic.co/package-registry/distribution:lite -# ports: -# - 8080:8080 -# options: --health-cmd="curl -f -L http://127.0.0.1:8080/health" --health-interval=10s --health-timeout=5s --health-retries=10 timeout-minutes: 15 strategy: diff --git a/Makefile b/Makefile index 631b9bac7..03f822eaa 100644 --- a/Makefile +++ b/Makefile @@ -133,7 +133,7 @@ docker-kibana: docker-network docker-elasticsearch set-kibana-password ## Start -e ELASTICSEARCH_PASSWORD=$(KIBANA_SYSTEM_PASSWORD) \ -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d \ -e "xpack.fleet.enabled=true" \ - -e "logging.root.level=debug" \ + -v ./kibana-config/:/usr/share/kibana/config/ \ --name $(KIBANA_NAME) \ --network $(ELASTICSEARCH_NETWORK) \ docker.elastic.co/kibana/kibana:$(STACK_VERSION); \ diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index a46b5b553..a653f8476 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -22,6 +22,9 @@ provider "elasticstack" { kibana {} fleet{} } +` + + privateLocationConfig = ` resource "elasticstack_fleet_agent_policy" "test" { name = "TestMonitorResource Agent Policy - test" @@ -38,6 +41,10 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { agent_policy_id = elasticstack_fleet_agent_policy.test.policy_id } +` + + httpMonitorConfig = ` + resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { name = "TestHttpMonitorResource" space_id = "default" @@ -66,6 +73,38 @@ resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { proxy_url = "http://localhost:8080" } } +` + + httpMonitorUpdated = ` +resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { + name = "TestHttpMonitorResource Updated" + space_id = "default" + schedule = 10 + private_locations = [elasticstack_kibana_synthetics_private_location.test.label] + enabled = false + tags = ["c", "d", "e"] + alert = { + status = { + enabled = true + } + tls = { + enabled = false + } + } + service_name = "test apm service" + timeout = 30 + http = { + url = "http://localhost:8080" + ssl_verification_mode = "full" + ssl_supported_protocols = ["TLSv1.2"] + max_redirects = "10" + mode = "all" + ipv4 = true + ipv6 = true + proxy_url = "" + } +} + ` /* @@ -84,7 +123,7 @@ func TestSyntheticMonitorResource(t *testing.T) { // Create and Read testing { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), - Config: providerConfig, + Config: providerConfig + privateLocationConfig + httpMonitorConfig, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet(httpMonitorId, "id"), resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource"), @@ -119,18 +158,40 @@ func TestSyntheticMonitorResource(t *testing.T) { ResourceName: httpMonitorId, ImportState: true, ImportStateVerify: true, - Config: providerConfig, + Config: providerConfig + privateLocationConfig + httpMonitorConfig, }, // Update and Read testing - /* - { - SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), - Config: "", // TODO - Check: resource.ComposeAggregateTestCheckFunc( - // TODO - ), - }, - */ + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + ResourceName: httpMonitorId, + Config: providerConfig + privateLocationConfig + httpMonitorUpdated, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(httpMonitorId, "id"), + resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource Updated"), + resource.TestCheckResourceAttr(httpMonitorId, "space_id", "default"), + resource.TestCheckResourceAttr(httpMonitorId, "schedule", "10"), + resource.TestCheckResourceAttr(httpMonitorId, "private_locations.#", "1"), + resource.TestCheckResourceAttrSet(httpMonitorId, "private_locations.0"), + resource.TestCheckResourceAttr(httpMonitorId, "enabled", "false"), + resource.TestCheckResourceAttr(httpMonitorId, "tags.#", "3"), + resource.TestCheckResourceAttr(httpMonitorId, "tags.0", "c"), + resource.TestCheckResourceAttr(httpMonitorId, "tags.1", "d"), + resource.TestCheckResourceAttr(httpMonitorId, "tags.1", "e"), + resource.TestCheckResourceAttr(httpMonitorId, "alert.status.enabled", "true"), + resource.TestCheckResourceAttr(httpMonitorId, "alert.tls.enabled", "false"), + resource.TestCheckResourceAttr(httpMonitorId, "service_name", "test apm service"), + resource.TestCheckResourceAttr(httpMonitorId, "timeout", "30"), + resource.TestCheckResourceAttr(httpMonitorId, "http.url", "http://localhost:8080"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_verification_mode", "full"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.#", "1"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.2", "TLSv1.2"), + resource.TestCheckResourceAttr(httpMonitorId, "http.max_redirects", "10"), + resource.TestCheckResourceAttr(httpMonitorId, "http.mode", "all"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ipv4", "true"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ipv6", "true"), + resource.TestCheckNoResourceAttr(httpMonitorId, "http.proxy_url"), + ), + }, // Delete testing automatically occurs in TestCase }, }) diff --git a/kibana-config/kibana.yml b/kibana-config/kibana.yml new file mode 100644 index 000000000..f4f3804aa --- /dev/null +++ b/kibana-config/kibana.yml @@ -0,0 +1,11 @@ +server.host: "0.0.0.0" +server.shutdownTimeout: "5s" +elasticsearch.hosts: [ "http://terraform-elasticstack-es:9200" ] + +server.oas.enabled: true + +#logging.root.level: debug + +logging.loggers: + - name: plugins.fleet + level: debug diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go index 58d461906..3b82ec7be 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go @@ -50,6 +50,8 @@ func testWithPolicy(t *testing.T, client *resty.Client, namespace string, f func f(policy.Item.Id) } +// TODO: test update method when set an optional parameter to `null` + func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { type TestConfig struct { From c60d641f0aec458489fd45a988f65e9379dcfc81 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 16:21:41 +0200 Subject: [PATCH 29/72] try relative path --- .github/workflows/test.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3a0f71023..6e121ef07 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -75,7 +75,7 @@ jobs: kibana: image: docker.elastic.co/kibana/kibana:${{ matrix.version }} volumes: - - /kibana-config/:/usr/share/kibana/config/ + - ./kibana-config/:/usr/share/kibana/config/ env: SERVER_NAME: kibana ELASTICSEARCH_HOSTS: http://elasticsearch:9200 @@ -91,21 +91,21 @@ jobs: fail-fast: false matrix: version: - - '7.17.13' - - '8.0.1' - - '8.1.3' - - '8.2.3' - - '8.3.3' - - '8.4.3' - - '8.5.3' - - '8.6.2' - - '8.7.1' - - '8.8.2' - - '8.9.2' - - '8.10.3' - - '8.11.4' - - '8.12.2' - - '8.13.4' +# - '7.17.13' +# - '8.0.1' +# - '8.1.3' +# - '8.2.3' +# - '8.3.3' +# - '8.4.3' +# - '8.5.3' +# - '8.6.2' +# - '8.7.1' +# - '8.8.2' +# - '8.9.2' +# - '8.10.3' +# - '8.11.4' +# - '8.12.2' +# - '8.13.4' - '8.14.3' steps: - uses: actions/checkout@v4 From 9b04aa3b6894d59d747ac62df2112c4d20d5eb92 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 16:29:39 +0200 Subject: [PATCH 30/72] explore GH workflow env --- .github/workflows/test.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6e121ef07..d6ed95c88 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,6 +47,12 @@ jobs: - name: Lint run: make lint + - name: Check env + run: |- + pwd + ls -la + echo "${{ github.workspace }}" + test: name: Matrix Acceptance Test needs: build @@ -75,7 +81,7 @@ jobs: kibana: image: docker.elastic.co/kibana/kibana:${{ matrix.version }} volumes: - - ./kibana-config/:/usr/share/kibana/config/ + - ${{ github.workspace }}/kibana-config:/usr/share/kibana/config env: SERVER_NAME: kibana ELASTICSEARCH_HOSTS: http://elasticsearch:9200 From 06f67c370a135453b01aec3cdc74a478192afc2f Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 16:32:09 +0200 Subject: [PATCH 31/72] wip --- kibana-config/kibana.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kibana-config/kibana.yml b/kibana-config/kibana.yml index f4f3804aa..d1c33dee4 100644 --- a/kibana-config/kibana.yml +++ b/kibana-config/kibana.yml @@ -1,6 +1,6 @@ server.host: "0.0.0.0" server.shutdownTimeout: "5s" -elasticsearch.hosts: [ "http://terraform-elasticstack-es:9200" ] +elasticsearch.hosts: [ "http://elasticsearch:9200" ] server.oas.enabled: true From 792c424d8b021aa87a219adaea9b11ae1170edcc Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 16:37:34 +0200 Subject: [PATCH 32/72] wip --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d6ed95c88..63b375f48 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,7 +50,7 @@ jobs: - name: Check env run: |- pwd - ls -la + ls -la ${{ github.workspace }}/kibana-config echo "${{ github.workspace }}" test: From 3e219392019bf09cf984b3644dbe26ba5adf6f5b Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 16:39:47 +0200 Subject: [PATCH 33/72] wip --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 63b375f48..ef2f9ad4e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,7 +81,7 @@ jobs: kibana: image: docker.elastic.co/kibana/kibana:${{ matrix.version }} volumes: - - ${{ github.workspace }}/kibana-config:/usr/share/kibana/config + - ${{ github.workspace }}/kibana-config/:/usr/share/kibana/config/ env: SERVER_NAME: kibana ELASTICSEARCH_HOSTS: http://elasticsearch:9200 From 0b77e80cf2422a4468e56288ac53ffb839979c3b Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 16:50:31 +0200 Subject: [PATCH 34/72] add status check for kibana service --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ef2f9ad4e..fdd6062f4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,7 +81,7 @@ jobs: kibana: image: docker.elastic.co/kibana/kibana:${{ matrix.version }} volumes: - - ${{ github.workspace }}/kibana-config/:/usr/share/kibana/config/ + - ${{ github.workspace }}/kibana-config/kibana.yml:/usr/share/kibana/config/kibana.yml env: SERVER_NAME: kibana ELASTICSEARCH_HOSTS: http://elasticsearch:9200 @@ -91,6 +91,7 @@ jobs: xpack.fleet.enabled: true ports: - 5601:5601 + options: --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 timeout-minutes: 15 strategy: From 6bc5ae3bc8b85c9808348c9fbbf7de87ec71192d Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 16:53:16 +0200 Subject: [PATCH 35/72] use dirs --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fdd6062f4..18964f5af 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,7 +81,7 @@ jobs: kibana: image: docker.elastic.co/kibana/kibana:${{ matrix.version }} volumes: - - ${{ github.workspace }}/kibana-config/kibana.yml:/usr/share/kibana/config/kibana.yml + - ${{ github.workspace }}/kibana-config/:/usr/share/kibana/config/ env: SERVER_NAME: kibana ELASTICSEARCH_HOSTS: http://elasticsearch:9200 From c1ca74ef11199af3a8d087161c3940fa83f973d7 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 16:59:37 +0200 Subject: [PATCH 36/72] wip --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 18964f5af..3f576b840 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,7 +81,7 @@ jobs: kibana: image: docker.elastic.co/kibana/kibana:${{ matrix.version }} volumes: - - ${{ github.workspace }}/kibana-config/:/usr/share/kibana/config/ + - ${{ github.workspace }}/kibana-config:/usr/share/kibana/config env: SERVER_NAME: kibana ELASTICSEARCH_HOSTS: http://elasticsearch:9200 From b267654e5c3de99e52be346d592a379373aa9147 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 17:02:51 +0200 Subject: [PATCH 37/72] try workaround due-to missing checkout --- .github/workflows/test.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3f576b840..efa12943c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -91,7 +91,6 @@ jobs: xpack.fleet.enabled: true ports: - 5601:5601 - options: --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 timeout-minutes: 15 strategy: @@ -127,6 +126,11 @@ jobs: - name: Get dependencies run: make vendor + - name: restart kibana with mounted volume + uses: docker://docker + with: + args: docker restart kibana + - name: Setup Kibana user run: make set-kibana-password env: From a46bda71df69c2eec66686384023b223edb64b1d Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 17:07:07 +0200 Subject: [PATCH 38/72] try start --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index efa12943c..142df8a8d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -129,7 +129,7 @@ jobs: - name: restart kibana with mounted volume uses: docker://docker with: - args: docker restart kibana + args: docker start kibana - name: Setup Kibana user run: make set-kibana-password From a99ba14a8ab0ff09a6914b064b92c6fe8885b8a7 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 17:12:25 +0200 Subject: [PATCH 39/72] try to run kibana manually --- .github/workflows/test.yml | 40 ++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 142df8a8d..61d1664d5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -78,19 +78,19 @@ jobs: ports: - 9200:9200 options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 - kibana: - image: docker.elastic.co/kibana/kibana:${{ matrix.version }} - volumes: - - ${{ github.workspace }}/kibana-config:/usr/share/kibana/config - env: - SERVER_NAME: kibana - ELASTICSEARCH_HOSTS: http://elasticsearch:9200 - ELASTICSEARCH_USERNAME: ${{ env.KIBANA_SYSTEM_USERNAME }} - ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} - XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d - xpack.fleet.enabled: true - ports: - - 5601:5601 +# kibana: +# image: docker.elastic.co/kibana/kibana:${{ matrix.version }} +# volumes: +# - ${{ github.workspace }}/kibana-config:/usr/share/kibana/config +# env: +# SERVER_NAME: kibana +# ELASTICSEARCH_HOSTS: http://elasticsearch:9200 +# ELASTICSEARCH_USERNAME: ${{ env.KIBANA_SYSTEM_USERNAME }} +# ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} +# XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d +# xpack.fleet.enabled: true +# ports: +# - 5601:5601 timeout-minutes: 15 strategy: @@ -126,10 +126,20 @@ jobs: - name: Get dependencies run: make vendor - - name: restart kibana with mounted volume + - name: Start kibana with mounted volume uses: docker://docker with: - args: docker start kibana + args: docker run -d -p 5601:5601 \ + -e SERVER_NAME=kibana \ + -e ELASTICSEARCH_HOSTS="http://elasticsearch:9200" \ + -e ELASTICSEARCH_USERNAME=${{ env.KIBANA_SYSTEM_USERNAME }} \ + -e ELASTICSEARCH_PASSWORD=${{ env.KIBANA_SYSTEM_PASSWORD }} \ + -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d \ + -e SERVER_SSL_ENABLED=true \ + -e "logging.root.level=debug" \ + -v ${{ github.workspace }}/kibana-config:/usr/share/kibana/config \ + --name kibana \ + docker.elastic.co/kibana/kibana:${{ matrix.version }} - name: Setup Kibana user run: make set-kibana-password From 2fee65652ae78645b9b7082b1618394f9d2d3259 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 17:15:44 +0200 Subject: [PATCH 40/72] try run --- .github/workflows/test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61d1664d5..b2ec43513 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -127,9 +127,7 @@ jobs: run: make vendor - name: Start kibana with mounted volume - uses: docker://docker - with: - args: docker run -d -p 5601:5601 \ + run: docker run -d -p 5601:5601 \ -e SERVER_NAME=kibana \ -e ELASTICSEARCH_HOSTS="http://elasticsearch:9200" \ -e ELASTICSEARCH_USERNAME=${{ env.KIBANA_SYSTEM_USERNAME }} \ From f49d151574e4282dc0287fd91464f8c06f29ab69 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 17:19:17 +0200 Subject: [PATCH 41/72] fixing docker: invalid reference format. --- .github/workflows/test.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b2ec43513..bcd9ce55f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -127,16 +127,15 @@ jobs: run: make vendor - name: Start kibana with mounted volume - run: docker run -d -p 5601:5601 \ - -e SERVER_NAME=kibana \ - -e ELASTICSEARCH_HOSTS="http://elasticsearch:9200" \ - -e ELASTICSEARCH_USERNAME=${{ env.KIBANA_SYSTEM_USERNAME }} \ - -e ELASTICSEARCH_PASSWORD=${{ env.KIBANA_SYSTEM_PASSWORD }} \ - -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d \ - -e SERVER_SSL_ENABLED=true \ - -e "logging.root.level=debug" \ - -v ${{ github.workspace }}/kibana-config:/usr/share/kibana/config \ - --name kibana \ + run: docker run -d -p 5601:5601 + -e SERVER_NAME=kibana + -e ELASTICSEARCH_HOSTS="http://elasticsearch:9200" + -e ELASTICSEARCH_USERNAME=${{ env.KIBANA_SYSTEM_USERNAME }} + -e ELASTICSEARCH_PASSWORD=${{ env.KIBANA_SYSTEM_PASSWORD }} + -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d + -e "xpack.fleet.enabled=true" + -v ${{ github.workspace }}/kibana-config:/usr/share/kibana/config + --name kibana docker.elastic.co/kibana/kibana:${{ matrix.version }} - name: Setup Kibana user From bdff2100b7995e04614de817d86fa1ec47c594df Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 12 Aug 2024 17:29:32 +0200 Subject: [PATCH 42/72] wip --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bcd9ce55f..9ecf90ce1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -136,6 +136,7 @@ jobs: -e "xpack.fleet.enabled=true" -v ${{ github.workspace }}/kibana-config:/usr/share/kibana/config --name kibana + --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 docker.elastic.co/kibana/kibana:${{ matrix.version }} - name: Setup Kibana user From 83019cba7933fb06f933e5357a08eb9478e5f73f Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 08:05:34 +0200 Subject: [PATCH 43/72] wip --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9ecf90ce1..308ad7513 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -136,6 +136,7 @@ jobs: -e "xpack.fleet.enabled=true" -v ${{ github.workspace }}/kibana-config:/usr/share/kibana/config --name kibana + --network ${{ job.container.network }} --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 docker.elastic.co/kibana/kibana:${{ matrix.version }} From a70fb1d152964f45c940c61a707f7ca560a091d2 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 08:40:07 +0200 Subject: [PATCH 44/72] try the idea with env vars and longopts from kibana --- .github/workflows/test.yml | 55 ++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 308ad7513..c98f3233a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -78,19 +78,22 @@ jobs: ports: - 9200:9200 options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 -# kibana: -# image: docker.elastic.co/kibana/kibana:${{ matrix.version }} -# volumes: -# - ${{ github.workspace }}/kibana-config:/usr/share/kibana/config -# env: -# SERVER_NAME: kibana -# ELASTICSEARCH_HOSTS: http://elasticsearch:9200 -# ELASTICSEARCH_USERNAME: ${{ env.KIBANA_SYSTEM_USERNAME }} -# ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} -# XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d -# xpack.fleet.enabled: true -# ports: -# - 5601:5601 + kibana: + image: docker.elastic.co/kibana/kibana:${{ matrix.version }} + volumes: + - ${{ github.workspace }}/kibana-config:/usr/share/kibana/config + env: + SERVER_NAME: kibana + ELASTICSEARCH_HOSTS: http://elasticsearch:9200 + ELASTICSEARCH_USERNAME: ${{ env.KIBANA_SYSTEM_USERNAME }} + ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} + XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d + logging.root.level: debug + xpack.fleet.enabled: true + LOGGING_ROOT_LEVEL: debug + XPACK_FLEET_ENABLED: true + ports: + - 5601:5601 timeout-minutes: 15 strategy: @@ -126,19 +129,19 @@ jobs: - name: Get dependencies run: make vendor - - name: Start kibana with mounted volume - run: docker run -d -p 5601:5601 - -e SERVER_NAME=kibana - -e ELASTICSEARCH_HOSTS="http://elasticsearch:9200" - -e ELASTICSEARCH_USERNAME=${{ env.KIBANA_SYSTEM_USERNAME }} - -e ELASTICSEARCH_PASSWORD=${{ env.KIBANA_SYSTEM_PASSWORD }} - -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d - -e "xpack.fleet.enabled=true" - -v ${{ github.workspace }}/kibana-config:/usr/share/kibana/config - --name kibana - --network ${{ job.container.network }} - --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 - docker.elastic.co/kibana/kibana:${{ matrix.version }} +# - name: Start kibana with mounted volume +# run: docker run -d -p 5601:5601 +# -e SERVER_NAME=kibana +# -e ELASTICSEARCH_HOSTS="http://elasticsearch:9200" +# -e ELASTICSEARCH_USERNAME=${{ env.KIBANA_SYSTEM_USERNAME }} +# -e ELASTICSEARCH_PASSWORD=${{ env.KIBANA_SYSTEM_PASSWORD }} +# -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d +# -e "xpack.fleet.enabled=true" +# -v ${{ github.workspace }}/kibana-config:/usr/share/kibana/config +# --name kibana +# --network ${{ job.container.network }} +# --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 +# docker.elastic.co/kibana/kibana:${{ matrix.version }} - name: Setup Kibana user run: make set-kibana-password From f105d973e8007439e7a4f5f19d3df81f16b2c11e Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 08:45:22 +0200 Subject: [PATCH 45/72] forgot to remove mounted volume --- .github/workflows/test.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c98f3233a..706b6dc76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,20 +80,17 @@ jobs: options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10 kibana: image: docker.elastic.co/kibana/kibana:${{ matrix.version }} - volumes: - - ${{ github.workspace }}/kibana-config:/usr/share/kibana/config env: SERVER_NAME: kibana ELASTICSEARCH_HOSTS: http://elasticsearch:9200 ELASTICSEARCH_USERNAME: ${{ env.KIBANA_SYSTEM_USERNAME }} ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d - logging.root.level: debug - xpack.fleet.enabled: true LOGGING_ROOT_LEVEL: debug XPACK_FLEET_ENABLED: true ports: - 5601:5601 + options: --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 timeout-minutes: 15 strategy: From cc827e856ec94d9e1d6ad1fb97cdb4d674f9555f Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 12:02:13 +0200 Subject: [PATCH 46/72] add acc tests for tcp monitor --- .github/workflows/test.yml | 7 - Makefile | 5 +- docs/resources/kibana_synthetics_monitor.md | 2 - internal/kibana/synthetics/acc_test.go | 140 +++++- internal/kibana/synthetics/schema.go | 33 +- internal/kibana/synthetics/schema_test.go | 18 +- kibana-config/kibana.yml | 11 - libs/go-kibana-rest/docker-compose.yml | 1 - test.json | 516 ++++++++++++++++++++ 9 files changed, 674 insertions(+), 59 deletions(-) delete mode 100644 kibana-config/kibana.yml create mode 100644 test.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 706b6dc76..be3600829 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,12 +47,6 @@ jobs: - name: Lint run: make lint - - name: Check env - run: |- - pwd - ls -la ${{ github.workspace }}/kibana-config - echo "${{ github.workspace }}" - test: name: Matrix Acceptance Test needs: build @@ -87,7 +81,6 @@ jobs: ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d LOGGING_ROOT_LEVEL: debug - XPACK_FLEET_ENABLED: true ports: - 5601:5601 options: --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 diff --git a/Makefile b/Makefile index 03f822eaa..aa5d5eb15 100644 --- a/Makefile +++ b/Makefile @@ -132,8 +132,7 @@ docker-kibana: docker-network docker-elasticsearch set-kibana-password ## Start -e ELASTICSEARCH_USERNAME=$(KIBANA_SYSTEM_USERNAME) \ -e ELASTICSEARCH_PASSWORD=$(KIBANA_SYSTEM_PASSWORD) \ -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d \ - -e "xpack.fleet.enabled=true" \ - -v ./kibana-config/:/usr/share/kibana/config/ \ + -e LOGGING_ROOT_LEVEL=debug \ --name $(KIBANA_NAME) \ --network $(ELASTICSEARCH_NETWORK) \ docker.elastic.co/kibana/kibana:$(STACK_VERSION); \ @@ -158,7 +157,7 @@ docker-kibana-with-tls: docker-network docker-elasticsearch set-kibana-password -e SERVER_SSL_CERTIFICATE=/certs/localhost+1.pem \ -e SERVER_SSL_KEY=/certs/localhost+1-key.pem \ -e SERVER_SSL_ENABLED=true \ - -e "logging.root.level=debug" \ + -e LOGGING_ROOT_LEVEL=debug \ --name $(KIBANA_NAME) \ --network $(ELASTICSEARCH_NETWORK) \ docker.elastic.co/kibana/kibana:$(STACK_VERSION); \ diff --git a/docs/resources/kibana_synthetics_monitor.md b/docs/resources/kibana_synthetics_monitor.md index c3b8311ef..d274455c4 100644 --- a/docs/resources/kibana_synthetics_monitor.md +++ b/docs/resources/kibana_synthetics_monitor.md @@ -89,8 +89,6 @@ Required: Optional: -- `check_receive` (String) The expected answer. -- `check_send` (String) An optional payload string to send to the remote host. - `proxy_url` (String) The URL of the SOCKS5 proxy to use when connecting to the server. The value must be a URL with a scheme of `socks5://`. If the SOCKS5 proxy server requires client authentication, then a username and password can be embedded in the URL. When using a proxy, hostnames are resolved on the proxy server instead of on the client. You can change this behavior by setting the `proxy_use_local_resolver` option. - `proxy_use_local_resolver` (Boolean) A Boolean value that determines whether hostnames are resolved locally instead of being resolved on the proxy server. The default value is false, which means that name resolution occurs on the proxy server. - `ssl_supported_protocols` (List of String) List of allowed SSL/TLS versions. diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index a653f8476..53fb9a39f 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -15,6 +15,7 @@ var ( const ( httpMonitorId = "elasticstack_kibana_synthetics_monitor.http-monitor" + tcpMonitorId = "elasticstack_kibana_synthetics_monitor.tcp-monitor" providerConfig = ` provider "elasticstack" { @@ -101,18 +102,69 @@ resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { mode = "all" ipv4 = true ipv6 = true - proxy_url = "" + proxy_url = "http://localhost" } } ` - /* - check.send = "Hello" - check.receive = "World" + tcpMonitorConfig = ` + +resource "elasticstack_kibana_synthetics_monitor" "tcp-monitor" { + name = "TestTcpMonitorResource" + space_id = "default" + schedule = 5 + private_locations = [elasticstack_kibana_synthetics_private_location.test.label] + enabled = true + tags = ["a", "b"] + alert = { + status = { + enabled = true + } + tls = { + enabled = true + } + } + service_name = "test apm service" + timeout = 30 + tcp = { + host = "http://localhost:5601" + ssl_verification_mode = "full" + ssl_supported_protocols = ["TLSv1.0", "TLSv1.1", "TLSv1.2"] + proxy_url = "http://localhost:8080" proxy_use_local_resolver = true + } +} +` + + tcpMonitorUpdated = ` +resource "elasticstack_kibana_synthetics_monitor" "tcp-monitor" { + name = "TestTcpMonitorResource Updated" + space_id = "default" + schedule = 10 + private_locations = [elasticstack_kibana_synthetics_private_location.test.label] + enabled = false + tags = ["c", "d", "e"] + alert = { + status = { + enabled = true + } + tls = { + enabled = false + } + } + service_name = "test apm service" + timeout = 30 + tcp = { + host = "http://localhost:8080" + ssl_verification_mode = "full" + ssl_supported_protocols = ["TLSv1.2"] + proxy_url = "http://localhost" + proxy_use_local_resolver = false + } +} - */ +` ) func TestSyntheticMonitorResource(t *testing.T) { @@ -120,7 +172,7 @@ func TestSyntheticMonitorResource(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ProtoV6ProviderFactories: acctest.Providers, Steps: []resource.TestStep{ - // Create and Read testing + // Create and Read http monitor { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), Config: providerConfig + privateLocationConfig + httpMonitorConfig, @@ -160,7 +212,7 @@ func TestSyntheticMonitorResource(t *testing.T) { ImportStateVerify: true, Config: providerConfig + privateLocationConfig + httpMonitorConfig, }, - // Update and Read testing + // Update and Read testing http monitor { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), ResourceName: httpMonitorId, @@ -176,7 +228,7 @@ func TestSyntheticMonitorResource(t *testing.T) { resource.TestCheckResourceAttr(httpMonitorId, "tags.#", "3"), resource.TestCheckResourceAttr(httpMonitorId, "tags.0", "c"), resource.TestCheckResourceAttr(httpMonitorId, "tags.1", "d"), - resource.TestCheckResourceAttr(httpMonitorId, "tags.1", "e"), + resource.TestCheckResourceAttr(httpMonitorId, "tags.2", "e"), resource.TestCheckResourceAttr(httpMonitorId, "alert.status.enabled", "true"), resource.TestCheckResourceAttr(httpMonitorId, "alert.tls.enabled", "false"), resource.TestCheckResourceAttr(httpMonitorId, "service_name", "test apm service"), @@ -184,12 +236,80 @@ func TestSyntheticMonitorResource(t *testing.T) { resource.TestCheckResourceAttr(httpMonitorId, "http.url", "http://localhost:8080"), resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_verification_mode", "full"), resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.#", "1"), - resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.2", "TLSv1.2"), + resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.0", "TLSv1.2"), resource.TestCheckResourceAttr(httpMonitorId, "http.max_redirects", "10"), resource.TestCheckResourceAttr(httpMonitorId, "http.mode", "all"), resource.TestCheckResourceAttr(httpMonitorId, "http.ipv4", "true"), resource.TestCheckResourceAttr(httpMonitorId, "http.ipv6", "true"), - resource.TestCheckNoResourceAttr(httpMonitorId, "http.proxy_url"), + resource.TestCheckResourceAttr(httpMonitorId, "http.proxy_url", "http://localhost"), + resource.TestCheckNoResourceAttr(httpMonitorId, "tcp"), + ), + }, + // Create and Read tcp monitor + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + Config: providerConfig + privateLocationConfig + tcpMonitorConfig, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(tcpMonitorId, "id"), + resource.TestCheckResourceAttr(tcpMonitorId, "name", "TestTcpMonitorResource"), + resource.TestCheckResourceAttr(tcpMonitorId, "space_id", "default"), + resource.TestCheckResourceAttr(tcpMonitorId, "schedule", "5"), + resource.TestCheckResourceAttr(tcpMonitorId, "private_locations.#", "1"), + resource.TestCheckResourceAttrSet(tcpMonitorId, "private_locations.0"), + resource.TestCheckResourceAttr(tcpMonitorId, "enabled", "true"), + resource.TestCheckResourceAttr(tcpMonitorId, "tags.#", "2"), + resource.TestCheckResourceAttr(tcpMonitorId, "tags.0", "a"), + resource.TestCheckResourceAttr(tcpMonitorId, "tags.1", "b"), + resource.TestCheckResourceAttr(tcpMonitorId, "alert.status.enabled", "true"), + resource.TestCheckResourceAttr(tcpMonitorId, "alert.tls.enabled", "true"), + resource.TestCheckResourceAttr(tcpMonitorId, "service_name", "test apm service"), + resource.TestCheckResourceAttr(tcpMonitorId, "timeout", "30"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.host", "http://localhost:5601"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.ssl_verification_mode", "full"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.ssl_supported_protocols.#", "3"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.ssl_supported_protocols.0", "TLSv1.0"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.ssl_supported_protocols.1", "TLSv1.1"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.ssl_supported_protocols.2", "TLSv1.2"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.proxy_url", "http://localhost:8080"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.proxy_use_local_resolver", "true"), + ), + }, + // ImportState testing + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + ResourceName: tcpMonitorId, + ImportState: true, + ImportStateVerify: true, + Config: providerConfig + privateLocationConfig + tcpMonitorConfig, + }, + // Update and Read tcp monitor + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + ResourceName: tcpMonitorId, + Config: providerConfig + privateLocationConfig + tcpMonitorUpdated, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrSet(tcpMonitorId, "id"), + resource.TestCheckResourceAttr(tcpMonitorId, "name", "TestTcpMonitorResource Updated"), + resource.TestCheckResourceAttr(tcpMonitorId, "space_id", "default"), + resource.TestCheckResourceAttr(tcpMonitorId, "schedule", "10"), + resource.TestCheckResourceAttr(tcpMonitorId, "private_locations.#", "1"), + resource.TestCheckResourceAttrSet(tcpMonitorId, "private_locations.0"), + resource.TestCheckResourceAttr(tcpMonitorId, "enabled", "false"), + resource.TestCheckResourceAttr(tcpMonitorId, "tags.#", "3"), + resource.TestCheckResourceAttr(tcpMonitorId, "tags.0", "c"), + resource.TestCheckResourceAttr(tcpMonitorId, "tags.1", "d"), + resource.TestCheckResourceAttr(tcpMonitorId, "tags.2", "e"), + resource.TestCheckResourceAttr(tcpMonitorId, "alert.status.enabled", "true"), + resource.TestCheckResourceAttr(tcpMonitorId, "alert.tls.enabled", "false"), + resource.TestCheckResourceAttr(tcpMonitorId, "service_name", "test apm service"), + resource.TestCheckResourceAttr(tcpMonitorId, "timeout", "30"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.host", "http://localhost:8080"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.ssl_verification_mode", "full"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.ssl_supported_protocols.#", "1"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.ssl_supported_protocols.0", "TLSv1.2"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.proxy_url", "http://localhost"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.proxy_use_local_resolver", "false"), + resource.TestCheckNoResourceAttr(tcpMonitorId, "http"), ), }, // Delete testing automatically occurs in TestCase diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index 2a716b063..3d2d3dcc8 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -55,10 +55,11 @@ type tfTCPMonitorFieldsV0 struct { Host types.String `tfsdk:"host"` SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` - CheckSend types.String `tfsdk:"check_send"` - CheckReceive types.String `tfsdk:"check_receive"` - ProxyURL types.String `tfsdk:"proxy_url"` - ProxyUseLocalResolver types.Bool `tfsdk:"proxy_use_local_resolver"` + // commented out due-to https://github.com/elastic/kibana/issues/189906 + //CheckSend types.String `tfsdk:"check_send"` + //CheckReceive types.String `tfsdk:"check_receive"` + ProxyURL types.String `tfsdk:"proxy_url"` + ProxyUseLocalResolver types.Bool `tfsdk:"proxy_use_local_resolver"` } type tfModelV0 struct { @@ -273,14 +274,14 @@ func tcpMonitorFieldsSchema() schema.Attribute { Optional: true, MarkdownDescription: "List of allowed SSL/TLS versions.", }, - "check_send": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "An optional payload string to send to the remote host.", - }, - "check_receive": schema.StringAttribute{ - Optional: true, - MarkdownDescription: "The expected answer. ", - }, + //"check_send": schema.StringAttribute{ + // Optional: true, + // MarkdownDescription: "An optional payload string to send to the remote host.", + //}, + //"check_receive": schema.StringAttribute{ + // Optional: true, + // MarkdownDescription: "The expected answer. ", + //}, "proxy_url": schema.StringAttribute{ Optional: true, MarkdownDescription: "The URL of the SOCKS5 proxy to use when connecting to the server. The value must be a URL with a scheme of `socks5://`. If the SOCKS5 proxy server requires client authentication, then a username and password can be embedded in the URL. When using a proxy, hostnames are resolved on the proxy server instead of on the client. You can change this behavior by setting the `proxy_use_local_resolver` option.", @@ -446,8 +447,8 @@ func toTfTCPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfTCPMonitorFieldsV0 Host: types.StringValue(api.Host), SslVerificationMode: types.StringValue(api.SslVerificationMode), SslSupportedProtocols: StringSliceValue(api.SslSupportedProtocols), - CheckSend: types.StringValue(api.CheckSend), - CheckReceive: types.StringValue(api.CheckReceive), + //CheckSend: types.StringValue(api.CheckSend), + //CheckReceive: types.StringValue(api.CheckReceive), ProxyURL: types.StringValue(api.ProxyUrl), ProxyUseLocalResolver: types.BoolPointerValue(api.ProxyUseLocalResolver), }, nil @@ -586,8 +587,8 @@ func (v *tfModelV0) toTCPMonitorFields() kbapi.MonitorFields { Host: v.TCP.Host.ValueString(), SslVerificationMode: v.TCP.SslVerificationMode.ValueString(), SslSupportedProtocols: ValueStringSlice(v.TCP.SslSupportedProtocols), - CheckSend: v.TCP.CheckSend.ValueString(), - CheckReceive: v.TCP.CheckReceive.ValueString(), + //CheckSend: v.TCP.CheckSend.ValueString(), + //CheckReceive: v.TCP.CheckReceive.ValueString(), ProxyUrl: v.TCP.ProxyURL.ValueString(), ProxyUseLocalResolver: v.TCP.ProxyUseLocalResolver.ValueBoolPointer(), } diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go index 77c8521c5..a10426f05 100644 --- a/internal/kibana/synthetics/schema_test.go +++ b/internal/kibana/synthetics/schema_test.go @@ -67,9 +67,9 @@ func TestToModelV0(t *testing.T) { TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue(""), SslVerificationMode: types.StringValue(""), - CheckSend: types.StringValue(""), - CheckReceive: types.StringValue(""), - ProxyURL: types.StringValue(""), + //CheckSend: types.StringValue(""), + //CheckReceive: types.StringValue(""), + ProxyURL: types.StringValue(""), }, }, }, @@ -189,8 +189,8 @@ func TestToModelV0(t *testing.T) { Host: types.StringValue("example.com:9200"), SslVerificationMode: types.StringValue("full"), SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, - CheckSend: types.StringValue("hello"), - CheckReceive: types.StringValue("world"), + //CheckSend: types.StringValue("hello"), + //CheckReceive: types.StringValue("world"), ProxyURL: types.StringValue("http://proxy.com"), ProxyUseLocalResolver: types.BoolPointerValue(tBool), }, @@ -314,8 +314,8 @@ func TestToKibanaAPIRequest(t *testing.T) { Host: types.StringValue("example.com:9200"), SslVerificationMode: types.StringValue("full"), SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, - CheckSend: types.StringValue("hello"), - CheckReceive: types.StringValue("world"), + //CheckSend: types.StringValue("hello"), + //CheckReceive: types.StringValue("world"), ProxyURL: types.StringValue("http://proxy.com"), ProxyUseLocalResolver: types.BoolPointerValue(tBool), }, @@ -338,8 +338,8 @@ func TestToKibanaAPIRequest(t *testing.T) { Host: "example.com:9200", SslVerificationMode: "full", SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, - CheckSend: "hello", - CheckReceive: "world", + //CheckSend: "hello", + //CheckReceive: "world", ProxyUrl: "http://proxy.com", ProxyUseLocalResolver: tBool, }, diff --git a/kibana-config/kibana.yml b/kibana-config/kibana.yml deleted file mode 100644 index d1c33dee4..000000000 --- a/kibana-config/kibana.yml +++ /dev/null @@ -1,11 +0,0 @@ -server.host: "0.0.0.0" -server.shutdownTimeout: "5s" -elasticsearch.hosts: [ "http://elasticsearch:9200" ] - -server.oas.enabled: true - -#logging.root.level: debug - -logging.loggers: - - name: plugins.fleet - level: debug diff --git a/libs/go-kibana-rest/docker-compose.yml b/libs/go-kibana-rest/docker-compose.yml index 169867a57..10210a1b4 100644 --- a/libs/go-kibana-rest/docker-compose.yml +++ b/libs/go-kibana-rest/docker-compose.yml @@ -30,7 +30,6 @@ services: ELASTICSEARCH_USERNAME: kibana_system ELASTICSEARCH_PASSWORD: changeme XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: min-32-byte-long-strong-encryption-key - xpack.fleet.enabled: true links: - elasticsearch:es ports: diff --git a/test.json b/test.json new file mode 100644 index 000000000..25eb060be --- /dev/null +++ b/test.json @@ -0,0 +1,516 @@ +{ + "epm-packages": { + "installed_kibana": [], + "installed_kibana_space_id": "default", + "installed_es": [ + { + "id": "logs-elastic_agent.apm_server-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.apm_server-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.auditbeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.auditbeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.cloud_defend-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.cloudbeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.cloudbeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.elastic_agent-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.endpoint_security-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.endpoint_security-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.filebeat_input-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.filebeat_input-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.filebeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.filebeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.fleet_server-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.fleet_server-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.heartbeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.heartbeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.metricbeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.metricbeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.osquerybeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.osquerybeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.packetbeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "metrics-elastic_agent.packetbeat-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.pf_elastic_collector-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.pf_elastic_symbolizer-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.pf_host_agent-2.0.2", + "type": "ingest_pipeline" + }, + { + "id": "logs-elastic_agent.apm_server", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.apm_server@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.apm_server@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.apm_server", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.apm_server@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.apm_server@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.auditbeat", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.auditbeat@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.auditbeat@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.auditbeat", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.auditbeat@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.auditbeat@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.cloud_defend", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.cloud_defend@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.cloud_defend@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.cloudbeat", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.cloudbeat@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.cloudbeat@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.cloudbeat", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.cloudbeat@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.cloudbeat@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent", + "type": "index_template" + }, + { + "id": "logs-elastic_agent@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.elastic_agent", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.elastic_agent@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.elastic_agent@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.endpoint_security", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.endpoint_security@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.endpoint_security@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.endpoint_security", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.endpoint_security@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.endpoint_security@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.filebeat_input", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.filebeat_input@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.filebeat_input@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.filebeat_input", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.filebeat_input@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.filebeat_input@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.filebeat", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.filebeat@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.filebeat@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.filebeat", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.filebeat@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.filebeat@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.fleet_server", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.fleet_server@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.fleet_server@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.fleet_server", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.fleet_server@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.fleet_server@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.heartbeat", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.heartbeat@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.heartbeat@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.heartbeat", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.heartbeat@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.heartbeat@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.metricbeat", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.metricbeat@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.metricbeat@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.metricbeat", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.metricbeat@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.metricbeat@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.osquerybeat", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.osquerybeat@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.osquerybeat@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.osquerybeat", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.osquerybeat@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.osquerybeat@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.packetbeat", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.packetbeat@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.packetbeat@custom", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.packetbeat", + "type": "index_template" + }, + { + "id": "metrics-elastic_agent.packetbeat@package", + "type": "component_template" + }, + { + "id": "metrics-elastic_agent.packetbeat@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.pf_elastic_collector", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.pf_elastic_collector@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.pf_elastic_collector@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.pf_elastic_symbolizer", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.pf_elastic_symbolizer@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.pf_elastic_symbolizer@custom", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.pf_host_agent", + "type": "index_template" + }, + { + "id": "logs-elastic_agent.pf_host_agent@package", + "type": "component_template" + }, + { + "id": "logs-elastic_agent.pf_host_agent@custom", + "type": "component_template" + } + ], + "package_assets": [], + "es_index_patterns": { + "apm_server_logs": "logs-elastic_agent.apm_server-*", + "apm_server_metrics": "metrics-elastic_agent.apm_server-*", + "auditbeat_logs": "logs-elastic_agent.auditbeat-*", + "auditbeat_metrics": "metrics-elastic_agent.auditbeat-*", + "cloud_defend_logs": "logs-elastic_agent.cloud_defend-*", + "cloudbeat_logs": "logs-elastic_agent.cloudbeat-*", + "cloudbeat_metrics": "metrics-elastic_agent.cloudbeat-*", + "elastic_agent_logs": "logs-elastic_agent-*", + "elastic_agent_metrics": "metrics-elastic_agent.elastic_agent-*", + "endpoint_security_metrics": "metrics-elastic_agent.endpoint_security-*", + "endpoint_sercurity_logs": "logs-elastic_agent.endpoint_security-*", + "filebeat_input_logs": "logs-elastic_agent.filebeat_input-*", + "filebeat_input_metrics": "metrics-elastic_agent.filebeat_input-*", + "filebeat_logs": "logs-elastic_agent.filebeat-*", + "filebeat_metrics": "metrics-elastic_agent.filebeat-*", + "fleet_server_logs": "logs-elastic_agent.fleet_server-*", + "fleet_server_metrics": "metrics-elastic_agent.fleet_server-*", + "heartbeat_logs": "logs-elastic_agent.heartbeat-*", + "heartbeat_metrics": "metrics-elastic_agent.heartbeat-*", + "metricbeat_logs": "logs-elastic_agent.metricbeat-*", + "metricbeat_metrics": "metrics-elastic_agent.metricbeat-*", + "osquerybeat_logs": "logs-elastic_agent.osquerybeat-*", + "osquerybeat_metrics": "metrics-elastic_agent.osquerybeat-*", + "packetbeat_logs": "logs-elastic_agent.packetbeat-*", + "packetbeat_metrics": "metrics-elastic_agent.packetbeat-*", + "pf_elastic_collector": "logs-elastic_agent.pf_elastic_collector-*", + "pf_elastic_symbolizer": "logs-elastic_agent.pf_elastic_symbolizer-*", + "pf_host_agent_logs": "logs-elastic_agent.pf_host_agent-*" + }, + "name": "elastic_agent", + "version": "2.0.2", + "install_version": "2.0.2", + "install_status": "installing", + "install_started_at": "2024-08-13T06:48:27.724Z", + "install_source": "registry", + "install_format_schema_version": "1.2.0", + "verification_status": "verified", + "verification_key_id": "d27d666cd88e42b4" + }, + "type": "epm-packages", + "references": [], + "managed": false, + "coreMigrationVersion": "8.8.0", + "typeMig + rationVersion + ":" + 10.2.0 + "," + updated_at + ":" + 2024-08-13T06: 48 + : + 28.748Z + "," + created_at + ":" + 2024-08-13T06: 48 + : + 27.725Z + "} \ No newline at end of file From c8e21d2db0fac1bb97f7f2319a3ffbd91b09ec9d Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 12:28:52 +0200 Subject: [PATCH 47/72] clean-up before review --- .github/workflows/test.yml | 46 ++-- test.json | 516 ------------------------------------- 2 files changed, 16 insertions(+), 546 deletions(-) delete mode 100644 test.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index be3600829..67a725131 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,7 +80,7 @@ jobs: ELASTICSEARCH_USERNAME: ${{ env.KIBANA_SYSTEM_USERNAME }} ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d - LOGGING_ROOT_LEVEL: debug +# LOGGING_ROOT_LEVEL: debug ports: - 5601:5601 options: --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 @@ -90,21 +90,21 @@ jobs: fail-fast: false matrix: version: -# - '7.17.13' -# - '8.0.1' -# - '8.1.3' -# - '8.2.3' -# - '8.3.3' -# - '8.4.3' -# - '8.5.3' -# - '8.6.2' -# - '8.7.1' -# - '8.8.2' -# - '8.9.2' -# - '8.10.3' -# - '8.11.4' -# - '8.12.2' -# - '8.13.4' + - '7.17.13' + - '8.0.1' + - '8.1.3' + - '8.2.3' + - '8.3.3' + - '8.4.3' + - '8.5.3' + - '8.6.2' + - '8.7.1' + - '8.8.2' + - '8.9.2' + - '8.10.3' + - '8.11.4' + - '8.12.2' + - '8.13.4' - '8.14.3' steps: - uses: actions/checkout@v4 @@ -119,20 +119,6 @@ jobs: - name: Get dependencies run: make vendor -# - name: Start kibana with mounted volume -# run: docker run -d -p 5601:5601 -# -e SERVER_NAME=kibana -# -e ELASTICSEARCH_HOSTS="http://elasticsearch:9200" -# -e ELASTICSEARCH_USERNAME=${{ env.KIBANA_SYSTEM_USERNAME }} -# -e ELASTICSEARCH_PASSWORD=${{ env.KIBANA_SYSTEM_PASSWORD }} -# -e XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY=a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d -# -e "xpack.fleet.enabled=true" -# -v ${{ github.workspace }}/kibana-config:/usr/share/kibana/config -# --name kibana -# --network ${{ job.container.network }} -# --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 -# docker.elastic.co/kibana/kibana:${{ matrix.version }} - - name: Setup Kibana user run: make set-kibana-password env: diff --git a/test.json b/test.json deleted file mode 100644 index 25eb060be..000000000 --- a/test.json +++ /dev/null @@ -1,516 +0,0 @@ -{ - "epm-packages": { - "installed_kibana": [], - "installed_kibana_space_id": "default", - "installed_es": [ - { - "id": "logs-elastic_agent.apm_server-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.apm_server-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.auditbeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.auditbeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.cloud_defend-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.cloudbeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.cloudbeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.elastic_agent-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.endpoint_security-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.endpoint_security-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.filebeat_input-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.filebeat_input-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.filebeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.filebeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.fleet_server-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.fleet_server-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.heartbeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.heartbeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.metricbeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.metricbeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.osquerybeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.osquerybeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.packetbeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "metrics-elastic_agent.packetbeat-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.pf_elastic_collector-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.pf_elastic_symbolizer-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.pf_host_agent-2.0.2", - "type": "ingest_pipeline" - }, - { - "id": "logs-elastic_agent.apm_server", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.apm_server@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.apm_server@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.apm_server", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.apm_server@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.apm_server@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.auditbeat", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.auditbeat@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.auditbeat@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.auditbeat", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.auditbeat@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.auditbeat@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.cloud_defend", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.cloud_defend@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.cloud_defend@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.cloudbeat", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.cloudbeat@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.cloudbeat@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.cloudbeat", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.cloudbeat@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.cloudbeat@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent", - "type": "index_template" - }, - { - "id": "logs-elastic_agent@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.elastic_agent", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.elastic_agent@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.elastic_agent@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.endpoint_security", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.endpoint_security@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.endpoint_security@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.endpoint_security", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.endpoint_security@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.endpoint_security@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.filebeat_input", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.filebeat_input@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.filebeat_input@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.filebeat_input", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.filebeat_input@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.filebeat_input@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.filebeat", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.filebeat@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.filebeat@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.filebeat", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.filebeat@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.filebeat@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.fleet_server", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.fleet_server@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.fleet_server@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.fleet_server", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.fleet_server@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.fleet_server@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.heartbeat", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.heartbeat@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.heartbeat@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.heartbeat", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.heartbeat@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.heartbeat@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.metricbeat", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.metricbeat@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.metricbeat@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.metricbeat", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.metricbeat@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.metricbeat@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.osquerybeat", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.osquerybeat@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.osquerybeat@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.osquerybeat", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.osquerybeat@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.osquerybeat@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.packetbeat", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.packetbeat@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.packetbeat@custom", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.packetbeat", - "type": "index_template" - }, - { - "id": "metrics-elastic_agent.packetbeat@package", - "type": "component_template" - }, - { - "id": "metrics-elastic_agent.packetbeat@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.pf_elastic_collector", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.pf_elastic_collector@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.pf_elastic_collector@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.pf_elastic_symbolizer", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.pf_elastic_symbolizer@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.pf_elastic_symbolizer@custom", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.pf_host_agent", - "type": "index_template" - }, - { - "id": "logs-elastic_agent.pf_host_agent@package", - "type": "component_template" - }, - { - "id": "logs-elastic_agent.pf_host_agent@custom", - "type": "component_template" - } - ], - "package_assets": [], - "es_index_patterns": { - "apm_server_logs": "logs-elastic_agent.apm_server-*", - "apm_server_metrics": "metrics-elastic_agent.apm_server-*", - "auditbeat_logs": "logs-elastic_agent.auditbeat-*", - "auditbeat_metrics": "metrics-elastic_agent.auditbeat-*", - "cloud_defend_logs": "logs-elastic_agent.cloud_defend-*", - "cloudbeat_logs": "logs-elastic_agent.cloudbeat-*", - "cloudbeat_metrics": "metrics-elastic_agent.cloudbeat-*", - "elastic_agent_logs": "logs-elastic_agent-*", - "elastic_agent_metrics": "metrics-elastic_agent.elastic_agent-*", - "endpoint_security_metrics": "metrics-elastic_agent.endpoint_security-*", - "endpoint_sercurity_logs": "logs-elastic_agent.endpoint_security-*", - "filebeat_input_logs": "logs-elastic_agent.filebeat_input-*", - "filebeat_input_metrics": "metrics-elastic_agent.filebeat_input-*", - "filebeat_logs": "logs-elastic_agent.filebeat-*", - "filebeat_metrics": "metrics-elastic_agent.filebeat-*", - "fleet_server_logs": "logs-elastic_agent.fleet_server-*", - "fleet_server_metrics": "metrics-elastic_agent.fleet_server-*", - "heartbeat_logs": "logs-elastic_agent.heartbeat-*", - "heartbeat_metrics": "metrics-elastic_agent.heartbeat-*", - "metricbeat_logs": "logs-elastic_agent.metricbeat-*", - "metricbeat_metrics": "metrics-elastic_agent.metricbeat-*", - "osquerybeat_logs": "logs-elastic_agent.osquerybeat-*", - "osquerybeat_metrics": "metrics-elastic_agent.osquerybeat-*", - "packetbeat_logs": "logs-elastic_agent.packetbeat-*", - "packetbeat_metrics": "metrics-elastic_agent.packetbeat-*", - "pf_elastic_collector": "logs-elastic_agent.pf_elastic_collector-*", - "pf_elastic_symbolizer": "logs-elastic_agent.pf_elastic_symbolizer-*", - "pf_host_agent_logs": "logs-elastic_agent.pf_host_agent-*" - }, - "name": "elastic_agent", - "version": "2.0.2", - "install_version": "2.0.2", - "install_status": "installing", - "install_started_at": "2024-08-13T06:48:27.724Z", - "install_source": "registry", - "install_format_schema_version": "1.2.0", - "verification_status": "verified", - "verification_key_id": "d27d666cd88e42b4" - }, - "type": "epm-packages", - "references": [], - "managed": false, - "coreMigrationVersion": "8.8.0", - "typeMig - rationVersion - ":" - 10.2.0 - "," - updated_at - ":" - 2024-08-13T06: 48 - : - 28.748Z - "," - created_at - ":" - 2024-08-13T06: 48 - : - 27.725Z - "} \ No newline at end of file From a1b09afefd8d34fb9f77df79c167edd4f1f87085 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 12:41:44 +0200 Subject: [PATCH 48/72] add docs --- docs/resources/kibana_synthetics_monitor.md | 73 ++++++++++++++++--- .../import.sh | 1 + .../resource.tf | 31 ++++++++ libs/go-kibana-rest/kibana.go | 1 - .../kibana_synthetics_monitor.md.tmpl | 36 +++++++++ 5 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 examples/resources/elasticstack_kibana_synthetics_monitor/import.sh create mode 100644 examples/resources/elasticstack_kibana_synthetics_monitor/resource.tf create mode 100644 templates/resources/kibana_synthetics_monitor.md.tmpl diff --git a/docs/resources/kibana_synthetics_monitor.md b/docs/resources/kibana_synthetics_monitor.md index d274455c4..0877b16c3 100644 --- a/docs/resources/kibana_synthetics_monitor.md +++ b/docs/resources/kibana_synthetics_monitor.md @@ -1,16 +1,63 @@ --- -# generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "elasticstack_kibana_synthetics_monitor Resource - terraform-provider-elasticstack" -subcategory: "" +subcategory: "Kibana" +layout: "" +page_title: "Elasticstack: elasticstack_kibana_synthetics_monitor Resource" description: |- - Synthetics monitor config, see https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html for more details. The monitor must have one of the following: http, tcp, icmp or browser. + Creates or updates a Kibana synthetics monitor. --- -# elasticstack_kibana_synthetics_monitor (Resource) - -Synthetics monitor config, see https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html for more details. The monitor must have one of the following: http, tcp, icmp or browser. - - +# Resource: elasticstack_kibana_synthetics_monitor + +Creates or updates a Kibana synthetics monitor. +See [API docs](https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html) + +## Supported monitor types + * `http` + * `tcp` + +**NOTE:** Not all monitor fields are supported due-to API limitation. +Full field support could be implemented after this [kibana issue](https://github.com/elastic/kibana/issues/189906) is resolved. + +**NOTE:** Due-to nature of partial update API, reset values to defaults is not supported. + +In case you would like to reset an optional monitor value, please set it explicitly or delete and create new monitor. + + +## Example Usage + +```terraform +provider "elasticstack" { + kibana {} +} + +resource "elasticstack_kibana_synthetics_monitor" "my_monitor" { + name = "Example http monitor" + space_id = "default" + schedule = 10 + locations = ["us_west"] + enabled = false + tags = ["tag"] + alert = { + status = { + enabled = true + } + tls = { + enabled = false + } + } + service_name = "example apm service" + timeout = 30 + http = { + url = "http://localhost:8080" + ssl_verification_mode = "full" + ssl_supported_protocols = ["TLSv1.2"] + max_redirects = "10" + mode = "all" + ipv4 = true + ipv6 = true + } +} +``` ## Schema @@ -93,3 +140,11 @@ Optional: - `proxy_use_local_resolver` (Boolean) A Boolean value that determines whether hostnames are resolved locally instead of being resolved on the proxy server. The default value is false, which means that name resolution occurs on the proxy server. - `ssl_supported_protocols` (List of String) List of allowed SSL/TLS versions. - `ssl_verification_mode` (String) Controls the verification of server certificates. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import elasticstack_kibana_synthetics_monitor.my_monitor +``` \ No newline at end of file diff --git a/examples/resources/elasticstack_kibana_synthetics_monitor/import.sh b/examples/resources/elasticstack_kibana_synthetics_monitor/import.sh new file mode 100644 index 000000000..0ac6ac75d --- /dev/null +++ b/examples/resources/elasticstack_kibana_synthetics_monitor/import.sh @@ -0,0 +1 @@ +terraform import elasticstack_kibana_synthetics_monitor.my_monitor diff --git a/examples/resources/elasticstack_kibana_synthetics_monitor/resource.tf b/examples/resources/elasticstack_kibana_synthetics_monitor/resource.tf new file mode 100644 index 000000000..b38e70609 --- /dev/null +++ b/examples/resources/elasticstack_kibana_synthetics_monitor/resource.tf @@ -0,0 +1,31 @@ +provider "elasticstack" { + kibana {} +} + +resource "elasticstack_kibana_synthetics_monitor" "my_monitor" { + name = "Example http monitor" + space_id = "default" + schedule = 10 + locations = ["us_west"] + enabled = false + tags = ["tag"] + alert = { + status = { + enabled = true + } + tls = { + enabled = false + } + } + service_name = "example apm service" + timeout = 30 + http = { + url = "http://localhost:8080" + ssl_verification_mode = "full" + ssl_supported_protocols = ["TLSv1.2"] + max_redirects = "10" + mode = "all" + ipv4 = true + ipv6 = true + } +} diff --git a/libs/go-kibana-rest/kibana.go b/libs/go-kibana-rest/kibana.go index bb8e62948..560820a4b 100644 --- a/libs/go-kibana-rest/kibana.go +++ b/libs/go-kibana-rest/kibana.go @@ -35,7 +35,6 @@ func NewClient(cfg Config) (*Client, error) { } restyClient := resty.New(). - SetDebug(true). SetBaseURL(cfg.Address). SetHeader("kbn-xsrf", "true"). SetHeader("Content-Type", "application/json") diff --git a/templates/resources/kibana_synthetics_monitor.md.tmpl b/templates/resources/kibana_synthetics_monitor.md.tmpl new file mode 100644 index 000000000..75cabfdf8 --- /dev/null +++ b/templates/resources/kibana_synthetics_monitor.md.tmpl @@ -0,0 +1,36 @@ +--- +subcategory: "Kibana" +layout: "" +page_title: "Elasticstack: elasticstack_kibana_synthetics_monitor Resource" +description: |- + Creates or updates a Kibana synthetics monitor. +--- + +# Resource: elasticstack_kibana_synthetics_monitor + +Creates or updates a Kibana synthetics monitor. +See [API docs](https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html) + +## Supported monitor types + * `http` + * `tcp` + +**NOTE:** Not all monitor fields are supported due-to API limitation. +Full field support could be implemented after this [kibana issue](https://github.com/elastic/kibana/issues/189906) is resolved. + +**NOTE:** Due-to nature of partial update API, reset values to defaults is not supported. + +In case you would like to reset an optional monitor value, please set it explicitly or delete and create new monitor. + + +## Example Usage + +{{ tffile "examples/resources/elasticstack_kibana_synthetics_monitor/resource.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" "examples/resources/elasticstack_kibana_synthetics_monitor/import.sh" }} \ No newline at end of file From 55c704e4d23670efc7aa6b92e753aaf92f5b1b6b Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 14:33:29 +0200 Subject: [PATCH 49/72] try to force-install synthetics --- .github/workflows/test.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 67a725131..d49538927 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,7 +80,7 @@ jobs: ELASTICSEARCH_USERNAME: ${{ env.KIBANA_SYSTEM_USERNAME }} ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d -# LOGGING_ROOT_LEVEL: debug + LOGGING_ROOT_LEVEL: debug ports: - 5601:5601 options: --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 @@ -137,6 +137,11 @@ jobs: ELASTICSEARCH_USERNAME: "elastic" ELASTICSEARCH_PASSWORD: ${{ env.ELASTIC_PASSWORD }} + - id: force-install-synthetics + name: Force install synthetics + run: |- + curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 + - name: TF acceptance tests timeout-minutes: 10 run: make testacc From 09af708915ed8f1c1967714cf413a776d09b41cc Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 14:47:04 +0200 Subject: [PATCH 50/72] add re-try --- .github/workflows/test.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d49538927..aaa01c31b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -90,21 +90,21 @@ jobs: fail-fast: false matrix: version: - - '7.17.13' - - '8.0.1' - - '8.1.3' - - '8.2.3' - - '8.3.3' - - '8.4.3' - - '8.5.3' - - '8.6.2' - - '8.7.1' - - '8.8.2' - - '8.9.2' - - '8.10.3' - - '8.11.4' - - '8.12.2' - - '8.13.4' +# - '7.17.13' +# - '8.0.1' +# - '8.1.3' +# - '8.2.3' +# - '8.3.3' +# - '8.4.3' +# - '8.5.3' +# - '8.6.2' +# - '8.7.1' +# - '8.8.2' +# - '8.9.2' +# - '8.10.3' +# - '8.11.4' +# - '8.12.2' +# - '8.13.4' - '8.14.3' steps: - uses: actions/checkout@v4 @@ -140,7 +140,7 @@ jobs: - id: force-install-synthetics name: Force install synthetics run: |- - curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 + for i in {1..5}; do $(curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2) && break || sleep 15; done - name: TF acceptance tests timeout-minutes: 10 From bcde2d1e4989ecd79d12933a86226d5e188507df Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 14:56:34 +0200 Subject: [PATCH 51/72] force install synthetics only for 8.14.3 --- .github/workflows/test.yml | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index aaa01c31b..4f57c682f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -90,21 +90,21 @@ jobs: fail-fast: false matrix: version: -# - '7.17.13' -# - '8.0.1' -# - '8.1.3' -# - '8.2.3' -# - '8.3.3' -# - '8.4.3' -# - '8.5.3' -# - '8.6.2' -# - '8.7.1' -# - '8.8.2' -# - '8.9.2' -# - '8.10.3' -# - '8.11.4' -# - '8.12.2' -# - '8.13.4' + - '7.17.13' + - '8.0.1' + - '8.1.3' + - '8.2.3' + - '8.3.3' + - '8.4.3' + - '8.5.3' + - '8.6.2' + - '8.7.1' + - '8.8.2' + - '8.9.2' + - '8.10.3' + - '8.11.4' + - '8.12.2' + - '8.13.4' - '8.14.3' steps: - uses: actions/checkout@v4 @@ -139,8 +139,9 @@ jobs: - id: force-install-synthetics name: Force install synthetics + if: matrix.version == '8.14.3' run: |- - for i in {1..5}; do $(curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2) && break || sleep 15; done + for i in {1..5}; do curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 && break || sleep 15; done - name: TF acceptance tests timeout-minutes: 10 From 73b8afcb1f96bed885d6fb454fcf66400db082c1 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 15:26:37 +0200 Subject: [PATCH 52/72] try to use status api --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4f57c682f..d8614081b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -141,7 +141,8 @@ jobs: name: Force install synthetics if: matrix.version == '8.14.3' run: |- - for i in {1..5}; do curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 && break || sleep 15; done + for i in {1..5}; do synthstatus=$(curl -v --header "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "kbn-xsrf: true" http://localhost:5601/api/status | jq .status.plugins.synthetics.level); [ "$synthstatus" = '"available"' ] && break || sleep 15; done +# for i in {1..5}; do curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 && break || sleep 15; done - name: TF acceptance tests timeout-minutes: 10 From d2a7b91f2da0385de9d2c2c990aae8072bba63b4 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 15:30:56 +0200 Subject: [PATCH 53/72] add debug --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d8614081b..78849918c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -141,7 +141,7 @@ jobs: name: Force install synthetics if: matrix.version == '8.14.3' run: |- - for i in {1..5}; do synthstatus=$(curl -v --header "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "kbn-xsrf: true" http://localhost:5601/api/status | jq .status.plugins.synthetics.level); [ "$synthstatus" = '"available"' ] && break || sleep 15; done + for i in {1..5}; do synthstatus=$(curl -v --header "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "kbn-xsrf: true" http://localhost:5601/api/status | jq .status.plugins.synthetics.level); echo "STATUS: $synthstatus"; [ "$synthstatus" = '"available"' ] && break || sleep 15; done # for i in {1..5}; do curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 && break || sleep 15; done - name: TF acceptance tests From daa3b4c0e52ef5f0c2343a6e002f9777be4806f6 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 15:39:23 +0200 Subject: [PATCH 54/72] back to force-install (synthetics doesn't report plugin status via API) --- .github/workflows/test.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 78849918c..9954e6f99 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -80,7 +80,7 @@ jobs: ELASTICSEARCH_USERNAME: ${{ env.KIBANA_SYSTEM_USERNAME }} ELASTICSEARCH_PASSWORD: ${{ env.KIBANA_SYSTEM_PASSWORD }} XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY: a7a6311933d3503b89bc2dbc36572c33a6c10925682e591bffcab6911c06786d - LOGGING_ROOT_LEVEL: debug +# LOGGING_ROOT_LEVEL: debug ports: - 5601:5601 options: --health-cmd="curl http://localhost:5601/api/status" --health-interval=10s --health-timeout=5s --health-retries=10 @@ -141,8 +141,7 @@ jobs: name: Force install synthetics if: matrix.version == '8.14.3' run: |- - for i in {1..5}; do synthstatus=$(curl -v --header "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "kbn-xsrf: true" http://localhost:5601/api/status | jq .status.plugins.synthetics.level); echo "STATUS: $synthstatus"; [ "$synthstatus" = '"available"' ] && break || sleep 15; done -# for i in {1..5}; do curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 && break || sleep 15; done + for i in {1..5}; do curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 && break || sleep 15; done - name: TF acceptance tests timeout-minutes: 10 From 053394a95a40174ee23d50bfa7cbe507ae721361 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 13 Aug 2024 15:47:30 +0200 Subject: [PATCH 55/72] silence --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9954e6f99..53a32184e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -141,7 +141,7 @@ jobs: name: Force install synthetics if: matrix.version == '8.14.3' run: |- - for i in {1..5}; do curl -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 && break || sleep 15; done + for i in {1..5}; do curl -s -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 && break || sleep 15; done - name: TF acceptance tests timeout-minutes: 10 From 74445aff6422a6f2844408c74dbb1125613159b1 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Thu, 15 Aug 2024 12:15:26 +0200 Subject: [PATCH 56/72] CR comments: make `max_redirects` as int64 --- docs/resources/kibana_synthetics_monitor.md | 2 +- internal/kibana/synthetics/acc_test.go | 4 ++-- internal/kibana/synthetics/schema.go | 18 ++++++++++++++---- internal/kibana/synthetics/schema_test.go | 6 +++--- 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/resources/kibana_synthetics_monitor.md b/docs/resources/kibana_synthetics_monitor.md index 0877b16c3..9d681896e 100644 --- a/docs/resources/kibana_synthetics_monitor.md +++ b/docs/resources/kibana_synthetics_monitor.md @@ -120,7 +120,7 @@ Optional: - `ipv4` (Boolean) Whether to ping using the ipv4 protocol. - `ipv6` (Boolean) Whether to ping using the ipv6 protocol. -- `max_redirects` (String) The maximum number of redirects to follow. Default: `0` +- `max_redirects` (Number) The maximum number of redirects to follow. Default: `0` - `mode` (String) The mode of the monitor. Can be "all" or "any". If you’re using a DNS-load balancer and want to ping every IP address for the specified hostname, you should use all. - `proxy_url` (String) The URL of the proxy to use for this monitor. - `ssl_supported_protocols` (List of String) List of allowed SSL/TLS versions. diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index 53fb9a39f..3ee6b6f9f 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -67,7 +67,7 @@ resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { url = "http://localhost:5601" ssl_verification_mode = "full" ssl_supported_protocols = ["TLSv1.0", "TLSv1.1", "TLSv1.2"] - max_redirects = "10" + max_redirects = 10 mode = "any" ipv4 = true ipv6 = false @@ -98,7 +98,7 @@ resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { url = "http://localhost:8080" ssl_verification_mode = "full" ssl_supported_protocols = ["TLSv1.2"] - max_redirects = "10" + max_redirects = 10 mode = "all" ipv4 = true ipv6 = true diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index 3d2d3dcc8..e936afa51 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -37,7 +37,7 @@ type tfHTTPMonitorFieldsV0 struct { URL types.String `tfsdk:"url"` SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` - MaxRedirects types.String `tfsdk:"max_redirects"` + MaxRedirects types.Int64 `tfsdk:"max_redirects"` Mode types.String `tfsdk:"mode"` IPv4 types.Bool `tfsdk:"ipv4"` IPv6 types.Bool `tfsdk:"ipv6"` @@ -217,7 +217,7 @@ func httpMonitorFieldsSchema() schema.Attribute { Optional: true, MarkdownDescription: "List of allowed SSL/TLS versions.", }, - "max_redirects": schema.StringAttribute{ //TODO: make int64?? + "max_redirects": schema.Int64Attribute{ Optional: true, MarkdownDescription: "The maximum number of redirects to follow. Default: `0`", }, @@ -461,11 +461,16 @@ func toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFields // return nil, err //} + maxRedirects, err := stringToInt64(api.MaxRedirects) + if err != nil { + return nil, err + } + return &tfHTTPMonitorFieldsV0{ URL: types.StringValue(api.Url), SslVerificationMode: types.StringValue(api.SslVerificationMode), SslSupportedProtocols: StringSliceValue(api.SslSupportedProtocols), - MaxRedirects: types.StringValue(api.MaxRedirects), + MaxRedirects: types.Int64Value(maxRedirects), Mode: types.StringValue(string(api.Mode)), IPv4: types.BoolPointerValue(api.Ipv4), IPv6: types.BoolPointerValue(api.Ipv6), @@ -565,11 +570,16 @@ func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics //if dg.HasError() { // return nil, dg //} + maxRedirects := "" + if !v.HTTP.MaxRedirects.IsUnknown() && !v.HTTP.MaxRedirects.IsNull() { // handle omitempty case + maxRedirects = strconv.FormatInt(v.HTTP.MaxRedirects.ValueInt64(), 10) + + } return kbapi.HTTPMonitorFields{ Url: v.HTTP.URL.ValueString(), SslVerificationMode: v.HTTP.SslVerificationMode.ValueString(), SslSupportedProtocols: ValueStringSlice(v.HTTP.SslSupportedProtocols), - MaxRedirects: v.HTTP.MaxRedirects.ValueString(), + MaxRedirects: maxRedirects, Mode: kbapi.HttpMonitorMode(v.HTTP.Mode.ValueString()), Ipv4: v.HTTP.IPv4.ValueBoolPointer(), Ipv6: v.HTTP.IPv6.ValueBoolPointer(), diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go index a10426f05..17d29e5b8 100644 --- a/internal/kibana/synthetics/schema_test.go +++ b/internal/kibana/synthetics/schema_test.go @@ -42,7 +42,7 @@ func TestToModelV0(t *testing.T) { HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue(""), SslVerificationMode: types.StringValue(""), - MaxRedirects: types.StringValue(""), + MaxRedirects: types.Int64Value(0), Mode: types.StringValue(""), //Username: types.StringValue(""), //Password: types.StringValue(""), @@ -132,7 +132,7 @@ func TestToModelV0(t *testing.T) { URL: types.StringValue("https://example.com"), SslVerificationMode: types.StringValue("full"), SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, - MaxRedirects: types.StringValue("5"), + MaxRedirects: types.Int64Value(5), Mode: types.StringValue("all"), IPv4: types.BoolPointerValue(tBool), IPv6: types.BoolPointerValue(fBool), @@ -252,7 +252,7 @@ func TestToKibanaAPIRequest(t *testing.T) { URL: types.StringValue("https://example.com"), SslVerificationMode: types.StringValue("full"), SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, - MaxRedirects: types.StringValue("5"), + MaxRedirects: types.Int64Value(5), Mode: types.StringValue("all"), IPv4: types.BoolPointerValue(tBool), IPv6: types.BoolPointerValue(fBool), From ef09e6451c95b5924cc00dc89c22b3862c4683c6 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Thu, 15 Aug 2024 12:56:23 +0200 Subject: [PATCH 57/72] reproduce monitor id issue - need to add namespace --- internal/kibana/synthetics/acc_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index 3ee6b6f9f..34a6af1bc 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -29,7 +29,7 @@ provider "elasticstack" { resource "elasticstack_fleet_agent_policy" "test" { name = "TestMonitorResource Agent Policy - test" - namespace = "default" + namespace = "testacc" description = "TestMonitorResource Agent Policy" monitor_logs = true monitor_metrics = true @@ -38,7 +38,7 @@ resource "elasticstack_fleet_agent_policy" "test" { resource "elasticstack_kibana_synthetics_private_location" "test" { label = "TestMonitorResource-label" - space_id = "default" + space_id = "testacc" agent_policy_id = elasticstack_fleet_agent_policy.test.policy_id } @@ -48,7 +48,7 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { name = "TestHttpMonitorResource" - space_id = "default" + space_id = "testacc" schedule = 5 private_locations = [elasticstack_kibana_synthetics_private_location.test.label] enabled = true @@ -79,7 +79,7 @@ resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { httpMonitorUpdated = ` resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { name = "TestHttpMonitorResource Updated" - space_id = "default" + space_id = "testacc" schedule = 10 private_locations = [elasticstack_kibana_synthetics_private_location.test.label] enabled = false @@ -179,7 +179,7 @@ func TestSyntheticMonitorResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet(httpMonitorId, "id"), resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource"), - resource.TestCheckResourceAttr(httpMonitorId, "space_id", "default"), + resource.TestCheckResourceAttr(httpMonitorId, "space_id", "testacc"), resource.TestCheckResourceAttr(httpMonitorId, "schedule", "5"), resource.TestCheckResourceAttr(httpMonitorId, "private_locations.#", "1"), resource.TestCheckResourceAttrSet(httpMonitorId, "private_locations.0"), @@ -220,7 +220,7 @@ func TestSyntheticMonitorResource(t *testing.T) { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet(httpMonitorId, "id"), resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource Updated"), - resource.TestCheckResourceAttr(httpMonitorId, "space_id", "default"), + resource.TestCheckResourceAttr(httpMonitorId, "space_id", "testacc"), resource.TestCheckResourceAttr(httpMonitorId, "schedule", "10"), resource.TestCheckResourceAttr(httpMonitorId, "private_locations.#", "1"), resource.TestCheckResourceAttrSet(httpMonitorId, "private_locations.0"), From a25b691f5253a2703369b46eb615e23a026e0f7c Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Thu, 15 Aug 2024 13:09:59 +0200 Subject: [PATCH 58/72] add context to kibana synthetic API calls --- internal/kibana/synthetics/create.go | 2 +- internal/kibana/synthetics/delete.go | 2 +- .../synthetics/private_location/create.go | 2 +- .../synthetics/private_location/delete.go | 2 +- .../synthetics/private_location/read.go | 2 +- internal/kibana/synthetics/read.go | 2 +- internal/kibana/synthetics/update.go | 2 +- .../kbapi/api.kibana_synthetics.go | 43 ++++++++++--------- .../kbapi/api.kibana_synthetics_test.go | 38 +++++++++------- 9 files changed, 51 insertions(+), 44 deletions(-) diff --git a/internal/kibana/synthetics/create.go b/internal/kibana/synthetics/create.go index 63b536435..8f7ef2dbb 100644 --- a/internal/kibana/synthetics/create.go +++ b/internal/kibana/synthetics/create.go @@ -30,7 +30,7 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r } namespace := plan.SpaceID.ValueString() - result, err := kibanaClient.KibanaSynthetics.Monitor.Add(input.config, input.fields, namespace) + result, err := kibanaClient.KibanaSynthetics.Monitor.Add(ctx, input.config, input.fields, namespace) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("Failed to create Kibana monitor `%s`, namespace %s", input.config.Name, namespace), err.Error()) return diff --git a/internal/kibana/synthetics/delete.go b/internal/kibana/synthetics/delete.go index 42f69f33c..4e7834bdf 100644 --- a/internal/kibana/synthetics/delete.go +++ b/internal/kibana/synthetics/delete.go @@ -26,7 +26,7 @@ func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, r monitorId := kbapi.MonitorID(plan.ID.ValueString()) namespace := plan.SpaceID.ValueString() - _, err := kibanaClient.KibanaSynthetics.Monitor.Delete(namespace, monitorId) + _, err := kibanaClient.KibanaSynthetics.Monitor.Delete(ctx, namespace, monitorId) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", monitorId, namespace), err.Error()) diff --git a/internal/kibana/synthetics/private_location/create.go b/internal/kibana/synthetics/private_location/create.go index f66b95777..bfa3633d7 100644 --- a/internal/kibana/synthetics/private_location/create.go +++ b/internal/kibana/synthetics/private_location/create.go @@ -24,7 +24,7 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r input := plan.toPrivateLocation() namespace := plan.SpaceID.ValueString() - result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Create(input.PrivateLocationConfig, namespace) + result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Create(ctx, input.PrivateLocationConfig, namespace) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("Failed to create private location `%s`, namespace %s", input.Label, namespace), err.Error()) return diff --git a/internal/kibana/synthetics/private_location/delete.go b/internal/kibana/synthetics/private_location/delete.go index 05cb32a73..c32b076c1 100644 --- a/internal/kibana/synthetics/private_location/delete.go +++ b/internal/kibana/synthetics/private_location/delete.go @@ -23,7 +23,7 @@ func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, r id := plan.ID.ValueString() namespace := plan.SpaceID.ValueString() - err := kibanaClient.KibanaSynthetics.PrivateLocation.Delete(id, namespace) + err := kibanaClient.KibanaSynthetics.PrivateLocation.Delete(ctx, id, namespace) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", id, namespace), err.Error()) diff --git a/internal/kibana/synthetics/private_location/read.go b/internal/kibana/synthetics/private_location/read.go index 2645f9acb..c12179889 100644 --- a/internal/kibana/synthetics/private_location/read.go +++ b/internal/kibana/synthetics/private_location/read.go @@ -25,7 +25,7 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo label := state.Label.ValueString() namespace := state.SpaceID.ValueString() - result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Get(label, namespace) + result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Get(ctx, label, namespace) if err != nil { var apiError *kbapi.APIError if errors.As(err, &apiError) && apiError.Code == 404 { diff --git a/internal/kibana/synthetics/read.go b/internal/kibana/synthetics/read.go index 1f8ede191..1a583364a 100644 --- a/internal/kibana/synthetics/read.go +++ b/internal/kibana/synthetics/read.go @@ -27,7 +27,7 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo namespace := state.SpaceID.ValueString() monitorId := kbapi.MonitorID(state.ID.ValueString()) - result, err := kibanaClient.KibanaSynthetics.Monitor.Get(monitorId, namespace) + result, err := kibanaClient.KibanaSynthetics.Monitor.Get(ctx, monitorId, namespace) if err != nil { var apiError *kbapi.APIError if errors.As(err, &apiError) && apiError.Code == 404 { diff --git a/internal/kibana/synthetics/update.go b/internal/kibana/synthetics/update.go index 1c34d060a..4e800ab6b 100644 --- a/internal/kibana/synthetics/update.go +++ b/internal/kibana/synthetics/update.go @@ -32,7 +32,7 @@ func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, r namespace := plan.SpaceID.ValueString() monitorId := kbapi.MonitorID(plan.ID.ValueString()) - result, err := kibanaClient.KibanaSynthetics.Monitor.Update(monitorId, input.config, input.fields, namespace) + result, err := kibanaClient.KibanaSynthetics.Monitor.Update(ctx, monitorId, input.config, input.fields, namespace) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("Failed to update Kibana monitor `%s`, namespace %s", input.config.Name, namespace), err.Error()) return diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go index e4acafd18..44193dede 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go @@ -1,6 +1,7 @@ package kbapi import ( + "context" "encoding/json" "fmt" "time" @@ -244,22 +245,22 @@ func (f TCPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} } } -type KibanaSyntheticsMonitorAdd func(config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) +type KibanaSyntheticsMonitorAdd func(ctx context.Context, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) -type KibanaSyntheticsMonitorUpdate func(id MonitorID, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) +type KibanaSyntheticsMonitorUpdate func(ctx context.Context, id MonitorID, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) -type KibanaSyntheticsMonitorGet func(id MonitorID, namespace string) (*SyntheticsMonitor, error) +type KibanaSyntheticsMonitorGet func(ctx context.Context, id MonitorID, namespace string) (*SyntheticsMonitor, error) -type KibanaSyntheticsMonitorDelete func(namespace string, ids ...MonitorID) ([]MonitorDeleteStatus, error) +type KibanaSyntheticsMonitorDelete func(ctx context.Context, namespace string, ids ...MonitorID) ([]MonitorDeleteStatus, error) -type KibanaSyntheticsPrivateLocationCreate func(pLoc PrivateLocationConfig, namespace string) (*PrivateLocation, error) +type KibanaSyntheticsPrivateLocationCreate func(ctx context.Context, pLoc PrivateLocationConfig, namespace string) (*PrivateLocation, error) -type KibanaSyntheticsPrivateLocationGet func(idOrLabel string, namespace string) (*PrivateLocation, error) +type KibanaSyntheticsPrivateLocationGet func(ctx context.Context, idOrLabel string, namespace string) (*PrivateLocation, error) -type KibanaSyntheticsPrivateLocationDelete func(id string, namespace string) error +type KibanaSyntheticsPrivateLocationDelete func(ctx context.Context, id string, namespace string) error func newKibanaSyntheticsPrivateLocationGetFunc(c *resty.Client) KibanaSyntheticsPrivateLocationGet { - return func(idOrLabel string, namespace string) (*PrivateLocation, error) { + return func(ctx context.Context, idOrLabel string, namespace string) (*PrivateLocation, error) { if idOrLabel == "" { return nil, APIError{ @@ -270,7 +271,7 @@ func newKibanaSyntheticsPrivateLocationGetFunc(c *resty.Client) KibanaSynthetics path := basePathWithId(namespace, privateLocationsSuffix, idOrLabel) log.Debugf("URL to get private locations: %s", path) - resp, err := c.R().Get(path) + resp, err := c.R().SetContext(ctx).Get(path) if err = handleKibanaError(err, resp); err != nil { return nil, err } @@ -279,21 +280,21 @@ func newKibanaSyntheticsPrivateLocationGetFunc(c *resty.Client) KibanaSynthetics } func newKibanaSyntheticsPrivateLocationDeleteFunc(c *resty.Client) KibanaSyntheticsPrivateLocationDelete { - return func(id string, namespace string) error { + return func(ctx context.Context, id string, namespace string) error { path := basePathWithId(namespace, privateLocationsSuffix, id) log.Debugf("URL to delete private locations: %s", path) - resp, err := c.R().Delete(path) + resp, err := c.R().SetContext(ctx).Delete(path) err = handleKibanaError(err, resp) return err } } func newKibanaSyntheticsMonitorGetFunc(c *resty.Client) KibanaSyntheticsMonitorGet { - return func(id MonitorID, namespace string) (*SyntheticsMonitor, error) { + return func(ctx context.Context, id MonitorID, namespace string) (*SyntheticsMonitor, error) { path := basePathWithId(namespace, monitorsSuffix, id) log.Debugf("URL to get monitor: %s", path) - resp, err := c.R().Get(path) + resp, err := c.R().SetContext(ctx).Get(path) if err := handleKibanaError(err, resp); err != nil { return nil, err } @@ -302,11 +303,11 @@ func newKibanaSyntheticsMonitorGetFunc(c *resty.Client) KibanaSyntheticsMonitorG } func newKibanaSyntheticsMonitorDeleteFunc(c *resty.Client) KibanaSyntheticsMonitorDelete { - return func(namespace string, ids ...MonitorID) ([]MonitorDeleteStatus, error) { + return func(ctx context.Context, namespace string, ids ...MonitorID) ([]MonitorDeleteStatus, error) { path := basePath(namespace, monitorsSuffix) log.Debugf("URL to delete monitors: %s", path) - resp, err := c.R().SetBody(map[string]interface{}{ + resp, err := c.R().SetContext(ctx).SetBody(map[string]interface{}{ "ids": ids, }).Delete(path) if err = handleKibanaError(err, resp); err != nil { @@ -319,11 +320,11 @@ func newKibanaSyntheticsMonitorDeleteFunc(c *resty.Client) KibanaSyntheticsMonit } func newKibanaSyntheticsPrivateLocationCreateFunc(c *resty.Client) KibanaSyntheticsPrivateLocationCreate { - return func(pLoc PrivateLocationConfig, namespace string) (*PrivateLocation, error) { + return func(ctx context.Context, pLoc PrivateLocationConfig, namespace string) (*PrivateLocation, error) { path := basePath(namespace, privateLocationsSuffix) log.Debugf("URL to create private locations: %s", path) - resp, err := c.R().SetBody(pLoc).Post(path) + resp, err := c.R().SetContext(ctx).SetBody(pLoc).Post(path) if err = handleKibanaError(err, resp); err != nil { return nil, err } @@ -332,12 +333,12 @@ func newKibanaSyntheticsPrivateLocationCreateFunc(c *resty.Client) KibanaSynthet } func newKibanaSyntheticsMonitorUpdateFunc(c *resty.Client) KibanaSyntheticsMonitorUpdate { - return func(id MonitorID, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) { + return func(ctx context.Context, id MonitorID, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) { path := basePathWithId(namespace, monitorsSuffix, id) log.Debugf("URL to update monitor: %s", path) data := fields.APIRequest(config) - resp, err := c.R().SetBody(data).Put(path) + resp, err := c.R().SetContext(ctx).SetBody(data).Put(path) if err := handleKibanaError(err, resp); err != nil { return nil, err } @@ -346,12 +347,12 @@ func newKibanaSyntheticsMonitorUpdateFunc(c *resty.Client) KibanaSyntheticsMonit } func newKibanaSyntheticsMonitorAddFunc(c *resty.Client) KibanaSyntheticsMonitorAdd { - return func(config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) { + return func(ctx context.Context, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error) { path := basePath(namespace, monitorsSuffix) log.Debugf("URL to create monitor: %s", path) data := fields.APIRequest(config) - resp, err := c.R().SetBody(data).Post(path) + resp, err := c.R().SetContext(ctx).SetBody(data).Post(path) if err := handleKibanaError(err, resp); err != nil { return nil, err } diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go index 3b82ec7be..22df4891b 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go @@ -1,6 +1,7 @@ package kbapi import ( + "context" "encoding/json" "fmt" "testing" @@ -59,6 +60,8 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { fields MonitorFields } + ctx := context.Background() + for _, n := range namespaces { testUuid := uuid.New().String() space := n @@ -69,10 +72,10 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { Label: fmt.Sprintf("TestKibanaSyntheticsMonitorAdd %s", testUuid), AgentPolicyId: policyId, } - location, err := syntheticsAPI.PrivateLocation.Create(locationConfig, space) + location, err := syntheticsAPI.PrivateLocation.Create(ctx, locationConfig, space) assert.NoError(s.T(), err) defer func(id string) { - syntheticsAPI.PrivateLocation.Delete(id, space) + syntheticsAPI.PrivateLocation.Delete(ctx, id, space) }(location.Id) f := new(bool) @@ -240,41 +243,41 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { config := tc.input.config fields := tc.input.fields - monitor, err := syntheticsAPI.Monitor.Add(config, fields, space) + monitor, err := syntheticsAPI.Monitor.Add(ctx, config, fields, space) assert.NoError(s.T(), err) assert.NotNil(s.T(), monitor) updateDueToKibanaAPIDiff(monitor) - get, err := syntheticsAPI.Monitor.Get(monitor.Id, space) + get, err := syntheticsAPI.Monitor.Get(ctx, monitor.Id, space) assert.NoError(s.T(), err) assert.Equal(s.T(), monitor, get) - get, err = syntheticsAPI.Monitor.Get(monitor.ConfigId, space) + get, err = syntheticsAPI.Monitor.Get(ctx, monitor.ConfigId, space) assert.NoError(s.T(), err) assert.Equal(s.T(), monitor, get) - update, err := syntheticsAPI.Monitor.Update(monitor.Id, tc.update.config, tc.update.fields, space) + update, err := syntheticsAPI.Monitor.Update(ctx, monitor.Id, tc.update.config, tc.update.fields, space) assert.NoError(s.T(), err) assert.NotNil(s.T(), update) updateDueToKibanaAPIDiff(update) - get, err = syntheticsAPI.Monitor.Get(monitor.ConfigId, space) + get, err = syntheticsAPI.Monitor.Get(ctx, monitor.ConfigId, space) assert.NoError(s.T(), err) get.CreatedAt = time.Time{} // update response doesn't have created_at field assert.Equal(s.T(), update, get) - deleted, err := syntheticsAPI.Monitor.Delete(space, monitor.ConfigId) + deleted, err := syntheticsAPI.Monitor.Delete(ctx, space, monitor.ConfigId) assert.NoError(s.T(), err) for _, d := range deleted { assert.True(s.T(), d.Deleted) } - deleted, err = syntheticsAPI.Monitor.Delete(space, monitor.Id) + deleted, err = syntheticsAPI.Monitor.Delete(ctx, space, monitor.Id) assert.NoError(s.T(), err) for _, d := range deleted { assert.False(s.T(), d.Deleted) } - _, err = syntheticsAPI.Monitor.Get(monitor.Id, space) + _, err = syntheticsAPI.Monitor.Get(ctx, monitor.Id, space) assert.Error(s.T(), err) assert.IsType(s.T(), APIError{}, err) assert.Equal(s.T(), 404, err.(APIError).Code) @@ -299,6 +302,8 @@ func updateDueToKibanaAPIDiff(m *SyntheticsMonitor) { func (s *KBAPITestSuite) TestKibanaSyntheticsPrivateLocationAPI() { + ctx := context.Background() + for _, n := range namespaces { testUuid := uuid.New().String() space := n @@ -316,23 +321,23 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsPrivateLocationAPI() { Lon: -42.42, }, } - created, err := pAPI.Create(cfg, space) + created, err := pAPI.Create(ctx, cfg, space) assert.NoError(s.T(), err) assert.Equal(s.T(), created.Label, cfg.Label) assert.Equal(s.T(), created.AgentPolicyId, cfg.AgentPolicyId) - get, err := pAPI.Get(created.Id, space) + get, err := pAPI.Get(ctx, created.Id, space) assert.NoError(s.T(), err) assert.Equal(s.T(), created, get) - get, err = pAPI.Get(created.Label, space) + get, err = pAPI.Get(ctx, created.Label, space) assert.NoError(s.T(), err) assert.Equal(s.T(), created, get) - err = pAPI.Delete(created.Id, space) + err = pAPI.Delete(ctx, created.Id, space) assert.NoError(s.T(), err) - _, err = pAPI.Get(created.Id, space) + _, err = pAPI.Get(ctx, created.Id, space) assert.Error(s.T(), err) }) }) @@ -346,10 +351,11 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsPrivateLocationNotFound() { pAPI := s.API.KibanaSynthetics.PrivateLocation ids := []string{"", "not-found", testUuid} + ctx := context.Background() for _, id := range ids { s.Run(fmt.Sprintf("TestKibanaSyntheticsPrivateLocationNotFound - %s - %s", n, id), func() { - _, err := pAPI.Get(id, space) + _, err := pAPI.Get(ctx, id, space) assert.Error(s.T(), err) assert.IsType(s.T(), APIError{}, err) assert.Equal(s.T(), 404, err.(APIError).Code) From a4432300fe49241b89682b2f6246742d81fa65f6 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Thu, 15 Aug 2024 14:56:25 +0200 Subject: [PATCH 59/72] use composite id for monitors to import from napespaces --- internal/kibana/synthetics/delete.go | 9 +++++++-- internal/kibana/synthetics/read.go | 10 ++++++++-- internal/kibana/synthetics/schema.go | 19 ++++++++++++++++++- internal/kibana/synthetics/schema_test.go | 8 ++++---- internal/kibana/synthetics/update.go | 9 +++++++-- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/internal/kibana/synthetics/delete.go b/internal/kibana/synthetics/delete.go index 4e7834bdf..3b540f74b 100644 --- a/internal/kibana/synthetics/delete.go +++ b/internal/kibana/synthetics/delete.go @@ -24,9 +24,14 @@ func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, r return } - monitorId := kbapi.MonitorID(plan.ID.ValueString()) + monitorId, dg := plan.getCompositeId() + response.Diagnostics.Append(dg...) + if response.Diagnostics.HasError() { + return + } + namespace := plan.SpaceID.ValueString() - _, err := kibanaClient.KibanaSynthetics.Monitor.Delete(ctx, namespace, monitorId) + _, err := kibanaClient.KibanaSynthetics.Monitor.Delete(ctx, namespace, kbapi.MonitorID(monitorId.ResourceId)) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", monitorId, namespace), err.Error()) diff --git a/internal/kibana/synthetics/read.go b/internal/kibana/synthetics/read.go index 1a583364a..81ba0da7f 100644 --- a/internal/kibana/synthetics/read.go +++ b/internal/kibana/synthetics/read.go @@ -25,8 +25,14 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo return } - namespace := state.SpaceID.ValueString() - monitorId := kbapi.MonitorID(state.ID.ValueString()) + compositeId, dg := state.getCompositeId() + response.Diagnostics.Append(dg...) + if response.Diagnostics.HasError() { + return + } + + namespace := compositeId.ClusterId + monitorId := kbapi.MonitorID(compositeId.ResourceId) result, err := kibanaClient.KibanaSynthetics.Monitor.Get(ctx, monitorId, namespace) if err != nil { var apiError *kbapi.APIError diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index e936afa51..817b58697 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -3,6 +3,7 @@ package synthetics import ( "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" + "github.com/elastic/terraform-provider-elasticstack/internal/clients" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -424,8 +425,13 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { // return nil, err //} + resourceID := clients.CompositeId{ + ClusterId: api.Namespace, + ResourceId: string(api.Id), + } + return &tfModelV0{ - ID: types.StringValue(string(api.Id)), + ID: types.StringValue(resourceID.String()), Name: types.StringValue(api.Name), SpaceID: types.StringValue(api.Namespace), Schedule: types.Int64Value(schedule), @@ -604,6 +610,17 @@ func (v *tfModelV0) toTCPMonitorFields() kbapi.MonitorFields { } } +func (v *tfModelV0) getCompositeId() (*clients.CompositeId, diag.Diagnostics) { + idStr := v.ID.ValueString() + compositeID, sdkDiag := clients.CompositeIdFromStr(idStr) + dg := diag.Diagnostics{} + if sdkDiag.HasError() { + dg.AddError(fmt.Sprintf("Failed to parse monitor ID %s", idStr), fmt.Sprintf("Resource ID must have following format: /. Current value: %s", idStr)) + return nil, dg + } + return compositeID, dg +} + func Map[T, U any](ts []T, f func(T) U) []U { var us []U for _, v := range ts { diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go index 17d29e5b8..c47807569 100644 --- a/internal/kibana/synthetics/schema_test.go +++ b/internal/kibana/synthetics/schema_test.go @@ -32,7 +32,7 @@ func TestToModelV0(t *testing.T) { Type: kbapi.Http, }, expected: tfModelV0{ - ID: types.StringValue(""), + ID: types.StringValue("/"), Name: types.StringValue(""), SpaceID: types.StringValue(""), Schedule: types.Int64Value(0), @@ -57,7 +57,7 @@ func TestToModelV0(t *testing.T) { Type: kbapi.Tcp, }, expected: tfModelV0{ - ID: types.StringValue(""), + ID: types.StringValue("/"), Name: types.StringValue(""), SpaceID: types.StringValue(""), Schedule: types.Int64Value(0), @@ -116,7 +116,7 @@ func TestToModelV0(t *testing.T) { SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, }, expected: tfModelV0{ - ID: types.StringValue("test-id-http"), + ID: types.StringValue("default/test-id-http"), Name: types.StringValue("test-name-http"), SpaceID: types.StringValue("default"), Schedule: types.Int64Value(5), @@ -173,7 +173,7 @@ func TestToModelV0(t *testing.T) { ProxyUseLocalResolver: tBool, }, expected: tfModelV0{ - ID: types.StringValue("test-id-tcp"), + ID: types.StringValue("default/test-id-tcp"), Name: types.StringValue("test-name-tcp"), SpaceID: types.StringValue("default"), Schedule: types.Int64Value(5), diff --git a/internal/kibana/synthetics/update.go b/internal/kibana/synthetics/update.go index 4e800ab6b..36e0c0be0 100644 --- a/internal/kibana/synthetics/update.go +++ b/internal/kibana/synthetics/update.go @@ -30,9 +30,14 @@ func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, r return } + monitorId, dg := plan.getCompositeId() + response.Diagnostics.Append(dg...) + if response.Diagnostics.HasError() { + return + } + namespace := plan.SpaceID.ValueString() - monitorId := kbapi.MonitorID(plan.ID.ValueString()) - result, err := kibanaClient.KibanaSynthetics.Monitor.Update(ctx, monitorId, input.config, input.fields, namespace) + result, err := kibanaClient.KibanaSynthetics.Monitor.Update(ctx, kbapi.MonitorID(monitorId.ResourceId), input.config, input.fields, namespace) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("Failed to update Kibana monitor `%s`, namespace %s", input.config.Name, namespace), err.Error()) return From 1cbe62373b026a176040a2b91294a492dedd0a6a Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Thu, 15 Aug 2024 15:16:37 +0200 Subject: [PATCH 60/72] migrate private location to composite id --- internal/kibana/synthetics/delete.go | 6 +++--- .../synthetics/private_location/create.go | 4 ++-- .../synthetics/private_location/delete.go | 11 +++++++--- .../synthetics/private_location/read.go | 13 ++++++++---- .../synthetics/private_location/resource.go | 2 +- .../synthetics/private_location/schema.go | 19 +++++++++-------- .../private_location/schema_test.go | 10 ++++++++- internal/kibana/synthetics/read.go | 2 +- internal/kibana/synthetics/schema.go | 21 +++++++++---------- internal/kibana/synthetics/update.go | 2 +- 10 files changed, 54 insertions(+), 36 deletions(-) diff --git a/internal/kibana/synthetics/delete.go b/internal/kibana/synthetics/delete.go index 3b540f74b..8a24cd646 100644 --- a/internal/kibana/synthetics/delete.go +++ b/internal/kibana/synthetics/delete.go @@ -24,17 +24,17 @@ func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, r return } - monitorId, dg := plan.getCompositeId() + compositeId, dg := GetCompositeId(plan.ID.ValueString()) response.Diagnostics.Append(dg...) if response.Diagnostics.HasError() { return } namespace := plan.SpaceID.ValueString() - _, err := kibanaClient.KibanaSynthetics.Monitor.Delete(ctx, namespace, kbapi.MonitorID(monitorId.ResourceId)) + _, err := kibanaClient.KibanaSynthetics.Monitor.Delete(ctx, namespace, kbapi.MonitorID(compositeId.ResourceId)) if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", monitorId, namespace), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", compositeId, namespace), err.Error()) return } } diff --git a/internal/kibana/synthetics/private_location/create.go b/internal/kibana/synthetics/private_location/create.go index bfa3633d7..7fd4c8a34 100644 --- a/internal/kibana/synthetics/private_location/create.go +++ b/internal/kibana/synthetics/private_location/create.go @@ -21,10 +21,10 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r return } - input := plan.toPrivateLocation() + input := plan.toPrivateLocationConfig() namespace := plan.SpaceID.ValueString() - result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Create(ctx, input.PrivateLocationConfig, namespace) + result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Create(ctx, input, namespace) if err != nil { response.Diagnostics.AddError(fmt.Sprintf("Failed to create private location `%s`, namespace %s", input.Label, namespace), err.Error()) return diff --git a/internal/kibana/synthetics/private_location/delete.go b/internal/kibana/synthetics/private_location/delete.go index c32b076c1..69bbe280a 100644 --- a/internal/kibana/synthetics/private_location/delete.go +++ b/internal/kibana/synthetics/private_location/delete.go @@ -21,12 +21,17 @@ func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, r return } - id := plan.ID.ValueString() + monitorId, dg := synthetics.GetCompositeId(plan.ID.ValueString()) + response.Diagnostics.Append(dg...) + if response.Diagnostics.HasError() { + return + } + namespace := plan.SpaceID.ValueString() - err := kibanaClient.KibanaSynthetics.PrivateLocation.Delete(ctx, id, namespace) + err := kibanaClient.KibanaSynthetics.PrivateLocation.Delete(ctx, monitorId.ResourceId, namespace) if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", id, namespace), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", monitorId, namespace), err.Error()) return } diff --git a/internal/kibana/synthetics/private_location/read.go b/internal/kibana/synthetics/private_location/read.go index c12179889..15bd87662 100644 --- a/internal/kibana/synthetics/private_location/read.go +++ b/internal/kibana/synthetics/private_location/read.go @@ -23,9 +23,14 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo return } - label := state.Label.ValueString() - namespace := state.SpaceID.ValueString() - result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Get(ctx, label, namespace) + compositeId, dg := synthetics.GetCompositeId(state.ID.ValueString()) + response.Diagnostics.Append(dg...) + if response.Diagnostics.HasError() { + return + } + + namespace := compositeId.ClusterId + result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Get(ctx, compositeId.ResourceId, namespace) if err != nil { var apiError *kbapi.APIError if errors.As(err, &apiError) && apiError.Code == 404 { @@ -33,7 +38,7 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo return } - response.Diagnostics.AddError(fmt.Sprintf("Failed to get private location `%s`, namespace %s", label, namespace), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("Failed to get private location `%s`, namespace %s", compositeId, namespace), err.Error()) return } diff --git a/internal/kibana/synthetics/private_location/resource.go b/internal/kibana/synthetics/private_location/resource.go index c685ca9ea..c4023e7de 100644 --- a/internal/kibana/synthetics/private_location/resource.go +++ b/internal/kibana/synthetics/private_location/resource.go @@ -31,7 +31,7 @@ func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *res } func (r *Resource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { - resource.ImportStatePassthroughID(ctx, path.Root("label"), request, response) + resource.ImportStatePassthroughID(ctx, path.Root("id"), request, response) } func (r *Resource) Configure(ctx context.Context, request resource.ConfigureRequest, response *resource.ConfigureResponse) { diff --git a/internal/kibana/synthetics/private_location/schema.go b/internal/kibana/synthetics/private_location/schema.go index 35648f57f..4ad0b6a38 100644 --- a/internal/kibana/synthetics/private_location/schema.go +++ b/internal/kibana/synthetics/private_location/schema.go @@ -2,6 +2,7 @@ package private_location import ( "github.com/disaster37/go-kibana-rest/v8/kbapi" + "github.com/elastic/terraform-provider-elasticstack/internal/clients" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" @@ -71,29 +72,29 @@ func privateLocationSchema() schema.Schema { } } -func (m *tfModelV0) toPrivateLocation() kbapi.PrivateLocation { +func (m *tfModelV0) toPrivateLocationConfig() kbapi.PrivateLocationConfig { var geoConfig *kbapi.SyntheticGeoConfig if m.Geo != nil { geoConfig = m.Geo.ToSyntheticGeoConfig() } - pLoc := kbapi.PrivateLocationConfig{ + return kbapi.PrivateLocationConfig{ Label: m.Label.ValueString(), AgentPolicyId: m.AgentPolicyId.ValueString(), Tags: synthetics.ValueStringSlice(m.Tags), Geo: geoConfig, } - - return kbapi.PrivateLocation{ - Id: m.ID.ValueString(), - Namespace: m.SpaceID.ValueString(), - PrivateLocationConfig: pLoc, - } } func toModelV0(pLoc kbapi.PrivateLocation) tfModelV0 { + + resourceID := clients.CompositeId{ + ClusterId: pLoc.Namespace, + ResourceId: pLoc.Id, + } + return tfModelV0{ - ID: types.StringValue(pLoc.Id), + ID: types.StringValue(resourceID.String()), Label: types.StringValue(pLoc.Label), SpaceID: types.StringValue(pLoc.Namespace), AgentPolicyId: types.StringValue(pLoc.AgentPolicyId), diff --git a/internal/kibana/synthetics/private_location/schema_test.go b/internal/kibana/synthetics/private_location/schema_test.go index c22c6a846..60ba3b047 100644 --- a/internal/kibana/synthetics/private_location/schema_test.go +++ b/internal/kibana/synthetics/private_location/schema_test.go @@ -1,6 +1,7 @@ package private_location import ( + "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "testing" "github.com/disaster37/go-kibana-rest/v8/kbapi" @@ -71,7 +72,14 @@ func Test_roundtrip(t *testing.T) { PrivateLocationConfig: plc, } modelV0 := toModelV0(input) - actual := modelV0.toPrivateLocation() + + compositeId, _ := synthetics.GetCompositeId(modelV0.ID.ValueString()) + + actual := kbapi.PrivateLocation{ + Id: compositeId.ResourceId, + Namespace: modelV0.SpaceID.ValueString(), + PrivateLocationConfig: modelV0.toPrivateLocationConfig(), + } assert.Equal(t, input, actual) }) } diff --git a/internal/kibana/synthetics/read.go b/internal/kibana/synthetics/read.go index 81ba0da7f..af5c64196 100644 --- a/internal/kibana/synthetics/read.go +++ b/internal/kibana/synthetics/read.go @@ -25,7 +25,7 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo return } - compositeId, dg := state.getCompositeId() + compositeId, dg := GetCompositeId(state.ID.ValueString()) response.Diagnostics.Append(dg...) if response.Diagnostics.HasError() { return diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index 817b58697..e831d6833 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -83,6 +83,16 @@ type tfModelV0 struct { //RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` } +func GetCompositeId(id string) (*clients.CompositeId, diag.Diagnostics) { + compositeID, sdkDiag := clients.CompositeIdFromStr(id) + dg := diag.Diagnostics{} + if sdkDiag.HasError() { + dg.AddError(fmt.Sprintf("Failed to parse monitor ID %s", id), fmt.Sprintf("Resource ID must have following format: /. Current value: %s", id)) + return nil, dg + } + return compositeID, dg +} + func monitorConfigSchema() schema.Schema { return schema.Schema{ MarkdownDescription: "Synthetics monitor config, see https://www.elastic.co/guide/en/kibana/current/add-monitor-api.html for more details. The monitor must have one of the following: http, tcp, icmp or browser.", @@ -610,17 +620,6 @@ func (v *tfModelV0) toTCPMonitorFields() kbapi.MonitorFields { } } -func (v *tfModelV0) getCompositeId() (*clients.CompositeId, diag.Diagnostics) { - idStr := v.ID.ValueString() - compositeID, sdkDiag := clients.CompositeIdFromStr(idStr) - dg := diag.Diagnostics{} - if sdkDiag.HasError() { - dg.AddError(fmt.Sprintf("Failed to parse monitor ID %s", idStr), fmt.Sprintf("Resource ID must have following format: /. Current value: %s", idStr)) - return nil, dg - } - return compositeID, dg -} - func Map[T, U any](ts []T, f func(T) U) []U { var us []U for _, v := range ts { diff --git a/internal/kibana/synthetics/update.go b/internal/kibana/synthetics/update.go index 36e0c0be0..ab34f5830 100644 --- a/internal/kibana/synthetics/update.go +++ b/internal/kibana/synthetics/update.go @@ -30,7 +30,7 @@ func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, r return } - monitorId, dg := plan.getCompositeId() + monitorId, dg := GetCompositeId(plan.ID.ValueString()) response.Diagnostics.Append(dg...) if response.Diagnostics.HasError() { return From 7bda56b50022703f414820c279028d3fb30fda24 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 16 Aug 2024 17:48:17 +0200 Subject: [PATCH 61/72] implement merge attributes --- docs/resources/kibana_synthetics_monitor.md | 13 +- internal/kibana/synthetics/acc_test.go | 41 +++ internal/kibana/synthetics/create.go | 7 +- internal/kibana/synthetics/delete.go | 3 - internal/kibana/synthetics/read.go | 7 +- internal/kibana/synthetics/schema.go | 285 +++++++++++--------- internal/kibana/synthetics/schema_test.go | 95 ++++--- internal/kibana/synthetics/update.go | 7 +- 8 files changed, 274 insertions(+), 184 deletions(-) diff --git a/docs/resources/kibana_synthetics_monitor.md b/docs/resources/kibana_synthetics_monitor.md index 9d681896e..fdea98387 100644 --- a/docs/resources/kibana_synthetics_monitor.md +++ b/docs/resources/kibana_synthetics_monitor.md @@ -70,14 +70,16 @@ resource "elasticstack_kibana_synthetics_monitor" "my_monitor" { - `alert` (Attributes) Alert configuration. Default: `{ status: { enabled: true }, tls: { enabled: true } }`. (see [below for nested schema](#nestedatt--alert)) - `enabled` (Boolean) Whether the monitor is enabled. Default: `true` -- `http` (Attributes) TODO (see [below for nested schema](#nestedatt--http)) +- `http` (Attributes) HTTP Monitor specific fields (see [below for nested schema](#nestedatt--http)) - `locations` (List of String) Where to deploy the monitor. Monitors can be deployed in multiple locations so that you can detect differences in availability and response times across those locations. +- `params` (String) Monitor parameters. Raw JSON object, use `jsonencode` function to represent JSON - `private_locations` (List of String) These Private Locations refer to locations hosted and managed by you, whereas locations are hosted by Elastic. You can specify a Private Location using the location’s name. +- `retest_on_failure` (Boolean) Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from "up" to "down". If the result of the retest is also "down", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`. - `schedule` (Number) The monitor’s schedule in minutes. Supported values are 1, 3, 5, 10, 15, 30, 60, 120 and 240. - `service_name` (String) The APM service name. - `space_id` (String) The namespace field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \, /, ?, ", <, >, |, whitespace, ,, #, :, or -. Default: `default` - `tags` (List of String) An array of tags. -- `tcp` (Attributes) TODO (see [below for nested schema](#nestedatt--tcp)) +- `tcp` (Attributes) TCP Monitor specific fields (see [below for nested schema](#nestedatt--tcp)) - `timeout` (Number) The monitor timeout in seconds, monitor will fail if it doesn’t complete within this time. Default: `16` ### Read-Only @@ -118,13 +120,18 @@ Required: Optional: +- `check` (String) The check request settings.. Raw JSON object, use `jsonencode` function to represent JSON - `ipv4` (Boolean) Whether to ping using the ipv4 protocol. - `ipv6` (Boolean) Whether to ping using the ipv6 protocol. - `max_redirects` (Number) The maximum number of redirects to follow. Default: `0` - `mode` (String) The mode of the monitor. Can be "all" or "any". If you’re using a DNS-load balancer and want to ping every IP address for the specified hostname, you should use all. +- `password` (String) The password for authenticating with the server. The credentials are passed with the request. +- `proxy_header` (String) Additional headers to send to proxies during CONNECT requests.. Raw JSON object, use `jsonencode` function to represent JSON - `proxy_url` (String) The URL of the proxy to use for this monitor. +- `response` (String) Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field.. Raw JSON object, use `jsonencode` function to represent JSON - `ssl_supported_protocols` (List of String) List of allowed SSL/TLS versions. - `ssl_verification_mode` (String) Controls the verification of server certificates. +- `username` (String) The username for authenticating with the server. The credentials are passed with the request. @@ -136,6 +143,8 @@ Required: Optional: +- `check_receive` (String) The expected answer. +- `check_send` (String) An optional payload string to send to the remote host. - `proxy_url` (String) The URL of the SOCKS5 proxy to use when connecting to the server. The value must be a URL with a scheme of `socks5://`. If the SOCKS5 proxy server requires client authentication, then a username and password can be embedded in the URL. When using a proxy, hostnames are resolved on the proxy server instead of on the client. You can change this behavior by setting the `proxy_use_local_resolver` option. - `proxy_use_local_resolver` (Boolean) A Boolean value that determines whether hostnames are resolved locally instead of being resolved on the proxy server. The default value is false, which means that name resolution occurs on the proxy server. - `ssl_supported_protocols` (List of String) List of allowed SSL/TLS versions. diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index 34a6af1bc..817d533e6 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -103,7 +103,35 @@ resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { ipv4 = true ipv6 = true proxy_url = "http://localhost" + proxy_header = jsonencode({ + "header-name" = "header-value-updated" + }) + username = "testupdated" + password = "testpassword-updated" + check = jsonencode({ + "request": { + "method": "POST", + "headers": { + "Content-Type": "application/x-www-form-urlencoded", + }, + "body": "name=first&email=someemail%40someemailprovider.com", + }, + "response": { + "status": [200, 201, 301], + "body": { + "positive": ["foo", "bar"] + } + } + }) + response = jsonencode({ + "include_body": "never", + "include_body_max_bytes": "1024", + }) } + params = jsonencode({ + "param-name" = "param-value-updated" + }) + retest_on_failure = false } ` @@ -161,6 +189,8 @@ resource "elasticstack_kibana_synthetics_monitor" "tcp-monitor" { ssl_supported_protocols = ["TLSv1.2"] proxy_url = "http://localhost" proxy_use_local_resolver = false + check_send = "Hello Updated" + check_receive = "World Updated" } } @@ -243,6 +273,14 @@ func TestSyntheticMonitorResource(t *testing.T) { resource.TestCheckResourceAttr(httpMonitorId, "http.ipv6", "true"), resource.TestCheckResourceAttr(httpMonitorId, "http.proxy_url", "http://localhost"), resource.TestCheckNoResourceAttr(httpMonitorId, "tcp"), + //check for merge attributes + resource.TestCheckResourceAttr(httpMonitorId, "http.proxy_header", `{"header-name":"header-value-updated"}`), + resource.TestCheckResourceAttr(httpMonitorId, "http.username", "testupdated"), + resource.TestCheckResourceAttr(httpMonitorId, "http.password", "testpassword-updated"), + resource.TestCheckResourceAttr(httpMonitorId, "http.check", `{"request":{"body":"name=first\u0026email=someemail%40someemailprovider.com","headers":{"Content-Type":"application/x-www-form-urlencoded"},"method":"POST"},"response":{"body":{"positive":["foo","bar"]},"status":[200,201,301]}}`), + resource.TestCheckResourceAttr(httpMonitorId, "http.response", `{"include_body":"never","include_body_max_bytes":"1024"}`), + resource.TestCheckResourceAttr(httpMonitorId, "params", `{"param-name":"param-value-updated"}`), + resource.TestCheckResourceAttr(httpMonitorId, "retest_on_failure", "false"), ), }, // Create and Read tcp monitor @@ -310,6 +348,9 @@ func TestSyntheticMonitorResource(t *testing.T) { resource.TestCheckResourceAttr(tcpMonitorId, "tcp.proxy_url", "http://localhost"), resource.TestCheckResourceAttr(tcpMonitorId, "tcp.proxy_use_local_resolver", "false"), resource.TestCheckNoResourceAttr(tcpMonitorId, "http"), + //check for merge attributes + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.check_send", "Hello Updated"), + resource.TestCheckResourceAttr(tcpMonitorId, "tcp.check_receive", "World Updated"), ), }, // Delete testing automatically occurs in TestCase diff --git a/internal/kibana/synthetics/create.go b/internal/kibana/synthetics/create.go index 8f7ef2dbb..26cc9285f 100644 --- a/internal/kibana/synthetics/create.go +++ b/internal/kibana/synthetics/create.go @@ -4,19 +4,16 @@ import ( "context" "fmt" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { - tflog.Info(ctx, "### Create monitor") - kibanaClient := GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return } - var plan *tfModelV0 = new(tfModelV0) + plan := new(tfModelV0) diags := request.Plan.Get(ctx, plan) response.Diagnostics.Append(diags...) if response.Diagnostics.HasError() { @@ -36,7 +33,7 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r return } - plan, err = toModelV0(result) + plan, err = plan.toModelV0(result) if err != nil { response.Diagnostics.AddError("Failed to convert Kibana monitor API to TF state", err.Error()) return diff --git a/internal/kibana/synthetics/delete.go b/internal/kibana/synthetics/delete.go index 8a24cd646..fc93d57a2 100644 --- a/internal/kibana/synthetics/delete.go +++ b/internal/kibana/synthetics/delete.go @@ -5,13 +5,10 @@ import ( "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { - tflog.Info(ctx, "### Delete monitor") - kibanaClient := GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return diff --git a/internal/kibana/synthetics/read.go b/internal/kibana/synthetics/read.go index af5c64196..d38799baa 100644 --- a/internal/kibana/synthetics/read.go +++ b/internal/kibana/synthetics/read.go @@ -6,19 +6,16 @@ import ( "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { - tflog.Info(ctx, "### Read monitor") - kibanaClient := GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return } - var state *tfModelV0 = new(tfModelV0) + state := new(tfModelV0) diags := request.State.Get(ctx, state) response.Diagnostics.Append(diags...) if response.Diagnostics.HasError() { @@ -45,7 +42,7 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo return } - state, err = toModelV0(result) + state, err = state.toModelV0(result) if err != nil { response.Diagnostics.AddError("Failed to convert Kibana monitor API to TF state", err.Error()) return diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index e831d6833..2f8267a4b 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -1,9 +1,11 @@ package synthetics import ( + "encoding/json" "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/elastic/terraform-provider-elasticstack/internal/clients" + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" @@ -35,32 +37,29 @@ type tfAlertConfigV0 struct { } type tfHTTPMonitorFieldsV0 struct { - URL types.String `tfsdk:"url"` - SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` - SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` - MaxRedirects types.Int64 `tfsdk:"max_redirects"` - Mode types.String `tfsdk:"mode"` - IPv4 types.Bool `tfsdk:"ipv4"` - IPv6 types.Bool `tfsdk:"ipv6"` - ProxyURL types.String `tfsdk:"proxy_url"` - // commented out due-to https://github.com/elastic/kibana/issues/189906 - // it leads to terraform Error: Provider produced inconsistent result after apply - //ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` - //Username types.String `tfsdk:"username"` - //Password types.String `tfsdk:"password"` - //Response jsontypes.Normalized `tfsdk:"response"` - //Check jsontypes.Normalized `tfsdk:"check"` + URL types.String `tfsdk:"url"` + SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` + SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` + MaxRedirects types.Int64 `tfsdk:"max_redirects"` + Mode types.String `tfsdk:"mode"` + IPv4 types.Bool `tfsdk:"ipv4"` + IPv6 types.Bool `tfsdk:"ipv6"` + ProxyURL types.String `tfsdk:"proxy_url"` + ProxyHeader jsontypes.Normalized `tfsdk:"proxy_header"` + Username types.String `tfsdk:"username"` + Password types.String `tfsdk:"password"` + Response jsontypes.Normalized `tfsdk:"response"` + Check jsontypes.Normalized `tfsdk:"check"` } type tfTCPMonitorFieldsV0 struct { Host types.String `tfsdk:"host"` SslVerificationMode types.String `tfsdk:"ssl_verification_mode"` SslSupportedProtocols []types.String `tfsdk:"ssl_supported_protocols"` - // commented out due-to https://github.com/elastic/kibana/issues/189906 - //CheckSend types.String `tfsdk:"check_send"` - //CheckReceive types.String `tfsdk:"check_receive"` - ProxyURL types.String `tfsdk:"proxy_url"` - ProxyUseLocalResolver types.Bool `tfsdk:"proxy_use_local_resolver"` + CheckSend types.String `tfsdk:"check_send"` + CheckReceive types.String `tfsdk:"check_receive"` + ProxyURL types.String `tfsdk:"proxy_url"` + ProxyUseLocalResolver types.Bool `tfsdk:"proxy_use_local_resolver"` } type tfModelV0 struct { @@ -77,10 +76,8 @@ type tfModelV0 struct { TimeoutSeconds types.Int64 `tfsdk:"timeout"` HTTP *tfHTTPMonitorFieldsV0 `tfsdk:"http"` TCP *tfTCPMonitorFieldsV0 `tfsdk:"tcp"` - // commented out due-to https://github.com/elastic/kibana/issues/189906 - // it leads to terraform Error: Provider produced inconsistent result after apply - //Params jsontypes.Normalized `tfsdk:"params"` - //RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` + Params jsontypes.Normalized `tfsdk:"params"` + RetestOnFailure types.Bool `tfsdk:"retest_on_failure"` } func GetCompositeId(id string) (*clients.CompositeId, diag.Diagnostics) { @@ -168,24 +165,24 @@ func monitorConfigSchema() schema.Schema { Optional: true, MarkdownDescription: "The monitor timeout in seconds, monitor will fail if it doesn’t complete within this time. Default: `16`", }, - //"params": jsonObjectSchema("Monitor parameters"), - "http": httpMonitorFieldsSchema(), - "tcp": tcpMonitorFieldsSchema(), - //"retest_on_failure": schema.BoolAttribute{ - // Optional: true, - // MarkdownDescription: "Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from \"up\" to \"down\". If the result of the retest is also \"down\", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`.", - //}, + "params": jsonObjectSchema("Monitor parameters"), + "http": httpMonitorFieldsSchema(), + "tcp": tcpMonitorFieldsSchema(), + "retest_on_failure": schema.BoolAttribute{ + Optional: true, + MarkdownDescription: "Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from \"up\" to \"down\". If the result of the retest is also \"down\", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`.", + }, }, } } -//func jsonObjectSchema(doc string) schema.Attribute { -// return schema.StringAttribute{ -// Optional: true, -// MarkdownDescription: fmt.Sprintf("%s. Raw JSON object, use `jsonencode` function to represent JSON", doc), -// CustomType: jsontypes.NormalizedType{}, -// } -//} +func jsonObjectSchema(doc string) schema.Attribute { + return schema.StringAttribute{ + Optional: true, + MarkdownDescription: fmt.Sprintf("%s. Raw JSON object, use `jsonencode` function to represent JSON", doc), + CustomType: jsontypes.NormalizedType{}, + } +} func statusConfigSchema() schema.Attribute { return schema.SingleNestedAttribute{ @@ -212,7 +209,7 @@ func monitorAlertConfigSchema() schema.Attribute { func httpMonitorFieldsSchema() schema.Attribute { return schema.SingleNestedAttribute{ Optional: true, - MarkdownDescription: "TODO", + MarkdownDescription: "HTTP Monitor specific fields", Attributes: map[string]schema.Attribute{ "url": schema.StringAttribute{ Optional: false, @@ -247,21 +244,21 @@ func httpMonitorFieldsSchema() schema.Attribute { Optional: true, MarkdownDescription: "Whether to ping using the ipv6 protocol.", }, - //"username": schema.StringAttribute{ - // Optional: true, - // MarkdownDescription: "The username for authenticating with the server. The credentials are passed with the request.", - //}, - //"password": schema.StringAttribute{ - // Optional: true, - // MarkdownDescription: "The password for authenticating with the server. The credentials are passed with the request.", - //}, - //"proxy_header": jsonObjectSchema("Additional headers to send to proxies during CONNECT requests."), + "username": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "The username for authenticating with the server. The credentials are passed with the request.", + }, + "password": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "The password for authenticating with the server. The credentials are passed with the request.", + }, + "proxy_header": jsonObjectSchema("Additional headers to send to proxies during CONNECT requests."), "proxy_url": schema.StringAttribute{ Optional: true, MarkdownDescription: "The URL of the proxy to use for this monitor.", }, - //"response": jsonObjectSchema("Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field."), - //"check": jsonObjectSchema("The check request settings."), + "response": jsonObjectSchema("Controls the indexing of the HTTP response body contents to the `http.response.body.contents` field."), + "check": jsonObjectSchema("The check request settings."), }, } } @@ -269,7 +266,7 @@ func httpMonitorFieldsSchema() schema.Attribute { func tcpMonitorFieldsSchema() schema.Attribute { return schema.SingleNestedAttribute{ Optional: true, - MarkdownDescription: "TODO", + MarkdownDescription: "TCP Monitor specific fields", Attributes: map[string]schema.Attribute{ "host": schema.StringAttribute{ Optional: false, @@ -285,14 +282,14 @@ func tcpMonitorFieldsSchema() schema.Attribute { Optional: true, MarkdownDescription: "List of allowed SSL/TLS versions.", }, - //"check_send": schema.StringAttribute{ - // Optional: true, - // MarkdownDescription: "An optional payload string to send to the remote host.", - //}, - //"check_receive": schema.StringAttribute{ - // Optional: true, - // MarkdownDescription: "The expected answer. ", - //}, + "check_send": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "An optional payload string to send to the remote host.", + }, + "check_receive": schema.StringAttribute{ + Optional: true, + MarkdownDescription: "The expected answer. ", + }, "proxy_url": schema.StringAttribute{ Optional: true, MarkdownDescription: "The URL of the SOCKS5 proxy to use when connecting to the server. The value must be a URL with a scheme of `socks5://`. If the SOCKS5 proxy server requires client authentication, then a username and password can be embedded in the URL. When using a proxy, hostnames are resolved on the proxy server instead of on the client. You can change this behavior by setting the `proxy_use_local_resolver` option.", @@ -362,25 +359,25 @@ func StringSliceValue(v []string) []types.String { return res } -//func toNormalizedValue(jsObj kbapi.JsonObject) (jsontypes.Normalized, error) { -// res, err := json.Marshal(jsObj) -// if err != nil { -// return jsontypes.NewNormalizedUnknown(), err -// } -// return jsontypes.NewNormalizedValue(string(res)), nil -//} -// -//func toJsonObject(v jsontypes.Normalized) (kbapi.JsonObject, diag.Diagnostics) { -// if v.IsNull() { -// return nil, diag.Diagnostics{} -// } -// var res kbapi.JsonObject -// dg := v.Unmarshal(&res) -// if dg.HasError() { -// return nil, dg -// } -// return res, diag.Diagnostics{} -//} +func toNormalizedValue(jsObj kbapi.JsonObject) (jsontypes.Normalized, error) { + res, err := json.Marshal(jsObj) + if err != nil { + return jsontypes.NewNormalizedUnknown(), err + } + return jsontypes.NewNormalizedValue(string(res)), nil +} + +func toJsonObject(v jsontypes.Normalized) (kbapi.JsonObject, diag.Diagnostics) { + if v.IsNull() { + return nil, diag.Diagnostics{} + } + var res kbapi.JsonObject + dg := v.Unmarshal(&res) + if dg.HasError() { + return nil, dg + } + return res, diag.Diagnostics{} +} func stringToInt64(v string) (int64, error) { var res int64 @@ -391,7 +388,7 @@ func stringToInt64(v string) (int64, error) { return res, err } -func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { +func (v *tfModelV0) toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { var schedule int64 var err error if api.Schedule != nil { @@ -417,11 +414,20 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { var http *tfHTTPMonitorFieldsV0 var tcp *tfTCPMonitorFieldsV0 + switch mType := api.Type; mType { case kbapi.Http: - http, err = toTfHTTPMonitorFieldsV0(api) + http = &tfHTTPMonitorFieldsV0{} + if v.HTTP != nil { + http = v.HTTP + } + http, err = http.toTfHTTPMonitorFieldsV0(api) case kbapi.Tcp: - tcp, err = toTfTCPMonitorFieldsV0(api) + tcp = &tfTCPMonitorFieldsV0{} + if v.TCP != nil { + tcp = v.TCP + } + tcp, err = tcp.toTfTCPMonitorFieldsV0(api) default: err = fmt.Errorf("unsupported monitor type: %s", mType) } @@ -430,10 +436,13 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { return nil, err } - //params, err := toNormalizedValue(api.Params) - //if err != nil { - // return nil, err - //} + params := v.Params + if api.Params != nil { + params, err = toNormalizedValue(api.Params) + if err != nil { + return nil, err + } + } resourceID := clients.CompositeId{ ClusterId: api.Namespace, @@ -452,30 +461,52 @@ func toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error) { Alert: toTfAlertConfigV0(api.Alert), APMServiceName: types.StringValue(api.APMServiceName), TimeoutSeconds: types.Int64Value(timeout), - //Params: params, - HTTP: http, - TCP: tcp, + Params: params, + HTTP: http, + TCP: tcp, + RetestOnFailure: v.RetestOnFailure, }, nil } -func toTfTCPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfTCPMonitorFieldsV0, error) { +func (v *tfTCPMonitorFieldsV0) toTfTCPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfTCPMonitorFieldsV0, error) { + checkSend := v.CheckSend + if api.CheckSend != "" { + checkSend = types.StringValue(api.CheckSend) + } + checkReceive := v.CheckReceive + if api.CheckReceive != "" { + checkReceive = types.StringValue(api.CheckReceive) + } return &tfTCPMonitorFieldsV0{ Host: types.StringValue(api.Host), SslVerificationMode: types.StringValue(api.SslVerificationMode), SslSupportedProtocols: StringSliceValue(api.SslSupportedProtocols), - //CheckSend: types.StringValue(api.CheckSend), - //CheckReceive: types.StringValue(api.CheckReceive), + CheckSend: checkSend, + CheckReceive: checkReceive, ProxyURL: types.StringValue(api.ProxyUrl), ProxyUseLocalResolver: types.BoolPointerValue(api.ProxyUseLocalResolver), }, nil } -func toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFieldsV0, error) { +func (v *tfHTTPMonitorFieldsV0) toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFieldsV0, error) { - //proxyHeaders, err := toNormalizedValue(api.ProxyHeaders) - //if err != nil { - // return nil, err - //} + var err error + proxyHeaders := v.ProxyHeader + if api.ProxyHeaders != nil { + proxyHeaders, err = toNormalizedValue(api.ProxyHeaders) + if err != nil { + return nil, err + } + } + + username := v.Username + if api.Username != "" { + username = types.StringValue(api.Username) + } + password := v.Password + if api.Password != "" { + password = types.StringValue(api.Password) + } maxRedirects, err := stringToInt64(api.MaxRedirects) if err != nil { @@ -490,10 +521,12 @@ func toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFields Mode: types.StringValue(string(api.Mode)), IPv4: types.BoolPointerValue(api.Ipv4), IPv6: types.BoolPointerValue(api.Ipv6), - //Username: types.StringValue(api.Username), - //Password: types.StringValue(api.Password), - //ProxyHeader: proxyHeaders, - ProxyURL: types.StringValue(api.ProxyUrl), + Username: username, + Password: password, + ProxyHeader: proxyHeaders, + ProxyURL: types.StringValue(api.ProxyUrl), + Check: v.Check, + Response: v.Response, }, nil } @@ -547,10 +580,10 @@ func (v *tfModelV0) toMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, diag.Diagnostics) { locations := Map[types.String, kbapi.MonitorLocation](v.Locations, func(s types.String) kbapi.MonitorLocation { return kbapi.MonitorLocation(s.ValueString()) }) - //params, dg := toJsonObject(v.Params) - //if dg.HasError() { - // return nil, dg - //} + params, dg := toJsonObject(v.Params) + if dg.HasError() { + return nil, dg + } var alert *kbapi.MonitorAlertConfig if v.Alert != nil { @@ -568,24 +601,24 @@ func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig, APMServiceName: v.APMServiceName.ValueString(), TimeoutSeconds: int(v.TimeoutSeconds.ValueInt64()), Namespace: v.SpaceID.ValueString(), - //Params: params, - //RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(), + Params: params, + RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(), }, diag.Diagnostics{} //dg } func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) { - //proxyHeaders, dg := toJsonObject(v.HTTP.ProxyHeader) - //if dg.HasError() { - // return nil, dg - //} - //response, dg := toJsonObject(v.HTTP.Response) - //if dg.HasError() { - // return nil, dg - //} - //check, dg := toJsonObject(v.HTTP.Check) - //if dg.HasError() { - // return nil, dg - //} + proxyHeaders, dg := toJsonObject(v.HTTP.ProxyHeader) + if dg.HasError() { + return nil, dg + } + response, dg := toJsonObject(v.HTTP.Response) + if dg.HasError() { + return nil, dg + } + check, dg := toJsonObject(v.HTTP.Check) + if dg.HasError() { + return nil, dg + } maxRedirects := "" if !v.HTTP.MaxRedirects.IsUnknown() && !v.HTTP.MaxRedirects.IsNull() { // handle omitempty case maxRedirects = strconv.FormatInt(v.HTTP.MaxRedirects.ValueInt64(), 10) @@ -599,12 +632,12 @@ func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics Mode: kbapi.HttpMonitorMode(v.HTTP.Mode.ValueString()), Ipv4: v.HTTP.IPv4.ValueBoolPointer(), Ipv6: v.HTTP.IPv6.ValueBoolPointer(), - //Username: v.HTTP.Username.ValueString(), - //Password: v.HTTP.Password.ValueString(), - //ProxyHeader: proxyHeaders, - ProxyUrl: v.HTTP.ProxyURL.ValueString(), - //Response: response, - //Check: check, + Username: v.HTTP.Username.ValueString(), + Password: v.HTTP.Password.ValueString(), + ProxyHeader: proxyHeaders, + ProxyUrl: v.HTTP.ProxyURL.ValueString(), + Response: response, + Check: check, }, diag.Diagnostics{} //dg } @@ -613,8 +646,8 @@ func (v *tfModelV0) toTCPMonitorFields() kbapi.MonitorFields { Host: v.TCP.Host.ValueString(), SslVerificationMode: v.TCP.SslVerificationMode.ValueString(), SslSupportedProtocols: ValueStringSlice(v.TCP.SslSupportedProtocols), - //CheckSend: v.TCP.CheckSend.ValueString(), - //CheckReceive: v.TCP.CheckReceive.ValueString(), + CheckSend: v.TCP.CheckSend.ValueString(), + CheckReceive: v.TCP.CheckReceive.ValueString(), ProxyUrl: v.TCP.ProxyURL.ValueString(), ProxyUseLocalResolver: v.TCP.ProxyUseLocalResolver.ValueBoolPointer(), } diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go index c47807569..da84ec8a7 100644 --- a/internal/kibana/synthetics/schema_test.go +++ b/internal/kibana/synthetics/schema_test.go @@ -2,6 +2,7 @@ package synthetics import ( "encoding/json" + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" "testing" "github.com/disaster37/go-kibana-rest/v8/kbapi" @@ -38,16 +39,18 @@ func TestToModelV0(t *testing.T) { Schedule: types.Int64Value(0), APMServiceName: types.StringValue(""), TimeoutSeconds: types.Int64Value(0), - //Params: jsontypes.NewNormalizedValue("null"), + Params: jsontypes.NewNormalizedValue("null"), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue(""), SslVerificationMode: types.StringValue(""), MaxRedirects: types.Int64Value(0), Mode: types.StringValue(""), - //Username: types.StringValue(""), - //Password: types.StringValue(""), - //ProxyHeader: jsontypes.NewNormalizedValue("null"), - ProxyURL: types.StringValue(""), + Username: types.StringValue(""), + Password: types.StringValue(""), + ProxyHeader: jsontypes.NewNormalizedValue("null"), + ProxyURL: types.StringValue(""), + Response: jsontypes.NewNormalizedValue("null"), + Check: jsontypes.NewNormalizedValue("null"), }, }, }, @@ -63,13 +66,13 @@ func TestToModelV0(t *testing.T) { Schedule: types.Int64Value(0), APMServiceName: types.StringValue(""), TimeoutSeconds: types.Int64Value(0), - //Params: jsontypes.NewNormalizedValue("null"), + Params: jsontypes.NewNormalizedValue("null"), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue(""), SslVerificationMode: types.StringValue(""), - //CheckSend: types.StringValue(""), - //CheckReceive: types.StringValue(""), - ProxyURL: types.StringValue(""), + CheckSend: types.StringValue(""), + CheckReceive: types.StringValue(""), + ProxyURL: types.StringValue(""), }, }, }, @@ -127,7 +130,7 @@ func TestToModelV0(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, APMServiceName: types.StringValue("test-service-http"), TimeoutSeconds: types.Int64Value(30), - //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue("https://example.com"), SslVerificationMode: types.StringValue("full"), @@ -136,10 +139,10 @@ func TestToModelV0(t *testing.T) { Mode: types.StringValue("all"), IPv4: types.BoolPointerValue(tBool), IPv6: types.BoolPointerValue(fBool), - //Username: types.StringValue("user"), - //Password: types.StringValue("pass"), - //ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), - ProxyURL: types.StringValue("https://proxy.com"), + Username: types.StringValue("user"), + Password: types.StringValue("pass"), + ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), }, }, }, @@ -184,13 +187,13 @@ func TestToModelV0(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, APMServiceName: types.StringValue("test-service-tcp"), TimeoutSeconds: types.Int64Value(30), - //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue("example.com:9200"), SslVerificationMode: types.StringValue("full"), SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, - //CheckSend: types.StringValue("hello"), - //CheckReceive: types.StringValue("world"), + CheckSend: types.StringValue("hello"), + CheckReceive: types.StringValue("world"), ProxyURL: types.StringValue("http://proxy.com"), ProxyUseLocalResolver: types.BoolPointerValue(tBool), }, @@ -200,7 +203,7 @@ func TestToModelV0(t *testing.T) { for _, tt := range testcases { t.Run(tt.name, func(t *testing.T) { - model, err := toModelV0(&tt.input) + model, err := tt.expected.toModelV0(&tt.input) assert.NoError(t, err) assert.Equal(t, &tt.expected, model) }) @@ -247,7 +250,7 @@ func TestToKibanaAPIRequest(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}, TLS: &tfStatusConfigV0{Enabled: types.BoolPointerValue(fBool)}}, APMServiceName: types.StringValue("test-service-http"), TimeoutSeconds: types.Int64Value(30), - //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), HTTP: &tfHTTPMonitorFieldsV0{ URL: types.StringValue("https://example.com"), SslVerificationMode: types.StringValue("full"), @@ -256,12 +259,12 @@ func TestToKibanaAPIRequest(t *testing.T) { Mode: types.StringValue("all"), IPv4: types.BoolPointerValue(tBool), IPv6: types.BoolPointerValue(fBool), - //Username: types.StringValue("user"), - //Password: types.StringValue("pass"), - //ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), - ProxyURL: types.StringValue("https://proxy.com"), - //Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), - //Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), + Username: types.StringValue("user"), + Password: types.StringValue("pass"), + ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + ProxyURL: types.StringValue("https://proxy.com"), + Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), + Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), }, }, expected: kibanaAPIRequest{ @@ -276,7 +279,7 @@ func TestToKibanaAPIRequest(t *testing.T) { APMServiceName: "test-service-http", Namespace: "default", TimeoutSeconds: 30, - //Params: kbapi.JsonObject{"param1": "value1"}, + Params: kbapi.JsonObject{"param1": "value1"}, }, fields: kbapi.HTTPMonitorFields{ Url: "https://example.com", @@ -286,12 +289,12 @@ func TestToKibanaAPIRequest(t *testing.T) { Mode: "all", Ipv4: tBool, Ipv6: fBool, - //Username: "user", - //Password: "pass", - //ProxyHeader: kbapi.JsonObject{"header1": "value1"}, - ProxyUrl: "https://proxy.com", - //Response: kbapi.JsonObject{"response1": "value1"}, - //Check: kbapi.JsonObject{"check1": "value1"}, + Username: "user", + Password: "pass", + ProxyHeader: kbapi.JsonObject{"header1": "value1"}, + ProxyUrl: "https://proxy.com", + Response: kbapi.JsonObject{"response1": "value1"}, + Check: kbapi.JsonObject{"check1": "value1"}, }, }, }, @@ -309,13 +312,13 @@ func TestToKibanaAPIRequest(t *testing.T) { Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}}, APMServiceName: types.StringValue("test-service-tcp"), TimeoutSeconds: types.Int64Value(30), - //Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), TCP: &tfTCPMonitorFieldsV0{ Host: types.StringValue("example.com:9200"), SslVerificationMode: types.StringValue("full"), SslSupportedProtocols: []types.String{types.StringValue("TLSv1.2"), types.StringValue("TLSv1.3")}, - //CheckSend: types.StringValue("hello"), - //CheckReceive: types.StringValue("world"), + CheckSend: types.StringValue("hello"), + CheckReceive: types.StringValue("world"), ProxyURL: types.StringValue("http://proxy.com"), ProxyUseLocalResolver: types.BoolPointerValue(tBool), }, @@ -332,14 +335,14 @@ func TestToKibanaAPIRequest(t *testing.T) { APMServiceName: "test-service-tcp", Namespace: "default", TimeoutSeconds: 30, - //Params: kbapi.JsonObject{"param1": "value1"}, + Params: kbapi.JsonObject{"param1": "value1"}, }, fields: kbapi.TCPMonitorFields{ Host: "example.com:9200", SslVerificationMode: "full", SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"}, - //CheckSend: "hello", - //CheckReceive: "world", + CheckSend: "hello", + CheckReceive: "world", ProxyUrl: "http://proxy.com", ProxyUseLocalResolver: tBool, }, @@ -355,3 +358,19 @@ func TestToKibanaAPIRequest(t *testing.T) { }) } } + +func TestToModelV0MergeAttributes(t *testing.T) { + state := &tfModelV0{ + HTTP: &tfHTTPMonitorFieldsV0{}, + } + + //TODO: tcp send and receive + + input := &kbapi.SyntheticsMonitor{ + Type: kbapi.Http, + } + + actual, err := state.toModelV0(input) + assert.NoError(t, err) + assert.NotNil(t, actual) +} diff --git a/internal/kibana/synthetics/update.go b/internal/kibana/synthetics/update.go index ab34f5830..bcd66b914 100644 --- a/internal/kibana/synthetics/update.go +++ b/internal/kibana/synthetics/update.go @@ -5,19 +5,16 @@ import ( "fmt" "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-log/tflog" ) func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) { - tflog.Info(ctx, "### Update monitor") - kibanaClient := GetKibanaClient(r, response.Diagnostics) if kibanaClient == nil { return } - var plan *tfModelV0 = new(tfModelV0) + plan := new(tfModelV0) diags := request.Plan.Get(ctx, plan) response.Diagnostics.Append(diags...) if response.Diagnostics.HasError() { @@ -43,7 +40,7 @@ func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, r return } - plan, err = toModelV0(result) + plan, err = plan.toModelV0(result) if err != nil { response.Diagnostics.AddError("Failed to convert Kibana monitor API to TF state", err.Error()) return From bc704d4ec3f6dca28cf1766a9a0b68eb00c88f67 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 16 Aug 2024 18:05:40 +0200 Subject: [PATCH 62/72] add unit tests for merge and udpate docs --- docs/resources/kibana_synthetics_monitor.md | 11 +-- internal/kibana/synthetics/schema_test.go | 90 ++++++++++++++++--- .../kibana_synthetics_monitor.md.tmpl | 11 +-- 3 files changed, 92 insertions(+), 20 deletions(-) diff --git a/docs/resources/kibana_synthetics_monitor.md b/docs/resources/kibana_synthetics_monitor.md index fdea98387..fee878dfc 100644 --- a/docs/resources/kibana_synthetics_monitor.md +++ b/docs/resources/kibana_synthetics_monitor.md @@ -15,11 +15,7 @@ See [API docs](https://www.elastic.co/guide/en/kibana/current/add-monitor-api.ht * `http` * `tcp` -**NOTE:** Not all monitor fields are supported due-to API limitation. -Full field support could be implemented after this [kibana issue](https://github.com/elastic/kibana/issues/189906) is resolved. - **NOTE:** Due-to nature of partial update API, reset values to defaults is not supported. - In case you would like to reset an optional monitor value, please set it explicitly or delete and create new monitor. @@ -156,4 +152,9 @@ Import is supported using the following syntax: ```shell terraform import elasticstack_kibana_synthetics_monitor.my_monitor -``` \ No newline at end of file +``` + +**NOTE:** Not all monitor fields are supported during the import due-to API limitation. +Full field support could be implemented after this [kibana issue](https://github.com/elastic/kibana/issues/189906) is resolved. + +Currently not supported fields during the import: `params`, `retest_on_failure`, `http.proxy_header`, `http.username`, `http.password`, `http.check`, `http.response`, `tcp.check_send`, `tcp.check_receive` diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go index da84ec8a7..a6a160e19 100644 --- a/internal/kibana/synthetics/schema_test.go +++ b/internal/kibana/synthetics/schema_test.go @@ -360,17 +360,87 @@ func TestToKibanaAPIRequest(t *testing.T) { } func TestToModelV0MergeAttributes(t *testing.T) { - state := &tfModelV0{ - HTTP: &tfHTTPMonitorFieldsV0{}, - } - - //TODO: tcp send and receive - input := &kbapi.SyntheticsMonitor{ - Type: kbapi.Http, + testcases := []struct { + name string + input kbapi.SyntheticsMonitor + state tfModelV0 + expected tfModelV0 + }{ + { + name: "HTTP monitor", + state: tfModelV0{ + HTTP: &tfHTTPMonitorFieldsV0{ + ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + Username: types.StringValue("test"), + Password: types.StringValue("password"), + Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), + Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), + }, + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + RetestOnFailure: types.BoolValue(true), + }, + input: kbapi.SyntheticsMonitor{ + Type: kbapi.Http, + }, + expected: tfModelV0{ + ID: types.StringValue("/"), + Name: types.StringValue(""), + SpaceID: types.StringValue(""), + Schedule: types.Int64Value(0), + APMServiceName: types.StringValue(""), + TimeoutSeconds: types.Int64Value(0), + Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`), + RetestOnFailure: types.BoolValue(true), + HTTP: &tfHTTPMonitorFieldsV0{ + URL: types.StringValue(""), + SslVerificationMode: types.StringValue(""), + MaxRedirects: types.Int64Value(0), + Mode: types.StringValue(""), + ProxyURL: types.StringValue(""), + ProxyHeader: jsontypes.NewNormalizedValue(`{"header1":"value1"}`), + Username: types.StringValue("test"), + Password: types.StringValue("password"), + Check: jsontypes.NewNormalizedValue(`{"check1":"value1"}`), + Response: jsontypes.NewNormalizedValue(`{"response1":"value1"}`), + }, + }, + }, + { + name: "TCP monitor", + state: tfModelV0{ + TCP: &tfTCPMonitorFieldsV0{ + CheckSend: types.StringValue("hello"), + CheckReceive: types.StringValue("world"), + }, + }, + input: kbapi.SyntheticsMonitor{ + Type: kbapi.Tcp, + }, + expected: tfModelV0{ + ID: types.StringValue("/"), + Name: types.StringValue(""), + SpaceID: types.StringValue(""), + Schedule: types.Int64Value(0), + APMServiceName: types.StringValue(""), + TimeoutSeconds: types.Int64Value(0), + TCP: &tfTCPMonitorFieldsV0{ + Host: types.StringValue(""), + SslVerificationMode: types.StringValue(""), + CheckSend: types.StringValue("hello"), + CheckReceive: types.StringValue("world"), + ProxyURL: types.StringValue(""), + }, + }, + }, } - actual, err := state.toModelV0(input) - assert.NoError(t, err) - assert.NotNil(t, actual) + for _, tt := range testcases { + t.Run(tt.name, func(t *testing.T) { + actual, err := tt.state.toModelV0(&tt.input) + assert.NoError(t, err) + assert.NotNil(t, actual) + assert.Equal(t, &tt.expected, actual) + }) + } } diff --git a/templates/resources/kibana_synthetics_monitor.md.tmpl b/templates/resources/kibana_synthetics_monitor.md.tmpl index 75cabfdf8..8700aa561 100644 --- a/templates/resources/kibana_synthetics_monitor.md.tmpl +++ b/templates/resources/kibana_synthetics_monitor.md.tmpl @@ -15,11 +15,7 @@ See [API docs](https://www.elastic.co/guide/en/kibana/current/add-monitor-api.ht * `http` * `tcp` -**NOTE:** Not all monitor fields are supported due-to API limitation. -Full field support could be implemented after this [kibana issue](https://github.com/elastic/kibana/issues/189906) is resolved. - **NOTE:** Due-to nature of partial update API, reset values to defaults is not supported. - In case you would like to reset an optional monitor value, please set it explicitly or delete and create new monitor. @@ -33,4 +29,9 @@ In case you would like to reset an optional monitor value, please set it explici Import is supported using the following syntax: -{{ codefile "shell" "examples/resources/elasticstack_kibana_synthetics_monitor/import.sh" }} \ No newline at end of file +{{ codefile "shell" "examples/resources/elasticstack_kibana_synthetics_monitor/import.sh" }} + +**NOTE:** Not all monitor fields are supported during the import due-to API limitation. +Full field support could be implemented after this [kibana issue](https://github.com/elastic/kibana/issues/189906) is resolved. + +Currently not supported fields during the import: `params`, `retest_on_failure`, `http.proxy_header`, `http.username`, `http.password`, `http.check`, `http.response`, `tcp.check_send`, `tcp.check_receive` From e7d056846100da3234b38be2b4946dab94238440 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 16 Aug 2024 22:49:54 +0200 Subject: [PATCH 63/72] cr comments - tests --- internal/kibana/synthetics/acc_test.go | 130 +++++++++++++++---------- 1 file changed, 79 insertions(+), 51 deletions(-) diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index 817d533e6..055468a4f 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -1,6 +1,8 @@ package synthetics_test import ( + "fmt" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "testing" "github.com/elastic/terraform-provider-elasticstack/internal/acctest" @@ -14,40 +16,10 @@ var ( ) const ( - httpMonitorId = "elasticstack_kibana_synthetics_monitor.http-monitor" - tcpMonitorId = "elasticstack_kibana_synthetics_monitor.tcp-monitor" - - providerConfig = ` -provider "elasticstack" { - elasticsearch {} - kibana {} - fleet{} -} -` - - privateLocationConfig = ` - -resource "elasticstack_fleet_agent_policy" "test" { - name = "TestMonitorResource Agent Policy - test" - namespace = "testacc" - description = "TestMonitorResource Agent Policy" - monitor_logs = true - monitor_metrics = true - skip_destroy = false -} - -resource "elasticstack_kibana_synthetics_private_location" "test" { - label = "TestMonitorResource-label" - space_id = "testacc" - agent_policy_id = elasticstack_fleet_agent_policy.test.policy_id -} - -` - httpMonitorConfig = ` -resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { - name = "TestHttpMonitorResource" +resource "elasticstack_kibana_synthetics_monitor" "%s" { + name = "TestHttpMonitorResource - %s" space_id = "testacc" schedule = 5 private_locations = [elasticstack_kibana_synthetics_private_location.test.label] @@ -77,8 +49,8 @@ resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { ` httpMonitorUpdated = ` -resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { - name = "TestHttpMonitorResource Updated" +resource "elasticstack_kibana_synthetics_monitor" "%s" { + name = "TestHttpMonitorResource Updated - %s" space_id = "testacc" schedule = 10 private_locations = [elasticstack_kibana_synthetics_private_location.test.label] @@ -114,7 +86,7 @@ resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { "headers": { "Content-Type": "application/x-www-form-urlencoded", }, - "body": "name=first&email=someemail%40someemailprovider.com", + "body": "name=first&email=someemail@someemailprovider.com", }, "response": { "status": [200, 201, 301], @@ -138,8 +110,8 @@ resource "elasticstack_kibana_synthetics_monitor" "http-monitor" { tcpMonitorConfig = ` -resource "elasticstack_kibana_synthetics_monitor" "tcp-monitor" { - name = "TestTcpMonitorResource" +resource "elasticstack_kibana_synthetics_monitor" "%s" { + name = "TestTcpMonitorResource - %s" space_id = "default" schedule = 5 private_locations = [elasticstack_kibana_synthetics_private_location.test.label] @@ -166,8 +138,8 @@ resource "elasticstack_kibana_synthetics_monitor" "tcp-monitor" { ` tcpMonitorUpdated = ` -resource "elasticstack_kibana_synthetics_monitor" "tcp-monitor" { - name = "TestTcpMonitorResource Updated" +resource "elasticstack_kibana_synthetics_monitor" "%s" { + name = "TestTcpMonitorResource Updated - %s" space_id = "default" schedule = 10 private_locations = [elasticstack_kibana_synthetics_private_location.test.label] @@ -197,7 +169,13 @@ resource "elasticstack_kibana_synthetics_monitor" "tcp-monitor" { ` ) -func TestSyntheticMonitorResource(t *testing.T) { +func TestSyntheticMonitorHTTPResource(t *testing.T) { + + name := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) + id := "http-monitor" + httpMonitorId, config := testHttpMonitorConfig(id, httpMonitorConfig, name) + _, configUpdated := testHttpMonitorConfig(id, httpMonitorUpdated, name) + resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ProtoV6ProviderFactories: acctest.Providers, @@ -205,10 +183,10 @@ func TestSyntheticMonitorResource(t *testing.T) { // Create and Read http monitor { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), - Config: providerConfig + privateLocationConfig + httpMonitorConfig, + Config: config, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet(httpMonitorId, "id"), - resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource"), + resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource - "+name), resource.TestCheckResourceAttr(httpMonitorId, "space_id", "testacc"), resource.TestCheckResourceAttr(httpMonitorId, "schedule", "5"), resource.TestCheckResourceAttr(httpMonitorId, "private_locations.#", "1"), @@ -240,16 +218,16 @@ func TestSyntheticMonitorResource(t *testing.T) { ResourceName: httpMonitorId, ImportState: true, ImportStateVerify: true, - Config: providerConfig + privateLocationConfig + httpMonitorConfig, + Config: config, }, // Update and Read testing http monitor { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), ResourceName: httpMonitorId, - Config: providerConfig + privateLocationConfig + httpMonitorUpdated, + Config: configUpdated, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet(httpMonitorId, "id"), - resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource Updated"), + resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource Updated - "+name), resource.TestCheckResourceAttr(httpMonitorId, "space_id", "testacc"), resource.TestCheckResourceAttr(httpMonitorId, "schedule", "10"), resource.TestCheckResourceAttr(httpMonitorId, "private_locations.#", "1"), @@ -277,19 +255,36 @@ func TestSyntheticMonitorResource(t *testing.T) { resource.TestCheckResourceAttr(httpMonitorId, "http.proxy_header", `{"header-name":"header-value-updated"}`), resource.TestCheckResourceAttr(httpMonitorId, "http.username", "testupdated"), resource.TestCheckResourceAttr(httpMonitorId, "http.password", "testpassword-updated"), - resource.TestCheckResourceAttr(httpMonitorId, "http.check", `{"request":{"body":"name=first\u0026email=someemail%40someemailprovider.com","headers":{"Content-Type":"application/x-www-form-urlencoded"},"method":"POST"},"response":{"body":{"positive":["foo","bar"]},"status":[200,201,301]}}`), + resource.TestCheckResourceAttr(httpMonitorId, "http.check", `{"request":{"body":"name=first\u0026email=someemail@someemailprovider.com","headers":{"Content-Type":"application/x-www-form-urlencoded"},"method":"POST"},"response":{"body":{"positive":["foo","bar"]},"status":[200,201,301]}}`), resource.TestCheckResourceAttr(httpMonitorId, "http.response", `{"include_body":"never","include_body_max_bytes":"1024"}`), resource.TestCheckResourceAttr(httpMonitorId, "params", `{"param-name":"param-value-updated"}`), resource.TestCheckResourceAttr(httpMonitorId, "retest_on_failure", "false"), ), }, + // Delete testing automatically occurs in TestCase + }, + }) +} + +func TestSyntheticMonitorTCPResource(t *testing.T) { + + name := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) + id := "tcp-monitor" + tcpMonitorId, config := testHttpMonitorConfig(id, tcpMonitorConfig, name) + _, configUpdated := testHttpMonitorConfig(id, tcpMonitorUpdated, name) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + // Create and Read tcp monitor { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), - Config: providerConfig + privateLocationConfig + tcpMonitorConfig, + Config: config, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet(tcpMonitorId, "id"), - resource.TestCheckResourceAttr(tcpMonitorId, "name", "TestTcpMonitorResource"), + resource.TestCheckResourceAttr(tcpMonitorId, "name", "TestTcpMonitorResource - "+name), resource.TestCheckResourceAttr(tcpMonitorId, "space_id", "default"), resource.TestCheckResourceAttr(tcpMonitorId, "schedule", "5"), resource.TestCheckResourceAttr(tcpMonitorId, "private_locations.#", "1"), @@ -318,16 +313,16 @@ func TestSyntheticMonitorResource(t *testing.T) { ResourceName: tcpMonitorId, ImportState: true, ImportStateVerify: true, - Config: providerConfig + privateLocationConfig + tcpMonitorConfig, + Config: config, }, // Update and Read tcp monitor { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), ResourceName: tcpMonitorId, - Config: providerConfig + privateLocationConfig + tcpMonitorUpdated, + Config: configUpdated, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrSet(tcpMonitorId, "id"), - resource.TestCheckResourceAttr(tcpMonitorId, "name", "TestTcpMonitorResource Updated"), + resource.TestCheckResourceAttr(tcpMonitorId, "name", "TestTcpMonitorResource Updated - "+name), resource.TestCheckResourceAttr(tcpMonitorId, "space_id", "default"), resource.TestCheckResourceAttr(tcpMonitorId, "schedule", "10"), resource.TestCheckResourceAttr(tcpMonitorId, "private_locations.#", "1"), @@ -354,6 +349,39 @@ func TestSyntheticMonitorResource(t *testing.T) { ), }, // Delete testing automatically occurs in TestCase + }, }) } + +func testHttpMonitorConfig(id, cfg, name string) (string, string) { + + resourceId := "elasticstack_kibana_synthetics_monitor." + id + + provider := fmt.Sprintf(` +provider "elasticstack" { + elasticsearch {} + kibana {} + fleet{} +} + +resource "elasticstack_fleet_agent_policy" "test" { + name = "TestMonitorResource Agent Policy - %s" + namespace = "testacc" + description = "TestMonitorResource Agent Policy" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} + +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "TestMonitorResource-label-%s" + space_id = "testacc" + agent_policy_id = elasticstack_fleet_agent_policy.test.policy_id +} +`, name, name) + + config := fmt.Sprintf(cfg, id, name) + + return resourceId, provider + config +} From e478e05ecb16b0217b5329a7a54690ccb4efda8f Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 16 Aug 2024 22:51:53 +0200 Subject: [PATCH 64/72] docs - add spaceId --- docs/resources/kibana_synthetics_monitor.md | 2 +- docs/resources/kibana_synthetics_private_location.md | 2 +- .../resources/elasticstack_kibana_synthetics_monitor/import.sh | 2 +- .../elasticstack_kibana_synthetics_private_location/import.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/resources/kibana_synthetics_monitor.md b/docs/resources/kibana_synthetics_monitor.md index fee878dfc..37ef5de61 100644 --- a/docs/resources/kibana_synthetics_monitor.md +++ b/docs/resources/kibana_synthetics_monitor.md @@ -151,7 +151,7 @@ Optional: Import is supported using the following syntax: ```shell -terraform import elasticstack_kibana_synthetics_monitor.my_monitor +terraform import elasticstack_kibana_synthetics_monitor.my_monitor / ``` **NOTE:** Not all monitor fields are supported during the import due-to API limitation. diff --git a/docs/resources/kibana_synthetics_private_location.md b/docs/resources/kibana_synthetics_private_location.md index 3ae5272b6..527554a61 100644 --- a/docs/resources/kibana_synthetics_private_location.md +++ b/docs/resources/kibana_synthetics_private_location.md @@ -72,5 +72,5 @@ Required: Import is supported using the following syntax: ```shell -terraform import elasticstack_kibana_synthetics_private_location.my_location +terraform import elasticstack_kibana_synthetics_private_location.my_location / ``` \ No newline at end of file diff --git a/examples/resources/elasticstack_kibana_synthetics_monitor/import.sh b/examples/resources/elasticstack_kibana_synthetics_monitor/import.sh index 0ac6ac75d..c66f88e8f 100644 --- a/examples/resources/elasticstack_kibana_synthetics_monitor/import.sh +++ b/examples/resources/elasticstack_kibana_synthetics_monitor/import.sh @@ -1 +1 @@ -terraform import elasticstack_kibana_synthetics_monitor.my_monitor +terraform import elasticstack_kibana_synthetics_monitor.my_monitor / diff --git a/examples/resources/elasticstack_kibana_synthetics_private_location/import.sh b/examples/resources/elasticstack_kibana_synthetics_private_location/import.sh index 628bacff4..2a16897dc 100644 --- a/examples/resources/elasticstack_kibana_synthetics_private_location/import.sh +++ b/examples/resources/elasticstack_kibana_synthetics_private_location/import.sh @@ -1 +1 @@ -terraform import elasticstack_kibana_synthetics_private_location.my_location +terraform import elasticstack_kibana_synthetics_private_location.my_location / From 0888164bb0bed62756a68c11d6f2c73c541f4bd4 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Sat, 17 Aug 2024 14:10:43 +0200 Subject: [PATCH 65/72] add 8.15.0 for synthetics install call conditions --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 81c3ebbbe..088f8d73b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -140,7 +140,7 @@ jobs: - id: force-install-synthetics name: Force install synthetics - if: matrix.version == '8.14.3' + if: matrix.version == '8.14.3' || matrix.version == '8.15.0' run: |- for i in {1..5}; do curl -s -H "Authorization: ApiKey ${{ steps.get-api-key.outputs.apikey }}" --header "Content-Type: application/json" --header "kbn-xsrf: true" --request POST --data '{ "force": true }' http://localhost:5601/api/fleet/epm/packages/synthetics/1.2.2 && break || sleep 15; done From f40d508cd0ced770285650738d2e4029eaafef29 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 19 Aug 2024 07:40:38 +0200 Subject: [PATCH 66/72] change log --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7e7c8071..192adf39c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ ## [Unreleased] +- - Add support for Kibana synthetics http and tcp monitors ([#699](https://github.com/elastic/terraform-provider-elasticstack/pull/699)) ## [0.11.5] - 2024-08-12 From dcb8391ff3587ed64a13b7062e0e657d9e121864 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 19 Aug 2024 07:46:55 +0200 Subject: [PATCH 67/72] handle private location id w/o namespace --- .../synthetics/private_location/read.go | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/internal/kibana/synthetics/private_location/read.go b/internal/kibana/synthetics/private_location/read.go index 15bd87662..0676069f9 100644 --- a/internal/kibana/synthetics/private_location/read.go +++ b/internal/kibana/synthetics/private_location/read.go @@ -7,6 +7,7 @@ import ( "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "github.com/hashicorp/terraform-plugin-framework/resource" + "strings" ) func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { @@ -23,14 +24,19 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo return } - compositeId, dg := synthetics.GetCompositeId(state.ID.ValueString()) - response.Diagnostics.Append(dg...) - if response.Diagnostics.HasError() { - return - } + resourceId := state.ID.ValueString() + namespace := state.SpaceID.ValueString() + if strings.Contains(resourceId, "/") { + compositeId, dg := synthetics.GetCompositeId(state.ID.ValueString()) + response.Diagnostics.Append(dg...) + if response.Diagnostics.HasError() { + return + } - namespace := compositeId.ClusterId - result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Get(ctx, compositeId.ResourceId, namespace) + resourceId = compositeId.ResourceId + namespace = compositeId.ClusterId + } + result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Get(ctx, resourceId, namespace) if err != nil { var apiError *kbapi.APIError if errors.As(err, &apiError) && apiError.Code == 404 { @@ -38,7 +44,7 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo return } - response.Diagnostics.AddError(fmt.Sprintf("Failed to get private location `%s`, namespace %s", compositeId, namespace), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("Failed to get private location `%s`, namespace %s", resourceId, namespace), err.Error()) return } From ec96f68815ea1a955b428ae3e33a7b397f228c7d Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 19 Aug 2024 07:49:52 +0200 Subject: [PATCH 68/72] use lable, as before --- internal/kibana/synthetics/private_location/read.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/kibana/synthetics/private_location/read.go b/internal/kibana/synthetics/private_location/read.go index 0676069f9..e8eb61391 100644 --- a/internal/kibana/synthetics/private_location/read.go +++ b/internal/kibana/synthetics/private_location/read.go @@ -35,7 +35,10 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo resourceId = compositeId.ResourceId namespace = compositeId.ClusterId + } else { + resourceId = state.Label.ValueString() } + result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Get(ctx, resourceId, namespace) if err != nil { var apiError *kbapi.APIError From e16ba84829d60678a0638759df2f37292d65cba5 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 19 Aug 2024 07:51:51 +0200 Subject: [PATCH 69/72] formatting --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 192adf39c..618190a0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ ## [Unreleased] -- - Add support for Kibana synthetics http and tcp monitors ([#699](https://github.com/elastic/terraform-provider-elasticstack/pull/699)) +- Add support for Kibana synthetics http and tcp monitors ([#699](https://github.com/elastic/terraform-provider-elasticstack/pull/699)) ## [0.11.5] - 2024-08-12 From 254d9e1f932b919de6add32d28fe3016d6e348de Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 19 Aug 2024 10:41:36 +0200 Subject: [PATCH 70/72] update private location delete to support both id formats --- .../kibana/synthetics/private_location/delete.go | 15 +++++++++++---- .../kibana/synthetics/private_location/read.go | 16 +++++++--------- .../kibana/synthetics/private_location/schema.go | 10 ++++++++++ 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/internal/kibana/synthetics/private_location/delete.go b/internal/kibana/synthetics/private_location/delete.go index 69bbe280a..d5639f5c8 100644 --- a/internal/kibana/synthetics/private_location/delete.go +++ b/internal/kibana/synthetics/private_location/delete.go @@ -21,17 +21,24 @@ func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, r return } - monitorId, dg := synthetics.GetCompositeId(plan.ID.ValueString()) + resourceId := plan.ID.ValueString() + namespace := plan.SpaceID.ValueString() + + compositeId, dg := readCompositeIdOrConfigId(resourceId) response.Diagnostics.Append(dg...) if response.Diagnostics.HasError() { return } - namespace := plan.SpaceID.ValueString() - err := kibanaClient.KibanaSynthetics.PrivateLocation.Delete(ctx, monitorId.ResourceId, namespace) + if compositeId != nil { + resourceId = compositeId.ResourceId + namespace = compositeId.ClusterId + } + + err := kibanaClient.KibanaSynthetics.PrivateLocation.Delete(ctx, resourceId, namespace) if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", monitorId, namespace), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", resourceId, namespace), err.Error()) return } diff --git a/internal/kibana/synthetics/private_location/read.go b/internal/kibana/synthetics/private_location/read.go index e8eb61391..9d4273b02 100644 --- a/internal/kibana/synthetics/private_location/read.go +++ b/internal/kibana/synthetics/private_location/read.go @@ -7,7 +7,6 @@ import ( "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "github.com/hashicorp/terraform-plugin-framework/resource" - "strings" ) func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { @@ -26,17 +25,16 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo resourceId := state.ID.ValueString() namespace := state.SpaceID.ValueString() - if strings.Contains(resourceId, "/") { - compositeId, dg := synthetics.GetCompositeId(state.ID.ValueString()) - response.Diagnostics.Append(dg...) - if response.Diagnostics.HasError() { - return - } + compositeId, dg := readCompositeIdOrConfigId(resourceId) + response.Diagnostics.Append(dg...) + if response.Diagnostics.HasError() { + return + } + + if compositeId != nil { resourceId = compositeId.ResourceId namespace = compositeId.ClusterId - } else { - resourceId = state.Label.ValueString() } result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Get(ctx, resourceId, namespace) diff --git a/internal/kibana/synthetics/private_location/schema.go b/internal/kibana/synthetics/private_location/schema.go index 4ad0b6a38..5ae2cdfb4 100644 --- a/internal/kibana/synthetics/private_location/schema.go +++ b/internal/kibana/synthetics/private_location/schema.go @@ -4,11 +4,13 @@ import ( "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/elastic/terraform-provider-elasticstack/internal/clients" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" + "strings" ) type tfModelV0 struct { @@ -86,6 +88,14 @@ func (m *tfModelV0) toPrivateLocationConfig() kbapi.PrivateLocationConfig { } } +func readCompositeIdOrConfigId(id string) (*clients.CompositeId, diag.Diagnostics) { + if strings.Contains(id, "/") { + compositeId, diagnostics := synthetics.GetCompositeId(id) + return compositeId, diagnostics + } + return nil, diag.Diagnostics{} +} + func toModelV0(pLoc kbapi.PrivateLocation) tfModelV0 { resourceID := clients.CompositeId{ From 5ccf10334d262cb38e53982ba9061afc8716ee8a Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 19 Aug 2024 10:45:04 +0200 Subject: [PATCH 71/72] naming ... --- internal/kibana/synthetics/private_location/delete.go | 2 +- internal/kibana/synthetics/private_location/read.go | 2 +- internal/kibana/synthetics/private_location/schema.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/kibana/synthetics/private_location/delete.go b/internal/kibana/synthetics/private_location/delete.go index d5639f5c8..34cf9a097 100644 --- a/internal/kibana/synthetics/private_location/delete.go +++ b/internal/kibana/synthetics/private_location/delete.go @@ -24,7 +24,7 @@ func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, r resourceId := plan.ID.ValueString() namespace := plan.SpaceID.ValueString() - compositeId, dg := readCompositeIdOrConfigId(resourceId) + compositeId, dg := tryReadCompositeId(resourceId) response.Diagnostics.Append(dg...) if response.Diagnostics.HasError() { return diff --git a/internal/kibana/synthetics/private_location/read.go b/internal/kibana/synthetics/private_location/read.go index 9d4273b02..dcd004d2d 100644 --- a/internal/kibana/synthetics/private_location/read.go +++ b/internal/kibana/synthetics/private_location/read.go @@ -26,7 +26,7 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo resourceId := state.ID.ValueString() namespace := state.SpaceID.ValueString() - compositeId, dg := readCompositeIdOrConfigId(resourceId) + compositeId, dg := tryReadCompositeId(resourceId) response.Diagnostics.Append(dg...) if response.Diagnostics.HasError() { return diff --git a/internal/kibana/synthetics/private_location/schema.go b/internal/kibana/synthetics/private_location/schema.go index 5ae2cdfb4..6a78942c5 100644 --- a/internal/kibana/synthetics/private_location/schema.go +++ b/internal/kibana/synthetics/private_location/schema.go @@ -88,7 +88,7 @@ func (m *tfModelV0) toPrivateLocationConfig() kbapi.PrivateLocationConfig { } } -func readCompositeIdOrConfigId(id string) (*clients.CompositeId, diag.Diagnostics) { +func tryReadCompositeId(id string) (*clients.CompositeId, diag.Diagnostics) { if strings.Contains(id, "/") { compositeId, diagnostics := synthetics.GetCompositeId(id) return compositeId, diagnostics From 98038d02d6e3b08dc28aa615197b1ea8c5481a44 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 19 Aug 2024 11:03:26 +0200 Subject: [PATCH 72/72] split synthetics monitor resources in tests between http and tcp to preven deletion of same private location across tests. --- internal/kibana/synthetics/acc_test.go | 30 ++++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/internal/kibana/synthetics/acc_test.go b/internal/kibana/synthetics/acc_test.go index 055468a4f..264ba04c4 100644 --- a/internal/kibana/synthetics/acc_test.go +++ b/internal/kibana/synthetics/acc_test.go @@ -22,7 +22,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" { name = "TestHttpMonitorResource - %s" space_id = "testacc" schedule = 5 - private_locations = [elasticstack_kibana_synthetics_private_location.test.label] + private_locations = [elasticstack_kibana_synthetics_private_location.%s.label] enabled = true tags = ["a", "b"] alert = { @@ -53,7 +53,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" { name = "TestHttpMonitorResource Updated - %s" space_id = "testacc" schedule = 10 - private_locations = [elasticstack_kibana_synthetics_private_location.test.label] + private_locations = [elasticstack_kibana_synthetics_private_location.%s.label] enabled = false tags = ["c", "d", "e"] alert = { @@ -114,7 +114,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" { name = "TestTcpMonitorResource - %s" space_id = "default" schedule = 5 - private_locations = [elasticstack_kibana_synthetics_private_location.test.label] + private_locations = [elasticstack_kibana_synthetics_private_location.%s.label] enabled = true tags = ["a", "b"] alert = { @@ -142,7 +142,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" { name = "TestTcpMonitorResource Updated - %s" space_id = "default" schedule = 10 - private_locations = [elasticstack_kibana_synthetics_private_location.test.label] + private_locations = [elasticstack_kibana_synthetics_private_location.%s.label] enabled = false tags = ["c", "d", "e"] alert = { @@ -173,8 +173,8 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) { name := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) id := "http-monitor" - httpMonitorId, config := testHttpMonitorConfig(id, httpMonitorConfig, name) - _, configUpdated := testHttpMonitorConfig(id, httpMonitorUpdated, name) + httpMonitorId, config := testMonitorConfig(id, httpMonitorConfig, name) + _, configUpdated := testMonitorConfig(id, httpMonitorUpdated, name) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -270,8 +270,8 @@ func TestSyntheticMonitorTCPResource(t *testing.T) { name := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) id := "tcp-monitor" - tcpMonitorId, config := testHttpMonitorConfig(id, tcpMonitorConfig, name) - _, configUpdated := testHttpMonitorConfig(id, tcpMonitorUpdated, name) + tcpMonitorId, config := testMonitorConfig(id, tcpMonitorConfig, name) + _, configUpdated := testMonitorConfig(id, tcpMonitorUpdated, name) resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -354,9 +354,11 @@ func TestSyntheticMonitorTCPResource(t *testing.T) { }) } -func testHttpMonitorConfig(id, cfg, name string) (string, string) { +func testMonitorConfig(id, cfg, name string) (string, string) { resourceId := "elasticstack_kibana_synthetics_monitor." + id + privateLocationId := "pl-" + id + agentPolicyId := "apl-" + id provider := fmt.Sprintf(` provider "elasticstack" { @@ -365,7 +367,7 @@ provider "elasticstack" { fleet{} } -resource "elasticstack_fleet_agent_policy" "test" { +resource "elasticstack_fleet_agent_policy" "%s" { name = "TestMonitorResource Agent Policy - %s" namespace = "testacc" description = "TestMonitorResource Agent Policy" @@ -374,14 +376,14 @@ resource "elasticstack_fleet_agent_policy" "test" { skip_destroy = false } -resource "elasticstack_kibana_synthetics_private_location" "test" { +resource "elasticstack_kibana_synthetics_private_location" "%s" { label = "TestMonitorResource-label-%s" space_id = "testacc" - agent_policy_id = elasticstack_fleet_agent_policy.test.policy_id + agent_policy_id = elasticstack_fleet_agent_policy.%s.policy_id } -`, name, name) +`, agentPolicyId, name, privateLocationId, name, agentPolicyId) - config := fmt.Sprintf(cfg, id, name) + config := fmt.Sprintf(cfg, id, name, privateLocationId) return resourceId, provider + config }