Skip to content

fix: make synthetics fleet namespace aware #1247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Add `slo_id` validation to `elasticstack_kibana_slo` ([#1221](https://github.com/elastic/terraform-provider-elasticstack/pull/1221))
- Add `ignore_missing_component_templates` to `elasticstack_elasticsearch_index_template` ([#1206](https://github.com/elastic/terraform-provider-elasticstack/pull/1206))
- Prevent provider panic when a script exists in state, but not in Elasticsearch ([#1218](https://github.com/elastic/terraform-provider-elasticstack/pull/1218))
- Fix `namespace` usage in synthetic monitors. Separate Kibana `space_id` the monitor is saved in from the `namespace`, the data stream namespace for Fleet. ([#1247](https://github.com/elastic/terraform-provider-elasticstack/pull/1247))

## [0.11.17] - 2025-07-21

Expand Down
3 changes: 2 additions & 1 deletion docs/resources/kibana_synthetics_monitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,13 @@ resource "elasticstack_kibana_synthetics_monitor" "my_monitor" {
- `http` (Attributes) HTTP Monitor specific fields (see [below for nested schema](#nestedatt--http))
- `icmp` (Attributes) ICMP Monitor specific fields (see [below for nested schema](#nestedatt--icmp))
- `locations` (List of String) Where to deploy the monitor. Monitors can be deployed in multiple locations so that you can detect differences in availability and response times across those locations.
- `namespace` (String) Fleet namespace. The `namespace` field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \, /, ?, ", <, >, |, whitespace, ,, #, :, or -. Default: `default`
- `params` (String) Monitor parameters. Raw JSON object, use `jsonencode` function to represent JSON
- `private_locations` (List of String) These Private Locations refer to locations hosted and managed by you, whereas locations are hosted by Elastic. You can specify a Private Location using the location’s name.
- `retest_on_failure` (Boolean) Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from "up" to "down". If the result of the retest is also "down", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`.
- `schedule` (Number) The monitor’s schedule in minutes. Supported values are 1, 3, 5, 10, 15, 30, 60, 120 and 240.
- `service_name` (String) The APM service name.
- `space_id` (String) The namespace field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \, /, ?, ", <, >, |, whitespace, ,, #, :, or -. Default: `default`
- `space_id` (String) Kibana space. The space ID that is part of the Kibana URL when inside the space. Space IDs are limited to lowercase alphanumeric, underscore, and hyphen characters (a-z, 0-9, _, and -). You are cannot change the ID with the update operation.
- `tags` (List of String) An array of tags.
- `tcp` (Attributes) TCP Monitor specific fields (see [below for nested schema](#nestedatt--tcp))
- `timeout` (Number) The monitor timeout in seconds, monitor will fail if it doesn’t complete within this time. Default: `16`
Expand Down
30 changes: 24 additions & 6 deletions internal/kibana/synthetics/acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestHttpMonitorResource - %s"
space_id = "testacc"
namespace = "test_namespace"
schedule = 5
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = true
Expand Down Expand Up @@ -164,6 +165,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestTcpMonitorResource - %s"
space_id = "testacc"
namespace = "testacc_test"
schedule = 5
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = true
Expand Down Expand Up @@ -230,6 +232,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestIcmpMonitorResource - %s"
space_id = "testacc"
namespace = "testacc_namespace"
schedule = 5
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = true
Expand Down Expand Up @@ -279,6 +282,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestBrowserMonitorResource - %s"
space_id = "testacc"
namespace = "testacc_ns"
schedule = 5
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = true
Expand Down Expand Up @@ -363,7 +367,8 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(bmMonitorId, "id"),
resource.TestCheckResourceAttr(bmMonitorId, "name", "TestHttpMonitorResource - "+bmName),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(bmMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.tls.enabled", "true"),
resource.TestCheckResourceAttr(bmMonitorId, "http.url", "http://localhost:5601"),
Expand All @@ -376,7 +381,8 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(sslHttpMonitorId, "id"),
resource.TestCheckResourceAttr(sslHttpMonitorId, "name", "TestHttpMonitorResource - "+sslName),
resource.TestCheckResourceAttr(sslHttpMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(sslHttpMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(sslHttpMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(sslHttpMonitorId, "http.url", "http://localhost:5601"),
resource.TestCheckResourceAttr(sslHttpMonitorId, "http.ssl_verification_mode", "full"),
resource.TestCheckResourceAttr(sslHttpMonitorId, "http.ssl_supported_protocols.#", "1"),
Expand Down Expand Up @@ -405,6 +411,7 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(httpMonitorId, "id"),
resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource - "+name),
resource.TestCheckResourceAttr(httpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(httpMonitorId, "namespace", "test_namespace"),
resource.TestCheckResourceAttr(httpMonitorId, "schedule", "5"),
resource.TestCheckResourceAttr(httpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(httpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -446,6 +453,7 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(httpMonitorId, "id"),
resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource Updated - "+name),
resource.TestCheckResourceAttr(httpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(httpMonitorId, "namespace", "test_namespace"),
resource.TestCheckResourceAttr(httpMonitorId, "schedule", "10"),
resource.TestCheckResourceAttr(httpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(httpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -509,7 +517,8 @@ func TestSyntheticMonitorTCPResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(bmMonitorId, "id"),
resource.TestCheckResourceAttr(bmMonitorId, "name", "TestTcpMonitorResource - "+bmName),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(bmMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "tcp.host", "http://localhost:5601"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.tls.enabled", "true"),
Expand All @@ -523,7 +532,8 @@ func TestSyntheticMonitorTCPResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(sslTcpMonitorId, "id"),
resource.TestCheckResourceAttr(sslTcpMonitorId, "name", "TestHttpMonitorResource - "+sslName),
resource.TestCheckResourceAttr(sslTcpMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(sslTcpMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(sslTcpMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(sslTcpMonitorId, "tcp.host", "http://localhost:5601"),
resource.TestCheckResourceAttr(sslTcpMonitorId, "tcp.ssl_verification_mode", "full"),
resource.TestCheckResourceAttr(sslTcpMonitorId, "tcp.ssl_supported_protocols.#", "1"),
Expand Down Expand Up @@ -552,6 +562,7 @@ func TestSyntheticMonitorTCPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(tcpMonitorId, "id"),
resource.TestCheckResourceAttr(tcpMonitorId, "name", "TestTcpMonitorResource - "+name),
resource.TestCheckResourceAttr(tcpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(tcpMonitorId, "namespace", "testacc_test"),
resource.TestCheckResourceAttr(tcpMonitorId, "schedule", "5"),
resource.TestCheckResourceAttr(tcpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(tcpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -590,6 +601,7 @@ func TestSyntheticMonitorTCPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(tcpMonitorId, "id"),
resource.TestCheckResourceAttr(tcpMonitorId, "name", "TestTcpMonitorResource Updated - "+name),
resource.TestCheckResourceAttr(tcpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(tcpMonitorId, "namespace", "testacc_test"),
resource.TestCheckResourceAttr(tcpMonitorId, "schedule", "10"),
resource.TestCheckResourceAttr(tcpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(tcpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -643,7 +655,8 @@ func TestSyntheticMonitorICMPResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(bmMonitorId, "id"),
resource.TestCheckResourceAttr(bmMonitorId, "name", "TestIcmpMonitorResource - "+bmName),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(bmMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "icmp.host", "localhost"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.tls.enabled", "true"),
Expand All @@ -658,6 +671,7 @@ func TestSyntheticMonitorICMPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(icmpMonitorId, "id"),
resource.TestCheckResourceAttr(icmpMonitorId, "name", "TestIcmpMonitorResource - "+name),
resource.TestCheckResourceAttr(icmpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(icmpMonitorId, "namespace", "testacc_namespace"),
resource.TestCheckResourceAttr(icmpMonitorId, "schedule", "5"),
resource.TestCheckResourceAttr(icmpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(icmpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -689,6 +703,7 @@ func TestSyntheticMonitorICMPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(icmpMonitorId, "id"),
resource.TestCheckResourceAttr(icmpMonitorId, "name", "TestIcmpMonitorResource Updated - "+name),
resource.TestCheckResourceAttr(icmpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(icmpMonitorId, "namespace", "testacc_namespace"),
resource.TestCheckResourceAttr(icmpMonitorId, "schedule", "10"),
resource.TestCheckResourceAttr(icmpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(icmpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -735,7 +750,8 @@ func TestSyntheticMonitorBrowserResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(bmMonitorId, "id"),
resource.TestCheckResourceAttr(bmMonitorId, "name", "TestBrowserMonitorResource - "+bmName),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(bmMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "browser.inline_script", "step('Go to https://google.com.co', () => page.goto('https://www.google.com'))"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.tls.enabled", "true"),
Expand All @@ -749,6 +765,7 @@ func TestSyntheticMonitorBrowserResource(t *testing.T) {
resource.TestCheckResourceAttrSet(browserMonitorId, "id"),
resource.TestCheckResourceAttr(browserMonitorId, "name", "TestBrowserMonitorResource - "+name),
resource.TestCheckResourceAttr(browserMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(browserMonitorId, "namespace", "testacc_ns"),
resource.TestCheckResourceAttr(browserMonitorId, "schedule", "5"),
resource.TestCheckResourceAttr(browserMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(browserMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -780,6 +797,7 @@ func TestSyntheticMonitorBrowserResource(t *testing.T) {
resource.TestCheckResourceAttrSet(browserMonitorId, "id"),
resource.TestCheckResourceAttr(browserMonitorId, "name", "TestBrowserMonitorResource Updated - "+name),
resource.TestCheckResourceAttr(browserMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(browserMonitorId, "namespace", "testacc_ns"),
resource.TestCheckResourceAttr(browserMonitorId, "schedule", "10"),
resource.TestCheckResourceAttr(browserMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(browserMonitorId, "private_locations.0"),
Expand Down
9 changes: 5 additions & 4 deletions internal/kibana/synthetics/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package synthetics
import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/resource"
)

Expand All @@ -26,14 +27,14 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r
return
}

namespace := plan.SpaceID.ValueString()
result, err := kibanaClient.KibanaSynthetics.Monitor.Add(ctx, input.config, input.fields, namespace)
spaceId := plan.SpaceID.ValueString()
result, err := kibanaClient.KibanaSynthetics.Monitor.Add(ctx, input.config, input.fields, spaceId)
if err != nil {
response.Diagnostics.AddError(fmt.Sprintf("Failed to create Kibana monitor `%s`, namespace %s", input.config.Name, namespace), err.Error())
response.Diagnostics.AddError(fmt.Sprintf("Failed to create Kibana monitor `%s`, space %s", input.config.Name, spaceId), err.Error())
return
}

plan, diags = plan.toModelV0(ctx, result)
plan, diags = plan.toModelV0(ctx, result, spaceId)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
Expand Down
9 changes: 5 additions & 4 deletions internal/kibana/synthetics/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"

"github.com/disaster37/go-kibana-rest/v8/kbapi"
"github.com/hashicorp/terraform-plugin-framework/resource"
)
Expand All @@ -28,21 +29,21 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo
return
}

namespace := compositeId.ClusterId
spaceId := compositeId.ClusterId
monitorId := kbapi.MonitorID(compositeId.ResourceId)
result, err := kibanaClient.KibanaSynthetics.Monitor.Get(ctx, monitorId, namespace)
result, err := kibanaClient.KibanaSynthetics.Monitor.Get(ctx, monitorId, spaceId)
if err != nil {
var apiError *kbapi.APIError
if errors.As(err, &apiError) && apiError.Code == 404 {
response.State.RemoveResource(ctx)
return
}

response.Diagnostics.AddError(fmt.Sprintf("Failed to get monitor `%s`, namespace %s", monitorId, namespace), err.Error())
response.Diagnostics.AddError(fmt.Sprintf("Failed to get monitor `%s`, space %s", monitorId, spaceId), err.Error())
return
}

state, diags = state.toModelV0(ctx, result)
state, diags = state.toModelV0(ctx, result, spaceId)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
Expand Down
21 changes: 16 additions & 5 deletions internal/kibana/synthetics/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type tfModelV0 struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
SpaceID types.String `tfsdk:"space_id"`
Namespace types.String `tfsdk:"namespace"`
Schedule types.Int64 `tfsdk:"schedule"`
Locations []types.String `tfsdk:"locations"`
PrivateLocations []types.String `tfsdk:"private_locations"`
Expand Down Expand Up @@ -143,7 +144,16 @@ func monitorConfigSchema() schema.Schema {
MarkdownDescription: "The monitor’s name.",
},
"space_id": schema.StringAttribute{
MarkdownDescription: "The namespace field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \\, /, ?, \", <, >, |, whitespace, ,, #, :, or -. Default: `default`",
MarkdownDescription: "Kibana space. The space ID that is part of the Kibana URL when inside the space. Space IDs are limited to lowercase alphanumeric, underscore, and hyphen characters (a-z, 0-9, _, and -). You are cannot change the ID with the update operation.",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
stringplanmodifier.RequiresReplace(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@biscout42 TODO: not the case, we could update namespace w/o changing a monitor. The monitor will write data for new test in new datastream in the case of a change.

},
Computed: true,
},
"namespace": schema.StringAttribute{
MarkdownDescription: "Fleet namespace. The `namespace` field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \\, /, ?, \", <, >, |, whitespace, ,, #, :, or -. Default: `default`",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
Expand Down Expand Up @@ -566,7 +576,7 @@ func stringToInt64(v string) (int64, error) {
return res, err
}

func (v *tfModelV0) toModelV0(ctx context.Context, api *kbapi.SyntheticsMonitor) (*tfModelV0, diag.Diagnostics) {
func (v *tfModelV0) toModelV0(ctx context.Context, api *kbapi.SyntheticsMonitor, space string) (*tfModelV0, diag.Diagnostics) {
var schedule int64
var err error
dg := diag.Diagnostics{}
Expand Down Expand Up @@ -640,7 +650,7 @@ func (v *tfModelV0) toModelV0(ctx context.Context, api *kbapi.SyntheticsMonitor)
}

resourceID := clients.CompositeId{
ClusterId: api.Namespace,
ClusterId: space,
ResourceId: string(api.Id),
}

Expand All @@ -652,7 +662,8 @@ func (v *tfModelV0) toModelV0(ctx context.Context, api *kbapi.SyntheticsMonitor)
return &tfModelV0{
ID: types.StringValue(resourceID.String()),
Name: types.StringValue(api.Name),
SpaceID: types.StringValue(api.Namespace),
SpaceID: types.StringValue(space),
Namespace: types.StringValue(api.Namespace),
Schedule: types.Int64Value(schedule),
Locations: v.Locations,
PrivateLocations: StringSliceValue(privateLocLabels),
Expand Down Expand Up @@ -883,7 +894,7 @@ func (v *tfModelV0) toSyntheticsMonitorConfig(ctx context.Context) (*kbapi.Synth
Alert: toTFAlertConfig(ctx, v.Alert),
APMServiceName: v.APMServiceName.ValueString(),
TimeoutSeconds: int(v.TimeoutSeconds.ValueInt64()),
Namespace: v.SpaceID.ValueString(),
Namespace: v.Namespace.ValueString(),
Params: params,
RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(),
}, diag.Diagnostics{} //dg
Expand Down
Loading
Loading