From 1d23c217d82961d6dbe61b889999a05a8beb9a43 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 2 Aug 2024 15:02:40 +0200 Subject: [PATCH 01/15] feat(synthetics): support for private location resource --- internal/kibana/data_view/create.go | 2 +- .../kibana/synthetics/monitor_http/schema.go | 73 +++++++ .../synthetics/private_location/create.go | 44 ++++ .../synthetics/private_location/delete.go | 37 ++++ .../synthetics/private_location/read.go | 45 ++++ .../synthetics/private_location/schema.go | 152 +++++++++++++ internal/kibana/synthetics/schema.go | 206 ++++++++++++++++++ .../kbapi/api.kibana_synthetics.go | 78 ++++--- .../kbapi/api.kibana_synthetics_test.go | 4 + 9 files changed, 607 insertions(+), 34 deletions(-) create mode 100644 internal/kibana/synthetics/monitor_http/schema.go create mode 100644 internal/kibana/synthetics/private_location/create.go create mode 100644 internal/kibana/synthetics/private_location/delete.go create mode 100644 internal/kibana/synthetics/private_location/read.go create mode 100644 internal/kibana/synthetics/private_location/schema.go create mode 100644 internal/kibana/synthetics/schema.go diff --git a/internal/kibana/data_view/create.go b/internal/kibana/data_view/create.go index 8c99f3be99..c3b781aebd 100644 --- a/internal/kibana/data_view/create.go +++ b/internal/kibana/data_view/create.go @@ -16,7 +16,7 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r dataviewClient, err := r.client.GetDataViewsClient() if err != nil { - response.Diagnostics.AddError("unable to get data view client", err.Error()) + response.Diagnostics.AddError("unable to get kibana client", err.Error()) return } diff --git a/internal/kibana/synthetics/monitor_http/schema.go b/internal/kibana/synthetics/monitor_http/schema.go new file mode 100644 index 0000000000..d5b506375f --- /dev/null +++ b/internal/kibana/synthetics/monitor_http/schema.go @@ -0,0 +1,73 @@ +package monitor_http + +import ( + "context" + "github.com/elastic/terraform-provider-elasticstack/internal/clients" + "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" +) + +//TODO: +// Ensure provider defined types fully satisfy framework interfaces + +type Resource struct { + client *clients.ApiClient +} + +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": synthetics.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": synthetics.JsonObjectSchema(), + "proxy_url": schema.StringAttribute{ + Optional: true, + Description: "", + }, + "response": synthetics.JsonObjectSchema(), + "check": synthetics.JsonObjectSchema(), + }, + } +} + +func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = getSchema() +} + +func getSchema() schema.Schema { + return schema.Schema{} +} diff --git a/internal/kibana/synthetics/private_location/create.go b/internal/kibana/synthetics/private_location/create.go new file mode 100644 index 0000000000..1e90aeca6d --- /dev/null +++ b/internal/kibana/synthetics/private_location/create.go @@ -0,0 +1,44 @@ +package private_location + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + + if !r.resourceReady(&response.Diagnostics) { + return + } + + kibanaClient, err := r.client.GetKibanaClient() + if err != nil { + response.Diagnostics.AddError("unable to get kibana client", err.Error()) + return + } + + var plan tfModelV0 + diags := request.Plan.Get(ctx, &plan) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + input := plan.toPrivateLocationConfig() + + namespace := plan.SpaceID.ValueString() + result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Create(input, namespace) + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("Failed to create private location `%s`, namespace %s", input.Label, namespace), err.Error()) + return + } + + plan = toModelV0(namespace, result.PrivateLocationConfig) + + diags = response.State.Set(ctx, plan) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } +} diff --git a/internal/kibana/synthetics/private_location/delete.go b/internal/kibana/synthetics/private_location/delete.go new file mode 100644 index 0000000000..00cdb39a71 --- /dev/null +++ b/internal/kibana/synthetics/private_location/delete.go @@ -0,0 +1,37 @@ +package private_location + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) { + + if !r.resourceReady(&response.Diagnostics) { + return + } + + kibanaClient, err := r.client.GetKibanaClient() + if err != nil { + response.Diagnostics.AddError("unable to get kibana client", err.Error()) + return + } + + var plan tfModelV0 + diags := request.State.Get(ctx, &plan) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + label := plan.Label.ValueString() + namespace := plan.SpaceID.ValueString() + err = kibanaClient.KibanaSynthetics.PrivateLocation.Delete(label, namespace) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", label, namespace), err.Error()) + return + } + +} diff --git a/internal/kibana/synthetics/private_location/read.go b/internal/kibana/synthetics/private_location/read.go new file mode 100644 index 0000000000..1850222c6a --- /dev/null +++ b/internal/kibana/synthetics/private_location/read.go @@ -0,0 +1,45 @@ +package private_location + +import ( + "context" + "fmt" + "github.com/hashicorp/terraform-plugin-framework/resource" +) + +func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + + // TODO: dry + if !r.resourceReady(&response.Diagnostics) { + return + } + + kibanaClient, err := r.client.GetKibanaClient() + if err != nil { + response.Diagnostics.AddError("unable to get kibana client", err.Error()) + return + } + + var state tfModelV0 + diags := request.State.Get(ctx, &state) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } + + label := state.Label.ValueString() + namespace := state.SpaceID.ValueString() + result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Get(label, namespace) + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("Failed to get private location `%s`, namespace %s", label, namespace), err.Error()) + return + } + + state = toModelV0(namespace, result.PrivateLocationConfig) + + // Set refreshed state + diags = response.State.Set(ctx, &state) + response.Diagnostics.Append(diags...) + if response.Diagnostics.HasError() { + return + } +} diff --git a/internal/kibana/synthetics/private_location/schema.go b/internal/kibana/synthetics/private_location/schema.go new file mode 100644 index 0000000000..0ff1043d25 --- /dev/null +++ b/internal/kibana/synthetics/private_location/schema.go @@ -0,0 +1,152 @@ +package private_location + +import ( + "context" + "github.com/disaster37/go-kibana-rest/v8/kbapi" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-log/tflog" + + "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" + "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" +) + +const resourceName = synthetics.MetadataPrefix + "private_location" + +// Ensure provider defined types fully satisfy framework interfaces +var _ resource.Resource = &Resource{} +var _ resource.ResourceWithConfigure = &Resource{} +var _ resource.ResourceWithImportState = &Resource{} + +type Resource struct { + client *clients.ApiClient +} + +type tfModelV0 struct { + Label types.String `tfsdk:"label"` + SpaceID types.String `tfsdk:"space_id"` + AgentPolicyId types.String `tfsdk:"agent_policy_id"` + Tags []types.String `tfsdk:"tags"` //> string + Geo synthetics.TFGeoConfigV0 `tfsdk:"geo"` +} + +func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = privateLocationSchema() +} + +func (r *Resource) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("label"), 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) Update(ctx context.Context, _ resource.UpdateRequest, response *resource.UpdateResponse) { + tflog.Warn(ctx, "Update isn't supported for elasticstack_"+resourceName) + response.Diagnostics.AddError( + "synthetics private location update not supported", + "Synthetics private location could only be replaced. Please, note, that only unused locations could be deleted.", + ) +} + +func privateLocationSchema() schema.Schema { + return schema.Schema{ + MarkdownDescription: "Synthetics private location config, see https://www.elastic.co/guide/en/kibana/current/create-private-location-api.html for more details", + Attributes: map[string]schema.Attribute{ + "space_id": schema.StringAttribute{ + MarkdownDescription: "An identifier for the space. If space_id is not provided, the default space is used.", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + stringplanmodifier.RequiresReplace(), + }, + }, + "label": schema.StringAttribute{ + Optional: false, + Computed: true, + MarkdownDescription: "A label for the private location, used as unique identifier", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + stringplanmodifier.RequiresReplace(), + }, + }, + "agent_policy_id": schema.StringAttribute{ + Optional: false, + Computed: true, + MarkdownDescription: "The ID of the agent policy associated with the private location. To create a private location for synthetics monitor you need to create an agent policy in fleet and use its agentPolicyId", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + stringplanmodifier.RequiresReplace(), + }, + }, + "tags": schema.ListAttribute{ + ElementType: types.StringType, + Optional: true, + Computed: true, + MarkdownDescription: "An array of tags to categorize the private location.", + PlanModifiers: []planmodifier.List{ + listplanmodifier.UseStateForUnknown(), + listplanmodifier.RequiresReplace(), + }, + }, + "geo": synthetics.GeoConfigSchema(), + }, + } +} + +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) toPrivateLocationConfig() kbapi.PrivateLocationConfig { + 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.String(), + Tags: tags, + Geo: &geoConfig, + } + + return pLoc +} + +func toModelV0(namespace string, pLoc kbapi.PrivateLocationConfig) tfModelV0 { + var tags []types.String + for _, tag := range pLoc.Tags { + tags = append(tags, types.StringValue(tag)) + } + return tfModelV0{ + Label: types.StringValue(pLoc.Label), + SpaceID: types.StringValue(namespace), + AgentPolicyId: types.StringValue(pLoc.AgentPolicyId), + Tags: tags, + Geo: synthetics.FromSyntheticGeoConfig(*pLoc.Geo), + } +} diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go new file mode 100644 index 0000000000..d4876caf68 --- /dev/null +++ b/internal/kibana/synthetics/schema.go @@ -0,0 +1,206 @@ +package synthetics + +import ( + "github.com/disaster37/go-kibana-rest/v8/kbapi" + "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" +) + +const ( + MetadataPrefix = "_kibana_synthetics_" +) + +func MonitorIDSchema() schema.Attribute { + return schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Generated identifier for the monitor", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + } +} + +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", + } +} + +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.Attribute { + //TODO: add MonitorIDSchema + return schema.SingleNestedAttribute{ + Optional: false, + Description: "", + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Optional: false, + Description: "", + }, + "space_id": schema.StringAttribute{ + Description: "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: "", + }, + }, + } +} + +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 GeoConfigSchema() schema.Attribute { + return schema.SingleNestedAttribute{ + Optional: true, + Description: "Geographic coordinates (WGS84) for the location", + Attributes: map[string]schema.Attribute{ + "lat": schema.Float64Attribute{ + Optional: false, + MarkdownDescription: "", + }, + "lon": schema.Float64Attribute{ + Optional: false, + MarkdownDescription: "", + }, + }, + } +} + +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: "", + }, + }, + } +} + +type TFGeoConfigV0 struct { + Lat types.Float64 `tfsdk:"lat"` + Lon types.Float64 `tfsdk:"lon"` +} + +func (m *TFGeoConfigV0) ToSyntheticGeoConfig() kbapi.SyntheticGeoConfig { + return kbapi.SyntheticGeoConfig{ + Lat: m.Lat.ValueFloat64(), + Lon: m.Lon.ValueFloat64(), + } +} + +func FromSyntheticGeoConfig(v kbapi.SyntheticGeoConfig) TFGeoConfigV0 { + return TFGeoConfigV0{ + Lat: types.Float64Value(v.Lat), + Lon: types.Float64Value(v.Lon), + } +} diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go index 6548130689..4041473c66 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go @@ -12,23 +12,7 @@ const ( basePathKibanaSynthetics = "/api/synthetics" privateLocationsSuffix = "/private_locations" monitorsSuffix = "/monitors" -) - -type MonitorID string -type MonitorType string -type MonitorLocation string -type MonitorSchedule int -type HttpMonitorMode string - -type KibanaError struct { - Code int `json:"statusCode,omitempty"` - Error string `json:"error,omitempty"` - Message string `json:"message,omitempty"` -} - -type JsonObject map[string]interface{} -const ( Http MonitorType = "http" Tcp MonitorType = "tcp" Icmp MonitorType = "icmp" @@ -61,6 +45,20 @@ const ( ModeAny = "any" ) +type KibanaError struct { + Code int `json:"statusCode,omitempty"` + Error string `json:"error,omitempty"` + Message string `json:"message,omitempty"` +} + +type MonitorID string +type MonitorType string +type MonitorLocation string +type MonitorSchedule int +type HttpMonitorMode string + +type JsonObject map[string]interface{} + type KibanaSyntheticsMonitorAPI struct { Add KibanaSyntheticsMonitorAdd Delete KibanaSyntheticsMonitorDelete @@ -130,6 +128,23 @@ type MonitorLocationConfig struct { IsServiceManaged bool `json:"isServiceManaged"` } +type PrivateLocationConfig struct { + Label string `json:"label"` + AgentPolicyId string `json:"agentPolicyId"` + Tags []string `json:"tags,omitempty"` + Geo *SyntheticGeoConfig `json:"geo,omitempty"` +} + +type PrivateLocation struct { + Id string `json:"id"` + PrivateLocationConfig +} + +type MonitorDeleteStatus struct { + Id MonitorID `json:"id"` + Deleted bool `json:"deleted"` +} + type SyntheticsMonitor struct { Name string `json:"name"` Type MonitorType `json:"type"` @@ -142,9 +157,12 @@ type SyntheticsMonitor struct { 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"` ResponseIncludeBody string `json:"response.include_body"` @@ -159,24 +177,18 @@ 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 PrivateLocationConfig struct { - Label string `json:"label"` - AgentPolicyId string `json:"agentPolicyId"` - Tags []string `json:"tags,omitempty"` - Geo *SyntheticGeoConfig `json:"geo,omitempty"` -} - -type PrivateLocation struct { - Id string `json:"id"` - PrivateLocationConfig -} - -type MonitorDeleteStatus struct { - Id MonitorID `json:"id"` - Deleted bool `json:"deleted"` } type KibanaSyntheticsMonitorAdd func(config SyntheticsMonitorConfig, fields HTTPMonitorFields, 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 524d583647..b9151845b7 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go @@ -119,6 +119,7 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() { Namespace: space, Params: map[string]interface{}{ "param1": "some-params", + "my_url": "http://localhost:8080", }, RetestOnFailure: f, }, @@ -179,6 +180,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 get, err := syntheticsAPI.Monitor.Get(monitor.Id, space) assert.NoError(s.T(), err) @@ -190,6 +192,8 @@ 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 get, err = syntheticsAPI.Monitor.Get(monitor.ConfigId, space) assert.NoError(s.T(), err) From 1c0bf92bb6ada9d33fc3b15a244e2442af724aa1 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 2 Aug 2024 16:10:02 +0200 Subject: [PATCH 02/15] add open TODOs --- internal/kibana/data_view/create.go | 2 +- internal/kibana/synthetics/private_location/schema.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/kibana/data_view/create.go b/internal/kibana/data_view/create.go index c3b781aebd..8c99f3be99 100644 --- a/internal/kibana/data_view/create.go +++ b/internal/kibana/data_view/create.go @@ -16,7 +16,7 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r dataviewClient, err := r.client.GetDataViewsClient() if err != nil { - response.Diagnostics.AddError("unable to get kibana client", err.Error()) + response.Diagnostics.AddError("unable to get data view client", err.Error()) return } diff --git a/internal/kibana/synthetics/private_location/schema.go b/internal/kibana/synthetics/private_location/schema.go index 0ff1043d25..58da219666 100644 --- a/internal/kibana/synthetics/private_location/schema.go +++ b/internal/kibana/synthetics/private_location/schema.go @@ -19,6 +19,8 @@ import ( const resourceName = synthetics.MetadataPrefix + "private_location" +// TODO: tests + // Ensure provider defined types fully satisfy framework interfaces var _ resource.Resource = &Resource{} var _ resource.ResourceWithConfigure = &Resource{} @@ -120,6 +122,7 @@ func (r *Resource) resourceReady(dg *diag.Diagnostics) bool { return true } +// TODO: tests func (m *tfModelV0) toPrivateLocationConfig() kbapi.PrivateLocationConfig { geoConfig := m.Geo.ToSyntheticGeoConfig() From 87f59eb791dede1067771d8ca0d6727c35223e22 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Fri, 2 Aug 2024 19:39:54 +0200 Subject: [PATCH 03/15] wip: inital test with `terraform-plugin-testing`. Hit the error: `flag redefined: sweep` --- go.mod | 20 ++-- go.sum | 50 +++++----- .../synthetics/private_location/acc_test.go | 94 +++++++++++++++++++ internal/kibana/synthetics/schema.go | 2 + provider/plugin_framework.go | 2 + 5 files changed, 135 insertions(+), 33 deletions(-) create mode 100644 internal/kibana/synthetics/private_location/acc_test.go diff --git a/go.mod b/go.mod index 702cdbf9f5..396ec8bccb 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-mux v0.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 + github.com/hashicorp/terraform-plugin-testing v1.9.0 github.com/mitchellh/mapstructure v1.5.0 github.com/oapi-codegen/runtime v1.1.1 github.com/stretchr/testify v1.9.0 @@ -38,12 +39,12 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect - github.com/hashicorp/hc-install v0.6.4 // indirect - github.com/hashicorp/hcl/v2 v2.20.1 // indirect + github.com/hashicorp/hc-install v0.7.0 // indirect + github.com/hashicorp/hcl/v2 v2.21.0 // indirect 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 @@ -65,12 +66,13 @@ require ( github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.14.4 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/mod v0.16.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.17.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.63.2 // indirect diff --git a/go.sum b/go.sum index bd0668fc8c..280353381b 100644 --- a/go.sum +++ b/go.sum @@ -65,8 +65,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= @@ -76,10 +76,10 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hc-install v0.6.4 h1:QLqlM56/+SIIGvGcfFiwMY3z5WGXT066suo/v9Km8e0= -github.com/hashicorp/hc-install v0.6.4/go.mod h1:05LWLy8TD842OtgcfBbOT0WMoInBMUSHjmDx10zuBIA= -github.com/hashicorp/hcl/v2 v2.20.1 h1:M6hgdyz7HYt1UN9e61j+qKJBqR3orTWbI1HKBJEdxtc= -github.com/hashicorp/hcl/v2 v2.20.1/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4= +github.com/hashicorp/hc-install v0.7.0 h1:Uu9edVqjKQxxuD28mR5TikkKDd/p55S8vzPC1659aBk= +github.com/hashicorp/hc-install v0.7.0/go.mod h1:ELmmzZlGnEcqoUMKUuykHaPCIR1sYLYX+KSggWSKZuA= +github.com/hashicorp/hcl/v2 v2.21.0 h1:lve4q/o/2rqwYOgUg3y3V2YPyD1/zkCLGjIV74Jit14= +github.com/hashicorp/hcl/v2 v2.21.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ= @@ -98,6 +98,8 @@ github.com/hashicorp/terraform-plugin-mux v0.16.0 h1:RCzXHGDYwUwwqfYYWJKBFaS3fQs github.com/hashicorp/terraform-plugin-mux v0.16.0/go.mod h1:PF79mAsPc8CpusXPfEVa4X8PtkB+ngWoiUClMrNZlYo= github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0 h1:kJiWGx2kiQVo97Y5IOGR4EMcZ8DtMswHhUuFibsCQQE= github.com/hashicorp/terraform-plugin-sdk/v2 v2.34.0/go.mod h1:sl/UoabMc37HA6ICVMmGO+/0wofkVIRxf+BMb/dnoIg= +github.com/hashicorp/terraform-plugin-testing v1.9.0 h1:xOsQRqqlHKXpFq6etTxih3ubdK3HVDtfE1IY7Rpd37o= +github.com/hashicorp/terraform-plugin-testing v1.9.0/go.mod h1:fhhVx/8+XNJZTD5o3b4stfZ6+q7z9+lIWigIYdT6/44= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= @@ -178,29 +180,29 @@ github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8= github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= -github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b h1:FosyBZYxY34Wul7O/MSKey3txpPYyCqVO5ZyceuQJEI= -github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= +github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -215,25 +217,25 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/internal/kibana/synthetics/private_location/acc_test.go b/internal/kibana/synthetics/private_location/acc_test.go new file mode 100644 index 0000000000..4587a6c1f3 --- /dev/null +++ b/internal/kibana/synthetics/private_location/acc_test.go @@ -0,0 +1,94 @@ +package private_location_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-testing/helper/resource" +) + +const ( + providerConfig = ` +provider "elasticstack" { + elasticsearch {} + kibana {} +} +` +) + +var ( + minKibanaVersion = version.Must(version.NewVersion("8.12.0")) +) + +func TestPrivateLocationResource(t *testing.T) { + resourceId := "elasticstack_kibana_synthetics_private_location.test" + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + // Create and Read testing + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + Config: providerConfig + ` +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "test label" + space_id = "testacc" + agent_policy_id = "agent-policy-id-test" + tags = ["a", "b"] + geo { + lat = 42.42 + lon = -42.42 + } +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceId, "label", "test label"), + resource.TestCheckResourceAttr(resourceId, "space_id", "testacc"), + resource.TestCheckResourceAttr(resourceId, "agent_policy_id", "agent-policy-id-test"), + resource.TestCheckResourceAttr(resourceId, "tags.#", "2"), + resource.TestCheckResourceAttr(resourceId, "tags.0", "a"), + resource.TestCheckResourceAttr(resourceId, "tags.1", "b"), + resource.TestCheckResourceAttr(resourceId, "geo.lat", "42.42"), + resource.TestCheckResourceAttr(resourceId, "geo.lon", "-42.42"), + ), + }, + // ImportState testing + { + //TODO: Config + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + ResourceName: resourceId, + ImportState: true, + ImportStateVerify: true, + }, + // Update and Read testing + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + Config: providerConfig + ` +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "test label 2" + space_id = "default" + agent_policy_id = "agent-policy-id-test-2" + tags = ["c", "d", "e"] + geo { + lat = -33.21 + lon = 42.42 + } +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceId, "label", "test label 2"), + resource.TestCheckResourceAttr(resourceId, "space_id", "default"), + resource.TestCheckResourceAttr(resourceId, "agent_policy_id", "agent-policy-id-test-2"), + resource.TestCheckResourceAttr(resourceId, "tags.#", "3"), + resource.TestCheckResourceAttr(resourceId, "tags.0", "c"), + resource.TestCheckResourceAttr(resourceId, "tags.1", "d"), + resource.TestCheckResourceAttr(resourceId, "tags.3", "e"), + resource.TestCheckResourceAttr(resourceId, "geo.lat", "-33.21"), + resource.TestCheckResourceAttr(resourceId, "geo.lon", "42.42"), + ), + }, + // Delete testing automatically occurs in TestCase + }, + }) +} diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index d4876caf68..b23fb72017 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -11,6 +11,8 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) +//TODO: monitor support is from 8.14.0 + const ( MetadataPrefix = "_kibana_synthetics_" ) diff --git a/provider/plugin_framework.go b/provider/plugin_framework.go index a5a190c6a2..b33c9fbefd 100644 --- a/provider/plugin_framework.go +++ b/provider/plugin_framework.go @@ -7,6 +7,7 @@ import ( "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/private_location" "github.com/elastic/terraform-provider-elasticstack/internal/schema" "github.com/hashicorp/terraform-plugin-framework/datasource" fwprovider "github.com/hashicorp/terraform-plugin-framework/provider" @@ -66,5 +67,6 @@ func (p *Provider) Resources(ctx context.Context) []func() resource.Resource { return []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{} }, } } From aea004796090660874e338774ef34b386ae29034 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Sun, 4 Aug 2024 13:53:09 +0200 Subject: [PATCH 04/15] try to use terraform-plugin-sdk and add synthetics stack versions to matrix check --- .github/workflows/test.yml | 3 +++ .../synthetics/private_location/acc_test.go | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a165ceb63e..9a30b20d4d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -103,6 +103,9 @@ jobs: - '8.12.2' - '8.13.4' - '8.14.0' + - '8.14.1' + - '8.14.2' + - '8.14.3' steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 diff --git a/internal/kibana/synthetics/private_location/acc_test.go b/internal/kibana/synthetics/private_location/acc_test.go index 4587a6c1f3..0b79711231 100644 --- a/internal/kibana/synthetics/private_location/acc_test.go +++ b/internal/kibana/synthetics/private_location/acc_test.go @@ -6,7 +6,7 @@ import ( "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-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) const ( @@ -25,6 +25,7 @@ var ( func TestPrivateLocationResource(t *testing.T) { resourceId := "elasticstack_kibana_synthetics_private_location.test" resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, ProtoV6ProviderFactories: acctest.Providers, Steps: []resource.TestStep{ // Create and Read testing @@ -55,11 +56,22 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { }, // ImportState testing { - //TODO: Config SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), ResourceName: resourceId, ImportState: true, ImportStateVerify: true, + Config: providerConfig + ` +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "test label import" + space_id = "testacc" + agent_policy_id = "agent-policy-id-test-import" + tags = ["a-import", "b-import"] + geo { + lat = 33 + lon = -55 + } +} +`, }, // Update and Read testing { From 96aced10e6be0a79b46c2c1c90e796bcbf077156 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Sun, 4 Aug 2024 17:11:29 +0200 Subject: [PATCH 05/15] make lint happy --- internal/kibana/synthetics/monitor_http/schema.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/kibana/synthetics/monitor_http/schema.go b/internal/kibana/synthetics/monitor_http/schema.go index d5b506375f..71b782f1b9 100644 --- a/internal/kibana/synthetics/monitor_http/schema.go +++ b/internal/kibana/synthetics/monitor_http/schema.go @@ -2,7 +2,6 @@ package monitor_http import ( "context" - "github.com/elastic/terraform-provider-elasticstack/internal/clients" "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -12,7 +11,7 @@ import ( // Ensure provider defined types fully satisfy framework interfaces type Resource struct { - client *clients.ApiClient + //client *clients.ApiClient } func HttpMonitorModeSchema() schema.Attribute { From f8bbe26f4db0b1729d9a27d53d51160233f73798 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Sun, 4 Aug 2024 17:29:36 +0200 Subject: [PATCH 06/15] fixing AttributeName("geo").AttributeName("lat"): must have Required, Optional, or Computed set.. --- internal/kibana/synthetics/schema.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index b23fb72017..df33851513 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -156,11 +156,13 @@ func GeoConfigSchema() schema.Attribute { Attributes: map[string]schema.Attribute{ "lat": schema.Float64Attribute{ Optional: false, - MarkdownDescription: "", + Computed: true, + MarkdownDescription: "", //TODO add description }, "lon": schema.Float64Attribute{ Optional: false, - MarkdownDescription: "", + Computed: true, + MarkdownDescription: "", //TODO add description }, }, } From 26ecfbd914443a05136a9adf1298a01c8c1fac6b Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Sun, 4 Aug 2024 17:30:34 +0200 Subject: [PATCH 07/15] make docs-generate --- .../kibana_synthetics_private_location.md | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/resources/kibana_synthetics_private_location.md diff --git a/docs/resources/kibana_synthetics_private_location.md b/docs/resources/kibana_synthetics_private_location.md new file mode 100644 index 0000000000..e55f9138d5 --- /dev/null +++ b/docs/resources/kibana_synthetics_private_location.md @@ -0,0 +1,35 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "elasticstack_kibana_synthetics_private_location Resource - terraform-provider-elasticstack" +subcategory: "" +description: |- + Synthetics private location config, see https://www.elastic.co/guide/en/kibana/current/create-private-location-api.html for more details +--- + +# elasticstack_kibana_synthetics_private_location (Resource) + +Synthetics private location config, see https://www.elastic.co/guide/en/kibana/current/create-private-location-api.html for more details + + + + +## Schema + +### Optional + +- `geo` (Attributes) Geographic coordinates (WGS84) for the location (see [below for nested schema](#nestedatt--geo)) +- `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used. +- `tags` (List of String) An array of tags to categorize the private location. + +### Read-Only + +- `agent_policy_id` (String) The ID of the agent policy associated with the private location. To create a private location for synthetics monitor you need to create an agent policy in fleet and use its agentPolicyId +- `label` (String) A label for the private location, used as unique identifier + + +### Nested Schema for `geo` + +Read-Only: + +- `lat` (Number) +- `lon` (Number) From 3e52b8d44c970fbcd46471a604094f1c2c6c6fdc Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Sun, 4 Aug 2024 17:59:15 +0200 Subject: [PATCH 08/15] try to fix: Blocks of type "geo" are not expected here. Did you mean to define argument --- internal/kibana/synthetics/private_location/acc_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/kibana/synthetics/private_location/acc_test.go b/internal/kibana/synthetics/private_location/acc_test.go index 0b79711231..3100521578 100644 --- a/internal/kibana/synthetics/private_location/acc_test.go +++ b/internal/kibana/synthetics/private_location/acc_test.go @@ -37,7 +37,7 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { space_id = "testacc" agent_policy_id = "agent-policy-id-test" tags = ["a", "b"] - geo { + geo = { lat = 42.42 lon = -42.42 } @@ -66,7 +66,7 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { space_id = "testacc" agent_policy_id = "agent-policy-id-test-import" tags = ["a-import", "b-import"] - geo { + geo = { lat = 33 lon = -55 } @@ -82,7 +82,7 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { space_id = "default" agent_policy_id = "agent-policy-id-test-2" tags = ["c", "d", "e"] - geo { + geo = { lat = -33.21 lon = 42.42 } From 4d1d7fee133e1599c3463892a0ef473caeb80fca Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 5 Aug 2024 11:09:49 +0200 Subject: [PATCH 09/15] fixed private location schema --- Makefile | 4 +- .../synthetics/private_location/acc_test.go | 39 +++++++++++---- .../synthetics/private_location/create.go | 9 ++-- .../synthetics/private_location/delete.go | 9 ++-- .../synthetics/private_location/read.go | 7 ++- .../synthetics/private_location/schema.go | 50 ++++++++++++------- internal/kibana/synthetics/schema.go | 16 +++--- .../kbapi/api.kibana_synthetics.go | 3 +- libs/go-kibana-rest/kibana.go | 1 + 9 files changed, 95 insertions(+), 43 deletions(-) diff --git a/Makefile b/Makefile index c6cbb5a4a2..3235784d5a 100644 --- a/Makefile +++ b/Makefile @@ -58,8 +58,8 @@ testacc: ## Run acceptance tests test: ## Run unit tests go test -v $(TEST) $(TESTARGS) -timeout=5m -parallel=4 -# Retry command - first argumment is how many attempts are required, second argument is the command to run -# Backoff starts with 1 second and double with next itteration +# Retry command - first argument is how many attempts are required, second argument is the command to run +# Backoff starts with 1 second and double with next iteration retry = until [ $$(if [ -z "$$attempt" ]; then echo -n "0"; else echo -n "$$attempt"; fi) -ge $(1) ]; do \ backoff=$$(if [ -z "$$backoff" ]; then echo "1"; else echo "$$backoff"; fi); \ sleep $$backoff; \ diff --git a/internal/kibana/synthetics/private_location/acc_test.go b/internal/kibana/synthetics/private_location/acc_test.go index 3100521578..ae4372c43d 100644 --- a/internal/kibana/synthetics/private_location/acc_test.go +++ b/internal/kibana/synthetics/private_location/acc_test.go @@ -15,6 +15,25 @@ provider "elasticstack" { elasticsearch {} kibana {} } + +resource "elasticstack_fleet_agent_policy" "test_policy" { + name = "Private location Agent Policy" + namespace = "testacc" + description = "TestPrivateLocationResource Agent Policy" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} + +resource "elasticstack_fleet_agent_policy" "test_policy_default" { + name = "Default namespace Private location Agent Policy" + namespace = "default" + description = "TestPrivateLocationResource Agent Policy" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} + ` ) @@ -35,7 +54,7 @@ func TestPrivateLocationResource(t *testing.T) { resource "elasticstack_kibana_synthetics_private_location" "test" { label = "test label" space_id = "testacc" - agent_policy_id = "agent-policy-id-test" + agent_policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id tags = ["a", "b"] geo = { lat = 42.42 @@ -46,7 +65,7 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceId, "label", "test label"), resource.TestCheckResourceAttr(resourceId, "space_id", "testacc"), - resource.TestCheckResourceAttr(resourceId, "agent_policy_id", "agent-policy-id-test"), + resource.TestCheckResourceAttrSet(resourceId, "agent_policy_id"), resource.TestCheckResourceAttr(resourceId, "tags.#", "2"), resource.TestCheckResourceAttr(resourceId, "tags.0", "a"), resource.TestCheckResourceAttr(resourceId, "tags.1", "b"), @@ -62,13 +81,13 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { ImportStateVerify: true, Config: providerConfig + ` resource "elasticstack_kibana_synthetics_private_location" "test" { - label = "test label import" + label = "test label" space_id = "testacc" - agent_policy_id = "agent-policy-id-test-import" - tags = ["a-import", "b-import"] + agent_policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id + tags = ["a", "b"] geo = { - lat = 33 - lon = -55 + lat = 42.42 + lon = -42.42 } } `, @@ -80,7 +99,7 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { resource "elasticstack_kibana_synthetics_private_location" "test" { label = "test label 2" space_id = "default" - agent_policy_id = "agent-policy-id-test-2" + agent_policy_id = elasticstack_fleet_agent_policy.test_policy_default.policy_id tags = ["c", "d", "e"] geo = { lat = -33.21 @@ -91,11 +110,11 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceId, "label", "test label 2"), resource.TestCheckResourceAttr(resourceId, "space_id", "default"), - resource.TestCheckResourceAttr(resourceId, "agent_policy_id", "agent-policy-id-test-2"), + resource.TestCheckResourceAttrSet(resourceId, "agent_policy_id"), resource.TestCheckResourceAttr(resourceId, "tags.#", "3"), resource.TestCheckResourceAttr(resourceId, "tags.0", "c"), resource.TestCheckResourceAttr(resourceId, "tags.1", "d"), - resource.TestCheckResourceAttr(resourceId, "tags.3", "e"), + resource.TestCheckResourceAttr(resourceId, "tags.2", "e"), resource.TestCheckResourceAttr(resourceId, "geo.lat", "-33.21"), resource.TestCheckResourceAttr(resourceId, "geo.lon", "42.42"), ), diff --git a/internal/kibana/synthetics/private_location/create.go b/internal/kibana/synthetics/private_location/create.go index 1e90aeca6d..989b7ad194 100644 --- a/internal/kibana/synthetics/private_location/create.go +++ b/internal/kibana/synthetics/private_location/create.go @@ -4,10 +4,13 @@ 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 private location") + if !r.resourceReady(&response.Diagnostics) { return } @@ -25,16 +28,16 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r return } - input := plan.toPrivateLocationConfig() + input := plan.toPrivateLocation() namespace := plan.SpaceID.ValueString() - result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Create(input, namespace) + result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Create(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 } - plan = toModelV0(namespace, result.PrivateLocationConfig) + plan = toModelV0(*result) diags = response.State.Set(ctx, plan) response.Diagnostics.Append(diags...) diff --git a/internal/kibana/synthetics/private_location/delete.go b/internal/kibana/synthetics/private_location/delete.go index 00cdb39a71..9386dbcdc0 100644 --- a/internal/kibana/synthetics/private_location/delete.go +++ b/internal/kibana/synthetics/private_location/delete.go @@ -4,10 +4,13 @@ import ( "context" "fmt" "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") + if !r.resourceReady(&response.Diagnostics) { return } @@ -25,12 +28,12 @@ func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, r return } - label := plan.Label.ValueString() + id := plan.ID.ValueString() namespace := plan.SpaceID.ValueString() - err = kibanaClient.KibanaSynthetics.PrivateLocation.Delete(label, namespace) + err = kibanaClient.KibanaSynthetics.PrivateLocation.Delete(id, namespace) if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", label, namespace), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("Failed to delete private location `%s`, namespace %s", id, namespace), err.Error()) return } diff --git a/internal/kibana/synthetics/private_location/read.go b/internal/kibana/synthetics/private_location/read.go index 1850222c6a..4db8faf603 100644 --- a/internal/kibana/synthetics/private_location/read.go +++ b/internal/kibana/synthetics/private_location/read.go @@ -4,10 +4,13 @@ import ( "context" "fmt" "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") + // TODO: dry if !r.resourceReady(&response.Diagnostics) { return @@ -19,6 +22,8 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo return } + //TODO: handle 404 error / remove resource + var state tfModelV0 diags := request.State.Get(ctx, &state) response.Diagnostics.Append(diags...) @@ -34,7 +39,7 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo return } - state = toModelV0(namespace, result.PrivateLocationConfig) + state = toModelV0(*result) // Set refreshed state diags = response.State.Set(ctx, &state) diff --git a/internal/kibana/synthetics/private_location/schema.go b/internal/kibana/synthetics/private_location/schema.go index 58da219666..2948d42f3d 100644 --- a/internal/kibana/synthetics/private_location/schema.go +++ b/internal/kibana/synthetics/private_location/schema.go @@ -31,11 +31,12 @@ type Resource struct { } type tfModelV0 struct { - Label types.String `tfsdk:"label"` - SpaceID types.String `tfsdk:"space_id"` - AgentPolicyId types.String `tfsdk:"agent_policy_id"` - Tags []types.String `tfsdk:"tags"` //> string - Geo synthetics.TFGeoConfigV0 `tfsdk:"geo"` + ID types.String `tfsdk:"id"` + Label types.String `tfsdk:"label"` + SpaceID types.String `tfsdk:"space_id"` + AgentPolicyId types.String `tfsdk:"agent_policy_id"` + Tags []types.String `tfsdk:"tags"` //> string + Geo *synthetics.TFGeoConfigV0 `tfsdk:"geo"` } func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { @@ -43,6 +44,7 @@ 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) } @@ -68,10 +70,17 @@ func privateLocationSchema() schema.Schema { return schema.Schema{ MarkdownDescription: "Synthetics private location config, see https://www.elastic.co/guide/en/kibana/current/create-private-location-api.html for more details", Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Generated id for the private location. For monitor setup please use private location label.", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + stringplanmodifier.RequiresReplace(), + }, + }, "space_id": schema.StringAttribute{ MarkdownDescription: "An identifier for the space. If space_id is not provided, the default space is used.", Optional: true, - Computed: true, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), stringplanmodifier.RequiresReplace(), @@ -79,7 +88,7 @@ func privateLocationSchema() schema.Schema { }, "label": schema.StringAttribute{ Optional: false, - Computed: true, + Required: true, MarkdownDescription: "A label for the private location, used as unique identifier", PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), @@ -88,7 +97,7 @@ func privateLocationSchema() schema.Schema { }, "agent_policy_id": schema.StringAttribute{ Optional: false, - Computed: true, + Required: true, MarkdownDescription: "The ID of the agent policy associated with the private location. To create a private location for synthetics monitor you need to create an agent policy in fleet and use its agentPolicyId", PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), @@ -98,7 +107,6 @@ func privateLocationSchema() schema.Schema { "tags": schema.ListAttribute{ ElementType: types.StringType, Optional: true, - Computed: true, MarkdownDescription: "An array of tags to categorize the private location.", PlanModifiers: []planmodifier.List{ listplanmodifier.UseStateForUnknown(), @@ -123,8 +131,11 @@ func (r *Resource) resourceReady(dg *diag.Diagnostics) bool { } // TODO: tests -func (m *tfModelV0) toPrivateLocationConfig() kbapi.PrivateLocationConfig { - geoConfig := m.Geo.ToSyntheticGeoConfig() +func (m *tfModelV0) toPrivateLocation() kbapi.PrivateLocation { + var geoConfig *kbapi.SyntheticGeoConfig + if m.Geo != nil { + geoConfig = m.Geo.ToSyntheticGeoConfig() + } var tags []string for _, tag := range m.Tags { @@ -132,24 +143,29 @@ func (m *tfModelV0) toPrivateLocationConfig() kbapi.PrivateLocationConfig { } pLoc := kbapi.PrivateLocationConfig{ Label: m.Label.ValueString(), - AgentPolicyId: m.AgentPolicyId.String(), + AgentPolicyId: m.AgentPolicyId.ValueString(), Tags: tags, - Geo: &geoConfig, + Geo: geoConfig, } - return pLoc + return kbapi.PrivateLocation{ + Id: m.ID.ValueString(), + Namespace: m.SpaceID.ValueString(), + PrivateLocationConfig: pLoc, + } } -func toModelV0(namespace string, pLoc kbapi.PrivateLocationConfig) tfModelV0 { +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(namespace), + SpaceID: types.StringValue(pLoc.Namespace), AgentPolicyId: types.StringValue(pLoc.AgentPolicyId), Tags: tags, - Geo: synthetics.FromSyntheticGeoConfig(*pLoc.Geo), + Geo: synthetics.FromSyntheticGeoConfig(pLoc.Geo), } } diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index df33851513..bc1cb15207 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -156,12 +156,12 @@ func GeoConfigSchema() schema.Attribute { Attributes: map[string]schema.Attribute{ "lat": schema.Float64Attribute{ Optional: false, - Computed: true, + Required: true, MarkdownDescription: "", //TODO add description }, "lon": schema.Float64Attribute{ Optional: false, - Computed: true, + Required: true, MarkdownDescription: "", //TODO add description }, }, @@ -195,15 +195,19 @@ type TFGeoConfigV0 struct { Lon types.Float64 `tfsdk:"lon"` } -func (m *TFGeoConfigV0) ToSyntheticGeoConfig() kbapi.SyntheticGeoConfig { - return kbapi.SyntheticGeoConfig{ +func (m *TFGeoConfigV0) ToSyntheticGeoConfig() *kbapi.SyntheticGeoConfig { + return &kbapi.SyntheticGeoConfig{ Lat: m.Lat.ValueFloat64(), Lon: m.Lon.ValueFloat64(), } } -func FromSyntheticGeoConfig(v kbapi.SyntheticGeoConfig) TFGeoConfigV0 { - return TFGeoConfigV0{ +// TODO: test +func FromSyntheticGeoConfig(v *kbapi.SyntheticGeoConfig) *TFGeoConfigV0 { + if v == nil { + return nil + } + return &TFGeoConfigV0{ Lat: types.Float64Value(v.Lat), Lon: types.Float64Value(v.Lon), } diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go index 4041473c66..ce10e1f848 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go @@ -136,7 +136,8 @@ type PrivateLocationConfig struct { } type PrivateLocation struct { - Id string `json:"id"` + Id string `json:"id"` + Namespace string `json:"namespace,omitempty"` PrivateLocationConfig } diff --git a/libs/go-kibana-rest/kibana.go b/libs/go-kibana-rest/kibana.go index 560820a4b7..bb8e629480 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") From 279ff743bdf91bffffc478f9f07058a39e8e3431 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 5 Aug 2024 11:14:48 +0200 Subject: [PATCH 10/15] make lint happy --- docs/resources/kibana_synthetics_private_location.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/resources/kibana_synthetics_private_location.md b/docs/resources/kibana_synthetics_private_location.md index e55f9138d5..7464d3c072 100644 --- a/docs/resources/kibana_synthetics_private_location.md +++ b/docs/resources/kibana_synthetics_private_location.md @@ -15,6 +15,11 @@ Synthetics private location config, see https://www.elastic.co/guide/en/kibana/c ## Schema +### Required + +- `agent_policy_id` (String) The ID of the agent policy associated with the private location. To create a private location for synthetics monitor you need to create an agent policy in fleet and use its agentPolicyId +- `label` (String) A label for the private location, used as unique identifier + ### Optional - `geo` (Attributes) Geographic coordinates (WGS84) for the location (see [below for nested schema](#nestedatt--geo)) @@ -23,13 +28,12 @@ Synthetics private location config, see https://www.elastic.co/guide/en/kibana/c ### Read-Only -- `agent_policy_id` (String) The ID of the agent policy associated with the private location. To create a private location for synthetics monitor you need to create an agent policy in fleet and use its agentPolicyId -- `label` (String) A label for the private location, used as unique identifier +- `id` (String) Generated id for the private location. For monitor setup please use private location label. ### Nested Schema for `geo` -Read-Only: +Required: - `lat` (Number) - `lon` (Number) From 2fbefced4d34d2d006b4e7cb29c9e4728ea323d9 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 5 Aug 2024 16:08:04 +0200 Subject: [PATCH 11/15] add schema tests --- internal/clients/api_client.go | 8 +- .../synthetics/private_location/acc_test.go | 108 +++++++++--------- .../synthetics/private_location/schema.go | 3 - .../private_location/schema_test.go | 78 +++++++++++++ internal/utils/http_log.go | 1 + libs/go-kibana-rest/kibana.go | 1 - provider/plugin_framework.go | 11 +- 7 files changed, 145 insertions(+), 65 deletions(-) create mode 100644 internal/kibana/synthetics/private_location/schema_test.go diff --git a/internal/clients/api_client.go b/internal/clients/api_client.go index e60e8d349d..f43eaec22d 100644 --- a/internal/clients/api_client.go +++ b/internal/clients/api_client.go @@ -391,8 +391,12 @@ func buildKibanaClient(cfg config.Client) (*kibana.Client, error) { if logging.IsDebugOrHigher() { // Don't use kib.Client.SetDebug() here as we re-use the http client within the OpenAPI generated clients - kibHttpClient := kib.Client.GetClient() - kibHttpClient.Transport = utils.NewDebugTransport("Kibana", kibHttpClient.Transport) + transport, err := kib.Client.Transport() + if err != nil { + return nil, err + } + var roundTripper http.RoundTripper = utils.NewDebugTransport("Kibana", transport) + kib.Client.SetTransport(roundTripper) } return kib, nil diff --git a/internal/kibana/synthetics/private_location/acc_test.go b/internal/kibana/synthetics/private_location/acc_test.go index ae4372c43d..d596c2c1e8 100644 --- a/internal/kibana/synthetics/private_location/acc_test.go +++ b/internal/kibana/synthetics/private_location/acc_test.go @@ -1,6 +1,7 @@ package private_location_test import ( + "fmt" "testing" "github.com/elastic/terraform-provider-elasticstack/internal/acctest" @@ -15,25 +16,6 @@ provider "elasticstack" { elasticsearch {} kibana {} } - -resource "elasticstack_fleet_agent_policy" "test_policy" { - name = "Private location Agent Policy" - namespace = "testacc" - description = "TestPrivateLocationResource Agent Policy" - monitor_logs = true - monitor_metrics = true - skip_destroy = false -} - -resource "elasticstack_fleet_agent_policy" "test_policy_default" { - name = "Default namespace Private location Agent Policy" - namespace = "default" - description = "TestPrivateLocationResource Agent Policy" - monitor_logs = true - monitor_metrics = true - skip_destroy = false -} - ` ) @@ -50,18 +32,7 @@ func TestPrivateLocationResource(t *testing.T) { // Create and Read testing { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), - Config: providerConfig + ` -resource "elasticstack_kibana_synthetics_private_location" "test" { - label = "test label" - space_id = "testacc" - agent_policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id - tags = ["a", "b"] - geo = { - lat = 42.42 - lon = -42.42 - } -} -`, + Config: testConfig(), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceId, "label", "test label"), resource.TestCheckResourceAttr(resourceId, "space_id", "testacc"), @@ -79,34 +50,12 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { ResourceName: resourceId, ImportState: true, ImportStateVerify: true, - Config: providerConfig + ` -resource "elasticstack_kibana_synthetics_private_location" "test" { - label = "test label" - space_id = "testacc" - agent_policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id - tags = ["a", "b"] - geo = { - lat = 42.42 - lon = -42.42 - } -} -`, + Config: testConfig(), }, // Update and Read testing { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), - Config: providerConfig + ` -resource "elasticstack_kibana_synthetics_private_location" "test" { - label = "test label 2" - space_id = "default" - agent_policy_id = elasticstack_fleet_agent_policy.test_policy_default.policy_id - tags = ["c", "d", "e"] - geo = { - lat = -33.21 - lon = 42.42 - } -} -`, + Config: updateConfig(), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr(resourceId, "label", "test label 2"), resource.TestCheckResourceAttr(resourceId, "space_id", "default"), @@ -116,10 +65,57 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { resource.TestCheckResourceAttr(resourceId, "tags.1", "d"), resource.TestCheckResourceAttr(resourceId, "tags.2", "e"), resource.TestCheckResourceAttr(resourceId, "geo.lat", "-33.21"), - resource.TestCheckResourceAttr(resourceId, "geo.lon", "42.42"), + resource.TestCheckResourceAttr(resourceId, "geo.lon", "-33.21"), ), }, // Delete testing automatically occurs in TestCase }, }) } + +func agentPolicyResource(namespace, name string) string { + return fmt.Sprintf(` +resource "elasticstack_fleet_agent_policy" "%s" { + name = "Private Location Agent Policy - %s" + namespace = "%s" + description = "TestPrivateLocationResource Agent Policy" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} +`, name, name, namespace) +} + +func testConfig() string { + namespace := "testacc" + return providerConfig + agentPolicyResource(namespace, "test_policy") + + fmt.Sprintf(` +resource "elasticstack_kibana_synthetics_private_location" "%s" { + label = "test label" + space_id = "%s" + agent_policy_id = %s + tags = ["a", "b"] + geo = { + lat = 42.42 + lon = -42.42 + } +} +`, "test", namespace, "elasticstack_fleet_agent_policy.test_policy.policy_id") +} + +func updateConfig() string { + namespace := "default" + return providerConfig + agentPolicyResource(namespace, "test_policy_default") + + fmt.Sprintf(` +resource "elasticstack_kibana_synthetics_private_location" "%s" { + label = "test label 2" + space_id = "%s" + agent_policy_id = %s + tags = ["c", "d", "e"] + geo = { + lat = -33.21 + lon = -33.21 + } +} +`, "test", namespace, "elasticstack_fleet_agent_policy.test_policy_default.policy_id") +} diff --git a/internal/kibana/synthetics/private_location/schema.go b/internal/kibana/synthetics/private_location/schema.go index 2948d42f3d..8458370a9c 100644 --- a/internal/kibana/synthetics/private_location/schema.go +++ b/internal/kibana/synthetics/private_location/schema.go @@ -19,8 +19,6 @@ import ( const resourceName = synthetics.MetadataPrefix + "private_location" -// TODO: tests - // Ensure provider defined types fully satisfy framework interfaces var _ resource.Resource = &Resource{} var _ resource.ResourceWithConfigure = &Resource{} @@ -130,7 +128,6 @@ func (r *Resource) resourceReady(dg *diag.Diagnostics) bool { return true } -// TODO: tests func (m *tfModelV0) toPrivateLocation() kbapi.PrivateLocation { var geoConfig *kbapi.SyntheticGeoConfig if m.Geo != nil { diff --git a/internal/kibana/synthetics/private_location/schema_test.go b/internal/kibana/synthetics/private_location/schema_test.go new file mode 100644 index 0000000000..e07628a368 --- /dev/null +++ b/internal/kibana/synthetics/private_location/schema_test.go @@ -0,0 +1,78 @@ +package private_location + +import ( + "testing" + + "github.com/disaster37/go-kibana-rest/v8/kbapi" + "github.com/stretchr/testify/assert" +) + +func Test_roundtrip(t *testing.T) { + tests := []struct { + name string + id string + ns string + plc kbapi.PrivateLocationConfig + }{ + { + name: "only required fields", + id: "id-1", + ns: "ns-1", + plc: kbapi.PrivateLocationConfig{ + Label: "label-1", + AgentPolicyId: "agent-policy-id-1", + }, + }, + { + name: "all fields", + id: "id-2", + ns: "ns-2", + plc: kbapi.PrivateLocationConfig{ + Label: "label-2", + AgentPolicyId: "agent-policy-id-2", + Tags: []string{"tag-1", "tag-2", "tag-3"}, + Geo: &kbapi.SyntheticGeoConfig{ + Lat: 43.2, + Lon: 23.1, + }, + }, + }, + { + name: "only tags", + id: "id-3", + ns: "ns-3", + plc: kbapi.PrivateLocationConfig{ + Label: "label-3", + AgentPolicyId: "agent-policy-id-3", + Tags: []string{"tag-1", "tag-2", "tag-3"}, + Geo: nil, + }, + }, + { + name: "only geo", + id: "id-4", + ns: "ns-4", + plc: kbapi.PrivateLocationConfig{ + Label: "label-4", + AgentPolicyId: "agent-policy-id-4", + Geo: &kbapi.SyntheticGeoConfig{ + Lat: 43.2, + Lon: 23.1, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + plc := tt.plc + input := kbapi.PrivateLocation{ + tt.id, + tt.ns, + plc, + } + modelV0 := toModelV0(input) + actual := modelV0.toPrivateLocation() + assert.Equal(t, input, actual) + }) + } +} diff --git a/internal/utils/http_log.go b/internal/utils/http_log.go index 512ab1da2e..7135a87697 100644 --- a/internal/utils/http_log.go +++ b/internal/utils/http_log.go @@ -33,6 +33,7 @@ func NewDebugTransport(name string, transport http.RoundTripper) *debugRoundTrip } func (d *debugRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { + ctx := r.Context() reqData, err := httputil.DumpRequestOut(r, true) if err == nil { diff --git a/libs/go-kibana-rest/kibana.go b/libs/go-kibana-rest/kibana.go index bb8e629480..560820a4b7 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/provider/plugin_framework.go b/provider/plugin_framework.go index b33c9fbefd..5a6b7f5f0b 100644 --- a/provider/plugin_framework.go +++ b/provider/plugin_framework.go @@ -15,6 +15,11 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource" ) +// Ensure the implementation satisfies the expected interfaces. +var ( + _ fwprovider.Provider = &Provider{} +) + type Provider struct { version string } @@ -42,14 +47,14 @@ func (p *Provider) Schema(ctx context.Context, req fwprovider.SchemaRequest, res } func (p *Provider) Configure(ctx context.Context, req fwprovider.ConfigureRequest, res *fwprovider.ConfigureResponse) { - var config config.ProviderConfiguration + var cfg config.ProviderConfiguration - res.Diagnostics.Append(req.Config.Get(ctx, &config)...) + res.Diagnostics.Append(req.Config.Get(ctx, &cfg)...) if res.Diagnostics.HasError() { return } - client, diags := clients.NewApiClientFromFramework(ctx, config, p.version) + client, diags := clients.NewApiClientFromFramework(ctx, cfg, p.version) res.Diagnostics.Append(diags...) if res.Diagnostics.HasError() { return From ffdef9067fda38573fd38ef96f4431b4d8c57e96 Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 5 Aug 2024 16:13:08 +0200 Subject: [PATCH 12/15] reduce scope to private location only --- .github/workflows/test.yml | 3 - .../kibana_synthetics_private_location.md | 4 +- .../kibana/synthetics/monitor_http/schema.go | 72 -------- .../synthetics/private_location/resource.go | 48 +++++ .../synthetics/private_location/schema.go | 44 ----- .../private_location/schema_test.go | 6 +- internal/kibana/synthetics/schema.go | 166 +----------------- .../kbapi/api.kibana_synthetics.go | 13 +- 8 files changed, 56 insertions(+), 300 deletions(-) delete mode 100644 internal/kibana/synthetics/monitor_http/schema.go create mode 100644 internal/kibana/synthetics/private_location/resource.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9a30b20d4d..da7f2fa215 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -102,9 +102,6 @@ jobs: - '8.11.4' - '8.12.2' - '8.13.4' - - '8.14.0' - - '8.14.1' - - '8.14.2' - '8.14.3' steps: - uses: actions/checkout@v4 diff --git a/docs/resources/kibana_synthetics_private_location.md b/docs/resources/kibana_synthetics_private_location.md index 7464d3c072..f1e67f7963 100644 --- a/docs/resources/kibana_synthetics_private_location.md +++ b/docs/resources/kibana_synthetics_private_location.md @@ -35,5 +35,5 @@ Synthetics private location config, see https://www.elastic.co/guide/en/kibana/c Required: -- `lat` (Number) -- `lon` (Number) +- `lat` (Number) The latitude of the location. +- `lon` (Number) The longitude of the location. diff --git a/internal/kibana/synthetics/monitor_http/schema.go b/internal/kibana/synthetics/monitor_http/schema.go deleted file mode 100644 index 71b782f1b9..0000000000 --- a/internal/kibana/synthetics/monitor_http/schema.go +++ /dev/null @@ -1,72 +0,0 @@ -package monitor_http - -import ( - "context" - "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" - "github.com/hashicorp/terraform-plugin-framework/resource" - "github.com/hashicorp/terraform-plugin-framework/resource/schema" -) - -//TODO: -// Ensure provider defined types fully satisfy framework interfaces - -type Resource struct { - //client *clients.ApiClient -} - -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": synthetics.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": synthetics.JsonObjectSchema(), - "proxy_url": schema.StringAttribute{ - Optional: true, - Description: "", - }, - "response": synthetics.JsonObjectSchema(), - "check": synthetics.JsonObjectSchema(), - }, - } -} - -func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = getSchema() -} - -func getSchema() schema.Schema { - return schema.Schema{} -} diff --git a/internal/kibana/synthetics/private_location/resource.go b/internal/kibana/synthetics/private_location/resource.go new file mode 100644 index 0000000000..8ea7936a6f --- /dev/null +++ b/internal/kibana/synthetics/private_location/resource.go @@ -0,0 +1,48 @@ +package private_location + +import ( + "context" + "github.com/elastic/terraform-provider-elasticstack/internal/clients" + "github.com/elastic/terraform-provider-elasticstack/internal/kibana/synthetics" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +const resourceName = synthetics.MetadataPrefix + "private_location" + +// Ensure provider defined types fully satisfy framework interfaces +var _ resource.Resource = &Resource{} +var _ resource.ResourceWithConfigure = &Resource{} +var _ resource.ResourceWithImportState = &Resource{} + +type Resource struct { + client *clients.ApiClient +} + +func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = privateLocationSchema() +} + +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) +} + +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) Update(ctx context.Context, _ resource.UpdateRequest, response *resource.UpdateResponse) { + tflog.Warn(ctx, "Update isn't supported for elasticstack_"+resourceName) + response.Diagnostics.AddError( + "synthetics private location update not supported", + "Synthetics private location could only be replaced. Please, note, that only unused locations could be deleted.", + ) +} diff --git a/internal/kibana/synthetics/private_location/schema.go b/internal/kibana/synthetics/private_location/schema.go index 8458370a9c..9b7316a5a5 100644 --- a/internal/kibana/synthetics/private_location/schema.go +++ b/internal/kibana/synthetics/private_location/schema.go @@ -1,15 +1,9 @@ package private_location import ( - "context" "github.com/disaster37/go-kibana-rest/v8/kbapi" - "github.com/hashicorp/terraform-plugin-framework/path" - "github.com/hashicorp/terraform-plugin-log/tflog" - - "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" "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" @@ -17,17 +11,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) -const resourceName = synthetics.MetadataPrefix + "private_location" - -// Ensure provider defined types fully satisfy framework interfaces -var _ resource.Resource = &Resource{} -var _ resource.ResourceWithConfigure = &Resource{} -var _ resource.ResourceWithImportState = &Resource{} - -type Resource struct { - client *clients.ApiClient -} - type tfModelV0 struct { ID types.String `tfsdk:"id"` Label types.String `tfsdk:"label"` @@ -37,33 +20,6 @@ type tfModelV0 struct { Geo *synthetics.TFGeoConfigV0 `tfsdk:"geo"` } -func (r *Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { - resp.Schema = privateLocationSchema() -} - -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) -} - -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) Update(ctx context.Context, _ resource.UpdateRequest, response *resource.UpdateResponse) { - tflog.Warn(ctx, "Update isn't supported for elasticstack_"+resourceName) - response.Diagnostics.AddError( - "synthetics private location update not supported", - "Synthetics private location could only be replaced. Please, note, that only unused locations could be deleted.", - ) -} - func privateLocationSchema() schema.Schema { return schema.Schema{ MarkdownDescription: "Synthetics private location config, see https://www.elastic.co/guide/en/kibana/current/create-private-location-api.html for more details", diff --git a/internal/kibana/synthetics/private_location/schema_test.go b/internal/kibana/synthetics/private_location/schema_test.go index e07628a368..c22c6a8469 100644 --- a/internal/kibana/synthetics/private_location/schema_test.go +++ b/internal/kibana/synthetics/private_location/schema_test.go @@ -66,9 +66,9 @@ func Test_roundtrip(t *testing.T) { t.Run(tt.name, func(t *testing.T) { plc := tt.plc input := kbapi.PrivateLocation{ - tt.id, - tt.ns, - plc, + Id: tt.id, + Namespace: tt.ns, + PrivateLocationConfig: plc, } modelV0 := toModelV0(input) actual := modelV0.toPrivateLocation() diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go index bc1cb15207..91f29aaf6b 100644 --- a/internal/kibana/synthetics/schema.go +++ b/internal/kibana/synthetics/schema.go @@ -2,153 +2,14 @@ package synthetics import ( "github.com/disaster37/go-kibana-rest/v8/kbapi" - "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 MonitorIDSchema() schema.Attribute { - return schema.StringAttribute{ - Computed: true, - MarkdownDescription: "Generated identifier for the monitor", - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - } -} - -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", - } -} - -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.Attribute { - //TODO: add MonitorIDSchema - return schema.SingleNestedAttribute{ - Optional: false, - Description: "", - Attributes: map[string]schema.Attribute{ - "name": schema.StringAttribute{ - Optional: false, - Description: "", - }, - "space_id": schema.StringAttribute{ - Description: "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: "", - }, - }, - } -} - -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 GeoConfigSchema() schema.Attribute { return schema.SingleNestedAttribute{ Optional: true, @@ -157,34 +18,12 @@ func GeoConfigSchema() schema.Attribute { "lat": schema.Float64Attribute{ Optional: false, Required: true, - MarkdownDescription: "", //TODO add description + MarkdownDescription: "The latitude of the location.", }, "lon": schema.Float64Attribute{ Optional: false, Required: true, - MarkdownDescription: "", //TODO add 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: "", + MarkdownDescription: "The longitude of the location.", }, }, } @@ -202,7 +41,6 @@ func (m *TFGeoConfigV0) ToSyntheticGeoConfig() *kbapi.SyntheticGeoConfig { } } -// TODO: test func FromSyntheticGeoConfig(v *kbapi.SyntheticGeoConfig) *TFGeoConfigV0 { if v == nil { return nil diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go index ce10e1f848..2897cdaa22 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go @@ -178,18 +178,7 @@ 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"` } type KibanaSyntheticsMonitorAdd func(config SyntheticsMonitorConfig, fields HTTPMonitorFields, namespace string) (*SyntheticsMonitor, error) From 8340a37651254f1f7287a364d5b8aca4bf443b6d Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 5 Aug 2024 17:34:35 +0200 Subject: [PATCH 13/15] handle 404 case --- .../synthetics/private_location/acc_test.go | 83 +++++++++---------- .../synthetics/private_location/create.go | 9 +- .../synthetics/private_location/delete.go | 11 +-- .../synthetics/private_location/read.go | 20 ++--- .../synthetics/private_location/resource.go | 15 ++++ .../kbapi/api.kibana_synthetics.go | 7 ++ .../kbapi/api.kibana_synthetics_test.go | 19 +++++ 7 files changed, 97 insertions(+), 67 deletions(-) diff --git a/internal/kibana/synthetics/private_location/acc_test.go b/internal/kibana/synthetics/private_location/acc_test.go index d596c2c1e8..3e82ba1db2 100644 --- a/internal/kibana/synthetics/private_location/acc_test.go +++ b/internal/kibana/synthetics/private_location/acc_test.go @@ -32,9 +32,20 @@ func TestPrivateLocationResource(t *testing.T) { // Create and Read testing { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), - Config: testConfig(), + Config: testConfig("testacc", "test_policy") + ` +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "pl-test-label" + space_id = "testacc" + agent_policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id + tags = ["a", "b"] + geo = { + lat = 42.42 + lon = -42.42 + } +} +`, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(resourceId, "label", "test label"), + resource.TestCheckResourceAttr(resourceId, "label", "pl-test-label"), resource.TestCheckResourceAttr(resourceId, "space_id", "testacc"), resource.TestCheckResourceAttrSet(resourceId, "agent_policy_id"), resource.TestCheckResourceAttr(resourceId, "tags.#", "2"), @@ -50,14 +61,36 @@ func TestPrivateLocationResource(t *testing.T) { ResourceName: resourceId, ImportState: true, ImportStateVerify: true, - Config: testConfig(), + Config: testConfig("testacc", "test_policy") + ` +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "pl-test-label" + space_id = "testacc" + agent_policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id + tags = ["a", "b"] + geo = { + lat = 42.42 + lon = -42.42 + } +} +`, }, // Update and Read testing { SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), - Config: updateConfig(), + Config: testConfig("default", "test_policy_default") + ` +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "pl-test-label-2" + space_id = "default" + agent_policy_id = elasticstack_fleet_agent_policy.test_policy_default.policy_id + tags = ["c", "d", "e"] + geo = { + lat = -33.21 + lon = -33.21 + } +} +`, Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(resourceId, "label", "test label 2"), + resource.TestCheckResourceAttr(resourceId, "label", "pl-test-label-2"), resource.TestCheckResourceAttr(resourceId, "space_id", "default"), resource.TestCheckResourceAttrSet(resourceId, "agent_policy_id"), resource.TestCheckResourceAttr(resourceId, "tags.#", "3"), @@ -73,8 +106,8 @@ func TestPrivateLocationResource(t *testing.T) { }) } -func agentPolicyResource(namespace, name string) string { - return fmt.Sprintf(` +func testConfig(namespace, agentPolicy string) string { + return providerConfig + fmt.Sprintf(` resource "elasticstack_fleet_agent_policy" "%s" { name = "Private Location Agent Policy - %s" namespace = "%s" @@ -83,39 +116,5 @@ resource "elasticstack_fleet_agent_policy" "%s" { monitor_metrics = true skip_destroy = false } -`, name, name, namespace) -} - -func testConfig() string { - namespace := "testacc" - return providerConfig + agentPolicyResource(namespace, "test_policy") + - fmt.Sprintf(` -resource "elasticstack_kibana_synthetics_private_location" "%s" { - label = "test label" - space_id = "%s" - agent_policy_id = %s - tags = ["a", "b"] - geo = { - lat = 42.42 - lon = -42.42 - } -} -`, "test", namespace, "elasticstack_fleet_agent_policy.test_policy.policy_id") -} - -func updateConfig() string { - namespace := "default" - return providerConfig + agentPolicyResource(namespace, "test_policy_default") + - fmt.Sprintf(` -resource "elasticstack_kibana_synthetics_private_location" "%s" { - label = "test label 2" - space_id = "%s" - agent_policy_id = %s - tags = ["c", "d", "e"] - geo = { - lat = -33.21 - lon = -33.21 - } -} -`, "test", namespace, "elasticstack_fleet_agent_policy.test_policy_default.policy_id") +`, agentPolicy, agentPolicy, namespace) } diff --git a/internal/kibana/synthetics/private_location/create.go b/internal/kibana/synthetics/private_location/create.go index 989b7ad194..9f0bf2aa3d 100644 --- a/internal/kibana/synthetics/private_location/create.go +++ b/internal/kibana/synthetics/private_location/create.go @@ -11,13 +11,8 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r tflog.Info(ctx, "Create private location") - if !r.resourceReady(&response.Diagnostics) { - return - } - - kibanaClient, err := r.client.GetKibanaClient() - if err != nil { - response.Diagnostics.AddError("unable to get kibana client", err.Error()) + kibanaClient := r.getKibanaClient(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 9386dbcdc0..1b7ecb5f1b 100644 --- a/internal/kibana/synthetics/private_location/delete.go +++ b/internal/kibana/synthetics/private_location/delete.go @@ -11,13 +11,8 @@ func (r *Resource) Delete(ctx context.Context, request resource.DeleteRequest, r tflog.Info(ctx, "Delete private location") - if !r.resourceReady(&response.Diagnostics) { - return - } - - kibanaClient, err := r.client.GetKibanaClient() - if err != nil { - response.Diagnostics.AddError("unable to get kibana client", err.Error()) + kibanaClient := r.getKibanaClient(response.Diagnostics) + if kibanaClient == nil { return } @@ -30,7 +25,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(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 4db8faf603..dedd7a8b74 100644 --- a/internal/kibana/synthetics/private_location/read.go +++ b/internal/kibana/synthetics/private_location/read.go @@ -2,7 +2,9 @@ package private_location import ( "context" + "errors" "fmt" + "github.com/disaster37/go-kibana-rest/v8/kbapi" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-log/tflog" ) @@ -11,19 +13,11 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo tflog.Info(ctx, "Read private location") - // TODO: dry - if !r.resourceReady(&response.Diagnostics) { + kibanaClient := r.getKibanaClient(response.Diagnostics) + if kibanaClient == nil { return } - kibanaClient, err := r.client.GetKibanaClient() - if err != nil { - response.Diagnostics.AddError("unable to get kibana client", err.Error()) - return - } - - //TODO: handle 404 error / remove resource - var state tfModelV0 diags := request.State.Get(ctx, &state) response.Diagnostics.Append(diags...) @@ -35,6 +29,12 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo namespace := state.SpaceID.ValueString() result, err := kibanaClient.KibanaSynthetics.PrivateLocation.Get(label, 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 private location `%s`, namespace %s", label, namespace), err.Error()) return } diff --git a/internal/kibana/synthetics/private_location/resource.go b/internal/kibana/synthetics/private_location/resource.go index 8ea7936a6f..0da381cdef 100644 --- a/internal/kibana/synthetics/private_location/resource.go +++ b/internal/kibana/synthetics/private_location/resource.go @@ -2,8 +2,10 @@ 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" @@ -46,3 +48,16 @@ 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/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go index 2897cdaa22..f141d8bed2 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go @@ -198,6 +198,13 @@ type KibanaSyntheticsPrivateLocationDelete func(id string, namespace string) err func newKibanaSyntheticsPrivateLocationGetFunc(c *resty.Client) KibanaSyntheticsPrivateLocationGet { return func(idOrLabel string, namespace string) (*PrivateLocation, error) { + if idOrLabel == "" { + return nil, APIError{ + Code: 404, + Message: "Private location id or label is empty", + } + } + path := basePathWithId(namespace, privateLocationsSuffix, idOrLabel) log.Debugf("URL to get private locations: %s", path) resp, err := c.R().Get(path) 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 b9151845b7..b3341653d8 100644 --- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go +++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go @@ -258,3 +258,22 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsPrivateLocationAPI() { }) } } + +func (s *KBAPITestSuite) TestKibanaSyntheticsPrivateLocationNotFound() { + for _, n := range namespaces { + testUuid := uuid.New().String() + space := n + pAPI := s.API.KibanaSynthetics.PrivateLocation + + ids := []string{"", "not-found", testUuid} + + for _, id := range ids { + s.Run(fmt.Sprintf("TestKibanaSyntheticsPrivateLocationNotFound - %s - %s", n, id), func() { + _, err := pAPI.Get(id, space) + assert.Error(s.T(), err) + assert.IsType(s.T(), APIError{}, err) + assert.Equal(s.T(), 404, err.(APIError).Code) + }) + } + } +} From 5742d9d9f9a748b67a65494eb13968de2e8e3eba Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Mon, 5 Aug 2024 17:42:34 +0200 Subject: [PATCH 14/15] test optional params --- .../synthetics/private_location/acc_test.go | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/internal/kibana/synthetics/private_location/acc_test.go b/internal/kibana/synthetics/private_location/acc_test.go index 3e82ba1db2..4e75af0ef9 100644 --- a/internal/kibana/synthetics/private_location/acc_test.go +++ b/internal/kibana/synthetics/private_location/acc_test.go @@ -101,6 +101,69 @@ resource "elasticstack_kibana_synthetics_private_location" "test" { resource.TestCheckResourceAttr(resourceId, "geo.lon", "-33.21"), ), }, + // Update and Read testing + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + Config: testConfig("default", "test_policy_default") + ` +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "pl-test-label-2" + space_id = "default" + agent_policy_id = elasticstack_fleet_agent_policy.test_policy_default.policy_id +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceId, "label", "pl-test-label-2"), + resource.TestCheckResourceAttr(resourceId, "space_id", "default"), + resource.TestCheckResourceAttrSet(resourceId, "agent_policy_id"), + resource.TestCheckNoResourceAttr(resourceId, "tags"), + resource.TestCheckNoResourceAttr(resourceId, "geo"), + ), + }, + // Update and Read testing + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + Config: testConfig("default", "test_policy_default") + ` +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "pl-test-label-2" + space_id = "default" + agent_policy_id = elasticstack_fleet_agent_policy.test_policy_default.policy_id + tags = ["c", "d", "e"] +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceId, "label", "pl-test-label-2"), + resource.TestCheckResourceAttr(resourceId, "space_id", "default"), + resource.TestCheckResourceAttrSet(resourceId, "agent_policy_id"), + resource.TestCheckResourceAttr(resourceId, "tags.#", "3"), + resource.TestCheckResourceAttr(resourceId, "tags.0", "c"), + resource.TestCheckResourceAttr(resourceId, "tags.1", "d"), + resource.TestCheckResourceAttr(resourceId, "tags.2", "e"), + resource.TestCheckNoResourceAttr(resourceId, "geo"), + ), + }, + // Update and Read testing + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion), + Config: testConfig("default", "test_policy_default") + ` +resource "elasticstack_kibana_synthetics_private_location" "test" { + label = "pl-test-label-2" + space_id = "default" + agent_policy_id = elasticstack_fleet_agent_policy.test_policy_default.policy_id + geo = { + lat = -33.21 + lon = -33.21 + } +} +`, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceId, "label", "pl-test-label-2"), + resource.TestCheckResourceAttr(resourceId, "space_id", "default"), + resource.TestCheckResourceAttrSet(resourceId, "agent_policy_id"), + resource.TestCheckNoResourceAttr(resourceId, "tags"), + resource.TestCheckResourceAttr(resourceId, "geo.lat", "-33.21"), + resource.TestCheckResourceAttr(resourceId, "geo.lon", "-33.21"), + ), + }, // Delete testing automatically occurs in TestCase }, }) From 4d048484c29f445e54bac0114587f0b0b33be62f Mon Sep 17 00:00:00 2001 From: Boris Ilyushonak Date: Tue, 6 Aug 2024 09:54:22 +0200 Subject: [PATCH 15/15] add documentation and updated read.me --- CHANGELOG.md | 1 + .../kibana_synthetics_private_location | 76 +++++++++++++++++++ .../kibana_synthetics_private_location.md | 35 +++++++++ .../import.sh | 1 + .../resource.tf | 24 ++++++ .../synthetics/private_location/acc_test.go | 1 + .../kibana_synthetics_private_location.tmpl | 25 ++++++ 7 files changed, 163 insertions(+) create mode 100644 docs/resources/kibana_synthetics_private_location create mode 100644 examples/resources/elasticstack_kibana_synthetics_private_location/import.sh create mode 100644 examples/resources/elasticstack_kibana_synthetics_private_location/resource.tf create mode 100644 templates/resources/kibana_synthetics_private_location.tmpl diff --git a/CHANGELOG.md b/CHANGELOG.md index f097c23c8a..9201c9ce3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Fix setting `id` for Fleet outputs and servers ([#666](https://github.com/elastic/terraform-provider-elasticstack/pull/666)) - Fix `elasticstack_fleet_enrollment_tokens` returning empty tokens in some case ([#683](https://github.com/elastic/terraform-provider-elasticstack/pull/683)) +- Add support for Kibana synthetics private locations ([#696](https://github.com/elastic/terraform-provider-elasticstack/pull/696)) ## [0.11.4] - 2024-06-13 diff --git a/docs/resources/kibana_synthetics_private_location b/docs/resources/kibana_synthetics_private_location new file mode 100644 index 0000000000..ee498c76af --- /dev/null +++ b/docs/resources/kibana_synthetics_private_location @@ -0,0 +1,76 @@ +--- +subcategory: "Kibana" +layout: "" +page_title: "Elasticstack: elasticstack_kibana_synthetics_private_location Resource" +description: |- + Creates or updates a Kibana synthetics private location. +--- + +# Resource: elasticstack_kibana_synthetics_private_location + +Creates or updates a Kibana synthetics private location. +See [Monitor via a private agent](https://www.elastic.co/guide/en/observability/current/synthetics-private-location.html#monitor-via-private-agent) +and [api docs](https://www.elastic.co/guide/en/kibana/current/create-private-location-api.html) + +## Example Usage + +```terraform +provider "elasticstack" { + fleet {} + kibana {} +} + +resource "elasticstack_fleet_agent_policy" "sample" { + name = "Sample Agent Policy" + namespace = "default" + description = "A sample agent policy" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} + +resource "elasticstack_kibana_synthetics_private_location" "example" { + label = "example label" + space_id = "default" + agent_policy_id = elasticstack_fleet_agent_policy.sample.policy_id + tags = ["tag-a", "tag-b"] + geo = { + lat = 40.7128 + lon = 74.0060 + } +} +``` + + +## Schema + +### Required + +- `agent_policy_id` (String) The ID of the agent policy associated with the private location. To create a private location for synthetics monitor you need to create an agent policy in fleet and use its agentPolicyId +- `label` (String) A label for the private location, used as unique identifier + +### Optional + +- `geo` (Attributes) Geographic coordinates (WGS84) for the location (see [below for nested schema](#nestedatt--geo)) +- `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used. +- `tags` (List of String) An array of tags to categorize the private location. + +### Read-Only + +- `id` (String) Generated id for the private location. For monitor setup please use private location label. + + +### Nested Schema for `geo` + +Required: + +- `lat` (Number) The latitude of the location. +- `lon` (Number) The longitude of the location. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import elasticstack_kibana_synthetics_private_location.my_location +``` \ No newline at end of file diff --git a/docs/resources/kibana_synthetics_private_location.md b/docs/resources/kibana_synthetics_private_location.md index f1e67f7963..0048150519 100644 --- a/docs/resources/kibana_synthetics_private_location.md +++ b/docs/resources/kibana_synthetics_private_location.md @@ -10,7 +10,34 @@ description: |- Synthetics private location config, see https://www.elastic.co/guide/en/kibana/current/create-private-location-api.html for more details +## Example Usage +```terraform +provider "elasticstack" { + fleet {} + kibana {} +} + +resource "elasticstack_fleet_agent_policy" "sample" { + name = "Sample Agent Policy" + namespace = "default" + description = "A sample agent policy" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} + +resource "elasticstack_kibana_synthetics_private_location" "example" { + label = "example label" + space_id = "default" + agent_policy_id = elasticstack_fleet_agent_policy.sample.policy_id + tags = ["tag-a", "tag-b"] + geo = { + lat = 40.7128 + lon = 74.0060 + } +} +``` ## Schema @@ -37,3 +64,11 @@ Required: - `lat` (Number) The latitude of the location. - `lon` (Number) The longitude of the location. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import elasticstack_kibana_synthetics_private_location.my_location +``` diff --git a/examples/resources/elasticstack_kibana_synthetics_private_location/import.sh b/examples/resources/elasticstack_kibana_synthetics_private_location/import.sh new file mode 100644 index 0000000000..628bacff47 --- /dev/null +++ b/examples/resources/elasticstack_kibana_synthetics_private_location/import.sh @@ -0,0 +1 @@ +terraform import elasticstack_kibana_synthetics_private_location.my_location diff --git a/examples/resources/elasticstack_kibana_synthetics_private_location/resource.tf b/examples/resources/elasticstack_kibana_synthetics_private_location/resource.tf new file mode 100644 index 0000000000..933485ee36 --- /dev/null +++ b/examples/resources/elasticstack_kibana_synthetics_private_location/resource.tf @@ -0,0 +1,24 @@ +provider "elasticstack" { + fleet {} + kibana {} +} + +resource "elasticstack_fleet_agent_policy" "sample" { + name = "Sample Agent Policy" + namespace = "default" + description = "A sample agent policy" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} + +resource "elasticstack_kibana_synthetics_private_location" "example" { + label = "example label" + space_id = "default" + agent_policy_id = elasticstack_fleet_agent_policy.sample.policy_id + tags = ["tag-a", "tag-b"] + geo = { + lat = 40.7128 + lon = 74.0060 + } +} diff --git a/internal/kibana/synthetics/private_location/acc_test.go b/internal/kibana/synthetics/private_location/acc_test.go index 4e75af0ef9..57995d7f59 100644 --- a/internal/kibana/synthetics/private_location/acc_test.go +++ b/internal/kibana/synthetics/private_location/acc_test.go @@ -15,6 +15,7 @@ const ( provider "elasticstack" { elasticsearch {} kibana {} + fleet{} } ` ) diff --git a/templates/resources/kibana_synthetics_private_location.tmpl b/templates/resources/kibana_synthetics_private_location.tmpl new file mode 100644 index 0000000000..694302668e --- /dev/null +++ b/templates/resources/kibana_synthetics_private_location.tmpl @@ -0,0 +1,25 @@ +--- +subcategory: "Kibana" +layout: "" +page_title: "Elasticstack: elasticstack_kibana_synthetics_private_location Resource" +description: |- + Creates or updates a Kibana synthetics private location. +--- + +# Resource: elasticstack_kibana_synthetics_private_location + +Creates or updates a Kibana synthetics private location. +See [Monitor via a private agent](https://www.elastic.co/guide/en/observability/current/synthetics-private-location.html#monitor-via-private-agent) +and [api docs](https://www.elastic.co/guide/en/kibana/current/create-private-location-api.html) + +## Example Usage + +{{ tffile "examples/resources/elasticstack_kibana_synthetics_private_location/resource.tf" }} + +{{ .SchemaMarkdown | trimspace }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" "examples/resources/elasticstack_kibana_synthetics_private_location/import.sh" }} \ No newline at end of file