Skip to content

Commit 430e4d3

Browse files
Copilottobio
andcommitted
Add namespace support to synthetics monitor resource
Co-authored-by: tobio <[email protected]>
1 parent fcd90c4 commit 430e4d3

File tree

6 files changed

+110
-3
lines changed

6 files changed

+110
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- Add support for `timeslice_metric_indicator` in `elasticstack_kibana_slo` ([#1195](https://github.com/elastic/terraform-provider-elasticstack/pull/1195))
44
- Add `elasticstack_elasticsearch_ingest_processor_reroute` data source ([#678](https://github.com/elastic/terraform-provider-elasticstack/issues/678))
5+
- Add `namespace` attribute to `elasticstack_kibana_synthetics_monitor` resource to support setting data stream namespace independently from `space_id` ([#1164](https://github.com/elastic/terraform-provider-elasticstack/issues/1164))
56

67
## [0.11.16] - 2025-07-09
78

internal/kibana/synthetics/acc_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/hashicorp/go-version"
1010
sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
1111
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
1213
)
1314

1415
var (
@@ -53,6 +54,34 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
5354
ipv6 = false
5455
}
5556
}
57+
`
58+
httpMonitorConfigWithNamespace = `
59+
60+
resource "elasticstack_kibana_synthetics_monitor" "%s" {
61+
name = "TestHttpMonitorResource - %s"
62+
space_id = "testacc"
63+
namespace = "test-namespace"
64+
schedule = 5
65+
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
66+
enabled = true
67+
tags = ["a", "b"]
68+
alert = {
69+
status = {
70+
enabled = true
71+
}
72+
tls = {
73+
enabled = true
74+
}
75+
}
76+
service_name = "test apm service"
77+
timeout = 30
78+
http = {
79+
url = "http://localhost:5601"
80+
mode = "any"
81+
ipv4 = true
82+
ipv6 = false
83+
}
84+
}
5685
`
5786
httpMonitorSslConfig = `
5887
@@ -842,3 +871,41 @@ resource "elasticstack_kibana_synthetics_private_location" "%s" {
842871

843872
return resourceId, provider + config
844873
}
874+
875+
func TestSyntheticMonitorHTTPResourceWithNamespace(t *testing.T) {
876+
877+
name := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum)
878+
id := "http-monitor-namespace"
879+
httpMonitorId, config := testMonitorConfig(id, httpMonitorConfigWithNamespace, name)
880+
881+
resource.Test(t, resource.TestCase{
882+
PreCheck: func() { acctest.PreCheck(t) },
883+
ProtoV6ProviderFactories: acctest.Providers,
884+
Steps: []resource.TestStep{
885+
// Create and Read http monitor with explicit namespace
886+
{
887+
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion),
888+
Config: config,
889+
Check: resource.ComposeAggregateTestCheckFunc(
890+
resource.TestCheckResourceAttrSet(httpMonitorId, "id"),
891+
resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource - "+name),
892+
resource.TestCheckResourceAttr(httpMonitorId, "space_id", "testacc"),
893+
resource.TestCheckResourceAttr(httpMonitorId, "namespace", "test-namespace"),
894+
resource.TestCheckResourceAttr(httpMonitorId, "schedule", "5"),
895+
resource.TestCheckResourceAttr(httpMonitorId, "enabled", "true"),
896+
resource.TestCheckResourceAttr(httpMonitorId, "http.url", "http://localhost:5601"),
897+
),
898+
},
899+
// Import
900+
{
901+
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion),
902+
ResourceName: httpMonitorId,
903+
ImportState: true,
904+
ImportStateIdFunc: func(s *terraform.State) (string, error) {
905+
return fmt.Sprintf("%s/%s", "testacc", s.RootModule().Resources[httpMonitorId].Primary.Attributes["id"]), nil
906+
},
907+
ImportStateVerify: true,
908+
},
909+
},
910+
})
911+
}

internal/kibana/synthetics/create.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r
2626
return
2727
}
2828

29-
namespace := plan.SpaceID.ValueString()
29+
// Use namespace if explicitly set, otherwise fall back to space_id
30+
namespace := plan.Namespace.ValueString()
31+
if namespace == "" || plan.Namespace.IsNull() || plan.Namespace.IsUnknown() {
32+
namespace = plan.SpaceID.ValueString()
33+
}
34+
3035
result, err := kibanaClient.KibanaSynthetics.Monitor.Add(ctx, input.config, input.fields, namespace)
3136
if err != nil {
3237
response.Diagnostics.AddError(fmt.Sprintf("Failed to create Kibana monitor `%s`, namespace %s", input.config.Name, namespace), err.Error())

internal/kibana/synthetics/schema.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ type tfModelV0 struct {
9999
ID types.String `tfsdk:"id"`
100100
Name types.String `tfsdk:"name"`
101101
SpaceID types.String `tfsdk:"space_id"`
102+
Namespace types.String `tfsdk:"namespace"`
102103
Schedule types.Int64 `tfsdk:"schedule"`
103104
Locations []types.String `tfsdk:"locations"`
104105
PrivateLocations []types.String `tfsdk:"private_locations"`
@@ -151,6 +152,15 @@ func monitorConfigSchema() schema.Schema {
151152
},
152153
Computed: true,
153154
},
155+
"namespace": schema.StringAttribute{
156+
MarkdownDescription: "The data stream namespace. If not specified, defaults to the value of space_id. The namespace must be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \\, /, ?, \", <, >, |, whitespace, ,, #, :, or -.",
157+
Optional: true,
158+
PlanModifiers: []planmodifier.String{
159+
stringplanmodifier.UseStateForUnknown(),
160+
stringplanmodifier.RequiresReplace(),
161+
},
162+
Computed: true,
163+
},
154164
"schedule": schema.Int64Attribute{
155165
Optional: true,
156166
MarkdownDescription: "The monitor’s schedule in minutes. Supported values are 1, 3, 5, 10, 15, 30, 60, 120 and 240.",
@@ -653,6 +663,7 @@ func (v *tfModelV0) toModelV0(ctx context.Context, api *kbapi.SyntheticsMonitor)
653663
ID: types.StringValue(resourceID.String()),
654664
Name: types.StringValue(api.Name),
655665
SpaceID: types.StringValue(api.Namespace),
666+
Namespace: types.StringValue(api.Namespace),
656667
Schedule: types.Int64Value(schedule),
657668
Locations: v.Locations,
658669
PrivateLocations: StringSliceValue(privateLocLabels),
@@ -873,6 +884,12 @@ func (v *tfModelV0) toSyntheticsMonitorConfig(ctx context.Context) (*kbapi.Synth
873884
return nil, dg
874885
}
875886

887+
// Use namespace if explicitly set, otherwise fall back to space_id
888+
namespace := v.Namespace.ValueString()
889+
if namespace == "" || v.Namespace.IsNull() || v.Namespace.IsUnknown() {
890+
namespace = v.SpaceID.ValueString()
891+
}
892+
876893
return &kbapi.SyntheticsMonitorConfig{
877894
Name: v.Name.ValueString(),
878895
Schedule: kbapi.MonitorSchedule(v.Schedule.ValueInt64()),
@@ -883,7 +900,7 @@ func (v *tfModelV0) toSyntheticsMonitorConfig(ctx context.Context) (*kbapi.Synth
883900
Alert: toTFAlertConfig(ctx, v.Alert),
884901
APMServiceName: v.APMServiceName.ValueString(),
885902
TimeoutSeconds: int(v.TimeoutSeconds.ValueInt64()),
886-
Namespace: v.SpaceID.ValueString(),
903+
Namespace: namespace,
887904
Params: params,
888905
RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(),
889906
}, diag.Diagnostics{} //dg

internal/kibana/synthetics/schema_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func TestToModelV0(t *testing.T) {
4949
ID: types.StringValue("/"),
5050
Name: types.StringValue(""),
5151
SpaceID: types.StringValue(""),
52+
Namespace: types.StringValue(""),
5253
Schedule: types.Int64Value(0),
5354
APMServiceName: types.StringValue(""),
5455
TimeoutSeconds: types.Int64Value(0),
@@ -83,6 +84,7 @@ func TestToModelV0(t *testing.T) {
8384
ID: types.StringValue("/"),
8485
Name: types.StringValue(""),
8586
SpaceID: types.StringValue(""),
87+
Namespace: types.StringValue(""),
8688
Schedule: types.Int64Value(0),
8789
APMServiceName: types.StringValue(""),
8890
TimeoutSeconds: types.Int64Value(0),
@@ -111,6 +113,7 @@ func TestToModelV0(t *testing.T) {
111113
ID: types.StringValue("/"),
112114
Name: types.StringValue(""),
113115
SpaceID: types.StringValue(""),
116+
Namespace: types.StringValue(""),
114117
Schedule: types.Int64Value(0),
115118
APMServiceName: types.StringValue(""),
116119
TimeoutSeconds: types.Int64Value(0),
@@ -130,6 +133,7 @@ func TestToModelV0(t *testing.T) {
130133
ID: types.StringValue("/"),
131134
Name: types.StringValue(""),
132135
SpaceID: types.StringValue(""),
136+
Namespace: types.StringValue(""),
133137
Schedule: types.Int64Value(0),
134138
APMServiceName: types.StringValue(""),
135139
TimeoutSeconds: types.Int64Value(0),
@@ -320,6 +324,7 @@ func TestToModelV0(t *testing.T) {
320324
ID: types.StringValue("default/test-id-icmp"),
321325
Name: types.StringValue("test-name-icmp"),
322326
SpaceID: types.StringValue("default"),
327+
Namespace: types.StringValue("default"),
323328
Schedule: types.Int64Value(5),
324329
Locations: nil,
325330
PrivateLocations: []types.String{types.StringValue("test private location")},
@@ -375,6 +380,7 @@ func TestToModelV0(t *testing.T) {
375380
ID: types.StringValue("default/test-id-browser"),
376381
Name: types.StringValue("test-name-browser"),
377382
SpaceID: types.StringValue("default"),
383+
Namespace: types.StringValue("default"),
378384
Schedule: types.Int64Value(5),
379385
Locations: nil,
380386
PrivateLocations: []types.String{types.StringValue("test private location")},
@@ -457,6 +463,7 @@ func TestToKibanaAPIRequest(t *testing.T) {
457463
ID: types.StringValue("test-id-http"),
458464
Name: types.StringValue("test-name-http"),
459465
SpaceID: types.StringValue("default"),
466+
Namespace: types.StringValue("default"),
460467
Schedule: types.Int64Value(5),
461468
Locations: []types.String{types.StringValue("us_east")},
462469
PrivateLocations: []types.String{types.StringValue("test private location")},
@@ -533,6 +540,7 @@ func TestToKibanaAPIRequest(t *testing.T) {
533540
ID: types.StringValue("test-id-tcp"),
534541
Name: types.StringValue("test-name-tcp"),
535542
SpaceID: types.StringValue("default"),
543+
Namespace: types.StringValue("default"),
536544
Schedule: types.Int64Value(5),
537545
Locations: []types.String{types.StringValue("us_east")},
538546
PrivateLocations: nil,
@@ -597,6 +605,7 @@ func TestToKibanaAPIRequest(t *testing.T) {
597605
ID: types.StringValue("test-id-icmp"),
598606
Name: types.StringValue("test-name-icmp"),
599607
SpaceID: types.StringValue("default"),
608+
Namespace: types.StringValue("default"),
600609
Schedule: types.Int64Value(5),
601610
Locations: []types.String{types.StringValue("us_east")},
602611
PrivateLocations: nil,
@@ -637,6 +646,7 @@ func TestToKibanaAPIRequest(t *testing.T) {
637646
ID: types.StringValue("test-id-browser"),
638647
Name: types.StringValue("test-name-browser"),
639648
SpaceID: types.StringValue("default"),
649+
Namespace: types.StringValue("default"),
640650
Schedule: types.Int64Value(5),
641651
Locations: []types.String{types.StringValue("us_east")},
642652
PrivateLocations: nil,
@@ -767,6 +777,7 @@ func TestToModelV0MergeAttributes(t *testing.T) {
767777
ID: types.StringValue("/"),
768778
Name: types.StringValue(""),
769779
SpaceID: types.StringValue(""),
780+
Namespace: types.StringValue(""),
770781
Schedule: types.Int64Value(0),
771782
APMServiceName: types.StringValue(""),
772783
TimeoutSeconds: types.Int64Value(0),
@@ -801,6 +812,7 @@ func TestToModelV0MergeAttributes(t *testing.T) {
801812
ID: types.StringValue("/"),
802813
Name: types.StringValue(""),
803814
SpaceID: types.StringValue(""),
815+
Namespace: types.StringValue(""),
804816
Schedule: types.Int64Value(0),
805817
APMServiceName: types.StringValue(""),
806818
TimeoutSeconds: types.Int64Value(0),

internal/kibana/synthetics/update.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ func (r *Resource) Update(ctx context.Context, request resource.UpdateRequest, r
3333
return
3434
}
3535

36-
namespace := plan.SpaceID.ValueString()
36+
// Use namespace if explicitly set, otherwise fall back to space_id
37+
namespace := plan.Namespace.ValueString()
38+
if namespace == "" || plan.Namespace.IsNull() || plan.Namespace.IsUnknown() {
39+
namespace = plan.SpaceID.ValueString()
40+
}
41+
3742
result, err := kibanaClient.KibanaSynthetics.Monitor.Update(ctx, kbapi.MonitorID(monitorId.ResourceId), input.config, input.fields, namespace)
3843
if err != nil {
3944
response.Diagnostics.AddError(fmt.Sprintf("Failed to update Kibana monitor `%s`, namespace %s", input.config.Name, namespace), err.Error())

0 commit comments

Comments
 (0)