diff --git a/CHANGELOG.md b/CHANGELOG.md
index d84909d93..443155405 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
- Add the `Frequency` field to the Create Rule API ([#753](https://github.com/elastic/terraform-provider-elasticstack/pull/753))
- Prevent a provider panic when the repository referenced in an `elasticstack_elasticsearch_snapshot_repository` does not exist ([#758](https://github.com/elastic/terraform-provider-elasticstack/pull/758))
- Add support for `remote_indicies` to `elasticstack_elasticsearch_security_api_key` (#766)[https://github.com/elastic/terraform-provider-elasticstack/pull/766]
+- Add support for `icmp` and `browser` monitor types to `elasticstack_kibana_synthetics_monitor` resource (#772)[https://github.com/elastic/terraform-provider-elasticstack/pull/772]
## [0.11.6] - 2024-08-20
diff --git a/docs/resources/kibana_synthetics_monitor.md b/docs/resources/kibana_synthetics_monitor.md
index 37ef5de61..4ab1908bc 100644
--- a/docs/resources/kibana_synthetics_monitor.md
+++ b/docs/resources/kibana_synthetics_monitor.md
@@ -14,6 +14,8 @@ See [API docs](https://www.elastic.co/guide/en/kibana/current/add-monitor-api.ht
## Supported monitor types
* `http`
* `tcp`
+ * `icmp`
+ * `browser`
**NOTE:** Due-to nature of partial update API, reset values to defaults is not supported.
In case you would like to reset an optional monitor value, please set it explicitly or delete and create new monitor.
@@ -65,8 +67,10 @@ resource "elasticstack_kibana_synthetics_monitor" "my_monitor" {
### Optional
- `alert` (Attributes) Alert configuration. Default: `{ status: { enabled: true }, tls: { enabled: true } }`. (see [below for nested schema](#nestedatt--alert))
+- `browser` (Attributes) Browser Monitor specific fields (see [below for nested schema](#nestedatt--browser))
- `enabled` (Boolean) Whether the monitor is enabled. Default: `true`
- `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.
- `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.
@@ -107,6 +111,21 @@ Optional:
+
+### Nested Schema for `browser`
+
+Required:
+
+- `inline_script` (String) The inline script.
+
+Optional:
+
+- `ignore_https_errors` (Boolean) Whether to ignore HTTPS errors.
+- `playwright_options` (String) Playwright options.. Raw JSON object, use `jsonencode` function to represent JSON
+- `screenshots` (String) Controls the behavior of the screenshots feature.
+- `synthetics_args` (List of String) Synthetics agent CLI arguments.
+
+
### Nested Schema for `http`
@@ -130,6 +149,18 @@ Optional:
- `username` (String) The username for authenticating with the server. The credentials are passed with the request.
+
+### Nested Schema for `icmp`
+
+Required:
+
+- `host` (String) Host to ping; it can be an IP address or a hostname.
+
+Optional:
+
+- `wait` (Number) Wait time in seconds. Default: `1`
+
+
### Nested Schema for `tcp`
@@ -157,4 +188,4 @@ terraform import elasticstack_kibana_synthetics_monitor.my_monitor / page.goto('https://www.google.com'))"),
+ ),
+ },
+ // ImportState testing - kibana doesn't return required parameter inline_script for browser monitor, so import state is not supported till the fix
+ /* {
+ SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion),
+ ResourceName: browserMonitorId,
+ ImportState: true,
+ ImportStateVerify: true,
+ Config: config,
+ },
+ */ // Update and Read browser monitor
+ {
+ SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion),
+ ResourceName: browserMonitorId,
+ Config: configUpdated,
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttrSet(browserMonitorId, "id"),
+ resource.TestCheckResourceAttr(browserMonitorId, "name", "TestBrowserMonitorResource Updated - "+name),
+ resource.TestCheckResourceAttr(browserMonitorId, "space_id", "testacc"),
+ resource.TestCheckResourceAttr(browserMonitorId, "schedule", "10"),
+ resource.TestCheckResourceAttr(browserMonitorId, "private_locations.#", "1"),
+ resource.TestCheckResourceAttrSet(browserMonitorId, "private_locations.0"),
+ resource.TestCheckResourceAttr(browserMonitorId, "enabled", "false"),
+ resource.TestCheckResourceAttr(browserMonitorId, "tags.#", "3"),
+ resource.TestCheckResourceAttr(browserMonitorId, "tags.0", "c"),
+ resource.TestCheckResourceAttr(browserMonitorId, "tags.1", "d"),
+ resource.TestCheckResourceAttr(browserMonitorId, "tags.2", "e"),
+ resource.TestCheckResourceAttr(browserMonitorId, "alert.status.enabled", "true"),
+ resource.TestCheckResourceAttr(browserMonitorId, "alert.tls.enabled", "false"),
+ resource.TestCheckResourceAttr(browserMonitorId, "service_name", "test apm service"),
+ resource.TestCheckResourceAttr(browserMonitorId, "timeout", "30"),
+ resource.TestCheckResourceAttr(browserMonitorId, "browser.inline_script", "step('Go to https://google.de', () => page.goto('https://www.google.de'))"),
+ resource.TestCheckResourceAttr(browserMonitorId, "browser.synthetics_args.#", "2"),
+ resource.TestCheckResourceAttr(browserMonitorId, "browser.synthetics_args.0", "--no-sandbox"),
+ resource.TestCheckResourceAttr(browserMonitorId, "browser.synthetics_args.1", "--disable-setuid-sandbox"),
+ resource.TestCheckResourceAttr(browserMonitorId, "browser.screenshots", "off"),
+ resource.TestCheckResourceAttr(browserMonitorId, "browser.ignore_https_errors", "true"),
+ resource.TestCheckResourceAttr(browserMonitorId, "browser.playwright_options", `{"httpCredentials":{"password":"test","username":"test"},"ignoreHTTPSErrors":false}`),
+ resource.TestCheckNoResourceAttr(browserMonitorId, "http"),
+ resource.TestCheckNoResourceAttr(browserMonitorId, "icmp"),
+ resource.TestCheckNoResourceAttr(browserMonitorId, "tcp"),
+ ),
+ },
+ // Delete testing automatically occurs in TestCase
+
+ },
+ })
+}
+
func testMonitorConfig(id, cfg, name string) (string, string) {
resourceId := "elasticstack_kibana_synthetics_monitor." + id
diff --git a/internal/kibana/synthetics/resource.go b/internal/kibana/synthetics/resource.go
index 1925ff521..33df23985 100644
--- a/internal/kibana/synthetics/resource.go
+++ b/internal/kibana/synthetics/resource.go
@@ -55,7 +55,8 @@ func (r *Resource) ConfigValidators(ctx context.Context) []resource.ConfigValida
resourcevalidator.ExactlyOneOf(
path.MatchRoot("http"),
path.MatchRoot("tcp"),
- // other monitor config types: icmp, browser
+ path.MatchRoot("icmp"),
+ path.MatchRoot("browser"),
),
resourcevalidator.AtLeastOneOf(
path.MatchRoot("locations"),
diff --git a/internal/kibana/synthetics/schema.go b/internal/kibana/synthetics/schema.go
index 2f8267a4b..2d69c285f 100644
--- a/internal/kibana/synthetics/schema.go
+++ b/internal/kibana/synthetics/schema.go
@@ -11,6 +11,8 @@ import (
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
+ "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"
@@ -62,22 +64,37 @@ type tfTCPMonitorFieldsV0 struct {
ProxyUseLocalResolver types.Bool `tfsdk:"proxy_use_local_resolver"`
}
+type tfICMPMonitorFieldsV0 struct {
+ Host types.String `tfsdk:"host"`
+ Wait types.Int64 `tfsdk:"wait"`
+}
+
+type tfBrowserMonitorFieldsV0 struct {
+ InlineScript types.String `tfsdk:"inline_script"`
+ Screenshots types.String `tfsdk:"screenshots"`
+ SyntheticsArgs []types.String `tfsdk:"synthetics_args"`
+ IgnoreHttpsErrors types.Bool `tfsdk:"ignore_https_errors"`
+ PlaywrightOptions jsontypes.Normalized `tfsdk:"playwright_options"`
+}
+
type tfModelV0 struct {
- ID types.String `tfsdk:"id"`
- Name types.String `tfsdk:"name"`
- SpaceID types.String `tfsdk:"space_id"`
- Schedule types.Int64 `tfsdk:"schedule"`
- Locations []types.String `tfsdk:"locations"`
- PrivateLocations []types.String `tfsdk:"private_locations"`
- Enabled types.Bool `tfsdk:"enabled"`
- Tags []types.String `tfsdk:"tags"`
- Alert *tfAlertConfigV0 `tfsdk:"alert"`
- APMServiceName types.String `tfsdk:"service_name"`
- TimeoutSeconds types.Int64 `tfsdk:"timeout"`
- HTTP *tfHTTPMonitorFieldsV0 `tfsdk:"http"`
- TCP *tfTCPMonitorFieldsV0 `tfsdk:"tcp"`
- Params jsontypes.Normalized `tfsdk:"params"`
- RetestOnFailure types.Bool `tfsdk:"retest_on_failure"`
+ ID types.String `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ SpaceID types.String `tfsdk:"space_id"`
+ Schedule types.Int64 `tfsdk:"schedule"`
+ Locations []types.String `tfsdk:"locations"`
+ PrivateLocations []types.String `tfsdk:"private_locations"`
+ Enabled types.Bool `tfsdk:"enabled"`
+ Tags []types.String `tfsdk:"tags"`
+ Alert *tfAlertConfigV0 `tfsdk:"alert"`
+ APMServiceName types.String `tfsdk:"service_name"`
+ TimeoutSeconds types.Int64 `tfsdk:"timeout"`
+ HTTP *tfHTTPMonitorFieldsV0 `tfsdk:"http"`
+ TCP *tfTCPMonitorFieldsV0 `tfsdk:"tcp"`
+ ICMP *tfICMPMonitorFieldsV0 `tfsdk:"icmp"`
+ Browser *tfBrowserMonitorFieldsV0 `tfsdk:"browser"`
+ Params jsontypes.Normalized `tfsdk:"params"`
+ RetestOnFailure types.Bool `tfsdk:"retest_on_failure"`
}
func GetCompositeId(id string) (*clients.CompositeId, diag.Diagnostics) {
@@ -165,9 +182,11 @@ func monitorConfigSchema() schema.Schema {
Optional: true,
MarkdownDescription: "The monitor timeout in seconds, monitor will fail if it doesn’t complete within this time. Default: `16`",
},
- "params": jsonObjectSchema("Monitor parameters"),
- "http": httpMonitorFieldsSchema(),
- "tcp": tcpMonitorFieldsSchema(),
+ "params": jsonObjectSchema("Monitor parameters"),
+ "http": httpMonitorFieldsSchema(),
+ "tcp": tcpMonitorFieldsSchema(),
+ "icmp": icmpMonitorFieldsSchema(),
+ "browser": browserMonitorFieldsSchema(),
"retest_on_failure": schema.BoolAttribute{
Optional: true,
MarkdownDescription: "Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from \"up\" to \"down\". If the result of the retest is also \"down\", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`.",
@@ -176,6 +195,61 @@ func monitorConfigSchema() schema.Schema {
}
}
+func browserMonitorFieldsSchema() schema.Attribute {
+ return schema.SingleNestedAttribute{
+ Optional: true,
+ MarkdownDescription: "Browser Monitor specific fields",
+ Attributes: map[string]schema.Attribute{
+ "inline_script": schema.StringAttribute{
+ Optional: false,
+ Required: true,
+ MarkdownDescription: "The inline script.",
+ },
+ "screenshots": schema.StringAttribute{
+ Optional: true,
+ MarkdownDescription: "Controls the behavior of the screenshots feature.",
+ Validators: []validator.String{
+ stringvalidator.OneOf("on", "off", "only-on-failure"),
+ },
+ Computed: true,
+ PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
+ },
+ "synthetics_args": schema.ListAttribute{
+ ElementType: types.StringType,
+ Optional: true,
+ MarkdownDescription: "Synthetics agent CLI arguments.",
+ },
+ "ignore_https_errors": schema.BoolAttribute{
+ Optional: true,
+ MarkdownDescription: "Whether to ignore HTTPS errors.",
+ Computed: true,
+ PlanModifiers: []planmodifier.Bool{boolplanmodifier.UseStateForUnknown()},
+ },
+ "playwright_options": jsonObjectSchema("Playwright options."),
+ },
+ }
+}
+
+func icmpMonitorFieldsSchema() schema.Attribute {
+ return schema.SingleNestedAttribute{
+ Optional: true,
+ MarkdownDescription: "ICMP Monitor specific fields",
+ Attributes: map[string]schema.Attribute{
+ "host": schema.StringAttribute{
+ Optional: false,
+ Required: true,
+ MarkdownDescription: "Host to ping; it can be an IP address or a hostname.",
+ },
+ "wait": schema.Int64Attribute{
+ Optional: true,
+ MarkdownDescription: " Wait time in seconds. Default: `1`",
+ PlanModifiers: []planmodifier.Int64{int64planmodifier.UseStateForUnknown()},
+ Computed: true,
+ },
+ },
+ }
+}
+
func jsonObjectSchema(doc string) schema.Attribute {
return schema.StringAttribute{
Optional: true,
@@ -228,6 +302,8 @@ func httpMonitorFieldsSchema() schema.Attribute {
"max_redirects": schema.Int64Attribute{
Optional: true,
MarkdownDescription: "The maximum number of redirects to follow. Default: `0`",
+ PlanModifiers: []planmodifier.Int64{int64planmodifier.UseStateForUnknown()},
+ Computed: true,
},
"mode": schema.StringAttribute{
Optional: true,
@@ -414,6 +490,8 @@ func (v *tfModelV0) toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error)
var http *tfHTTPMonitorFieldsV0
var tcp *tfTCPMonitorFieldsV0
+ var icmp *tfICMPMonitorFieldsV0
+ var browser *tfBrowserMonitorFieldsV0
switch mType := api.Type; mType {
case kbapi.Http:
@@ -428,6 +506,18 @@ func (v *tfModelV0) toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error)
tcp = v.TCP
}
tcp, err = tcp.toTfTCPMonitorFieldsV0(api)
+ case kbapi.Icmp:
+ icmp = &tfICMPMonitorFieldsV0{}
+ if v.ICMP != nil {
+ icmp = v.ICMP
+ }
+ icmp, err = icmp.toTfICMPMonitorFieldsV0(api)
+ case kbapi.Browser:
+ browser = &tfBrowserMonitorFieldsV0{}
+ if v.Browser != nil {
+ browser = v.Browser
+ }
+ browser, err = browser.toTfBrowserMonitorFieldsV0(api)
default:
err = fmt.Errorf("unsupported monitor type: %s", mType)
}
@@ -464,6 +554,8 @@ func (v *tfModelV0) toModelV0(api *kbapi.SyntheticsMonitor) (*tfModelV0, error)
Params: params,
HTTP: http,
TCP: tcp,
+ ICMP: icmp,
+ Browser: browser,
RetestOnFailure: v.RetestOnFailure,
}, nil
}
@@ -488,6 +580,47 @@ func (v *tfTCPMonitorFieldsV0) toTfTCPMonitorFieldsV0(api *kbapi.SyntheticsMonit
}, nil
}
+func (v *tfICMPMonitorFieldsV0) toTfICMPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfICMPMonitorFieldsV0, error) {
+ wait, err := stringToInt64(string(api.Wait))
+ if err != nil {
+ return nil, err
+ }
+ return &tfICMPMonitorFieldsV0{
+ Host: types.StringValue(api.Host),
+ Wait: types.Int64Value(wait),
+ }, nil
+}
+
+func (v *tfBrowserMonitorFieldsV0) toTfBrowserMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfBrowserMonitorFieldsV0, error) {
+
+ var err error
+ playwrightOptions := v.PlaywrightOptions
+ if api.PlaywrightOptions != nil {
+ playwrightOptions, err = toNormalizedValue(api.PlaywrightOptions)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ syntheticsArgs := v.SyntheticsArgs
+ if api.SyntheticsArgs != nil {
+ syntheticsArgs = StringSliceValue(api.SyntheticsArgs)
+ }
+
+ inlineScript := v.InlineScript
+ if api.InlineScript != "" {
+ inlineScript = types.StringValue(api.InlineScript)
+ }
+
+ return &tfBrowserMonitorFieldsV0{
+ InlineScript: inlineScript,
+ Screenshots: types.StringValue(api.Screenshots),
+ SyntheticsArgs: syntheticsArgs,
+ IgnoreHttpsErrors: types.BoolPointerValue(api.IgnoreHttpsErrors),
+ PlaywrightOptions: playwrightOptions,
+ }, nil
+}
+
func (v *tfHTTPMonitorFieldsV0) toTfHTTPMonitorFieldsV0(api *kbapi.SyntheticsMonitor) (*tfHTTPMonitorFieldsV0, error) {
var err error
@@ -572,6 +705,10 @@ func (v *tfModelV0) toMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) {
return v.toHttpMonitorFields()
} else if v.TCP != nil {
return v.toTCPMonitorFields(), dg
+ } else if v.ICMP != nil {
+ return v.toICMPMonitorFields(), dg
+ } else if v.Browser != nil {
+ return v.toBrowserMonitorFields()
}
dg.AddError("Unsupported monitor type config", "one of http,tcp monitor fields is required")
@@ -606,6 +743,14 @@ func (v *tfModelV0) toSyntheticsMonitorConfig() (*kbapi.SyntheticsMonitorConfig,
}, diag.Diagnostics{} //dg
}
+func tfInt64ToString(v types.Int64) string {
+ res := ""
+ if !v.IsUnknown() && !v.IsNull() { // handle omitempty case
+ return strconv.FormatInt(v.ValueInt64(), 10)
+ }
+ return res
+}
+
func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) {
proxyHeaders, dg := toJsonObject(v.HTTP.ProxyHeader)
if dg.HasError() {
@@ -619,11 +764,7 @@ func (v *tfModelV0) toHttpMonitorFields() (kbapi.MonitorFields, diag.Diagnostics
if dg.HasError() {
return nil, dg
}
- maxRedirects := ""
- if !v.HTTP.MaxRedirects.IsUnknown() && !v.HTTP.MaxRedirects.IsNull() { // handle omitempty case
- maxRedirects = strconv.FormatInt(v.HTTP.MaxRedirects.ValueInt64(), 10)
-
- }
+ maxRedirects := tfInt64ToString(v.HTTP.MaxRedirects)
return kbapi.HTTPMonitorFields{
Url: v.HTTP.URL.ValueString(),
SslVerificationMode: v.HTTP.SslVerificationMode.ValueString(),
@@ -653,6 +794,27 @@ func (v *tfModelV0) toTCPMonitorFields() kbapi.MonitorFields {
}
}
+func (v *tfModelV0) toICMPMonitorFields() kbapi.MonitorFields {
+ return kbapi.ICMPMonitorFields{
+ Host: v.ICMP.Host.ValueString(),
+ Wait: tfInt64ToString(v.ICMP.Wait),
+ }
+}
+
+func (v *tfModelV0) toBrowserMonitorFields() (kbapi.MonitorFields, diag.Diagnostics) {
+ playwrightOptions, dg := toJsonObject(v.Browser.PlaywrightOptions)
+ if dg.HasError() {
+ return nil, dg
+ }
+ return kbapi.BrowserMonitorFields{
+ InlineScript: v.Browser.InlineScript.ValueString(),
+ Screenshots: kbapi.ScreenshotOption(v.Browser.Screenshots.ValueString()),
+ SyntheticsArgs: ValueStringSlice(v.Browser.SyntheticsArgs),
+ IgnoreHttpsErrors: v.Browser.IgnoreHttpsErrors.ValueBoolPointer(),
+ PlaywrightOptions: playwrightOptions,
+ }, diag.Diagnostics{} //dg
+}
+
func Map[T, U any](ts []T, f func(T) U) []U {
var us []U
for _, v := range ts {
diff --git a/internal/kibana/synthetics/schema_test.go b/internal/kibana/synthetics/schema_test.go
index a6a160e19..f91dbd2f8 100644
--- a/internal/kibana/synthetics/schema_test.go
+++ b/internal/kibana/synthetics/schema_test.go
@@ -76,6 +76,45 @@ func TestToModelV0(t *testing.T) {
},
},
},
+ {
+ name: "ICMP monitor empty data",
+ input: kbapi.SyntheticsMonitor{
+ Type: kbapi.Icmp,
+ },
+ expected: tfModelV0{
+ ID: types.StringValue("/"),
+ Name: types.StringValue(""),
+ SpaceID: types.StringValue(""),
+ Schedule: types.Int64Value(0),
+ APMServiceName: types.StringValue(""),
+ TimeoutSeconds: types.Int64Value(0),
+ Params: jsontypes.NewNormalizedValue("null"),
+ ICMP: &tfICMPMonitorFieldsV0{
+ Host: types.StringValue(""),
+ Wait: types.Int64Value(0),
+ },
+ },
+ },
+ {
+ name: "Browser monitor empty data",
+ input: kbapi.SyntheticsMonitor{
+ Type: kbapi.Browser,
+ },
+ expected: tfModelV0{
+ ID: types.StringValue("/"),
+ Name: types.StringValue(""),
+ SpaceID: types.StringValue(""),
+ Schedule: types.Int64Value(0),
+ APMServiceName: types.StringValue(""),
+ TimeoutSeconds: types.Int64Value(0),
+ Params: jsontypes.NewNormalizedValue("null"),
+ Browser: &tfBrowserMonitorFieldsV0{
+ InlineScript: types.StringValue(""),
+ Screenshots: types.StringValue(""),
+ PlaywrightOptions: jsontypes.NewNormalizedValue("null"),
+ },
+ },
+ },
{
name: "HTTP monitor",
input: kbapi.SyntheticsMonitor{
@@ -199,6 +238,110 @@ func TestToModelV0(t *testing.T) {
},
},
},
+ {
+ name: "ICMP monitor",
+ input: kbapi.SyntheticsMonitor{
+ Id: "test-id-icmp",
+ Name: "test-name-icmp",
+ Namespace: "default",
+ Enabled: tBool,
+ Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}},
+ Schedule: &kbapi.MonitorScheduleConfig{Number: "5", Unit: "m"},
+ Tags: nil,
+ APMServiceName: "test-service-tcp",
+ Timeout: json.Number("30"),
+ Locations: []kbapi.MonitorLocationConfig{
+ {Label: "test private location", IsServiceManaged: false},
+ },
+ Origin: "origin",
+ Params: kbapi.JsonObject{"param1": "value1"},
+ MaxAttempts: 3,
+ Revision: 1,
+ Ui: kbapi.JsonObject{"is_tls_enabled": false},
+ Type: kbapi.Icmp,
+ SslVerificationMode: "full",
+ SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"},
+ ProxyUrl: "http://proxy.com",
+ Host: "example.com:9200",
+ Wait: "30",
+ },
+ expected: tfModelV0{
+ ID: types.StringValue("default/test-id-icmp"),
+ Name: types.StringValue("test-name-icmp"),
+ SpaceID: types.StringValue("default"),
+ Schedule: types.Int64Value(5),
+ Locations: nil,
+ PrivateLocations: []types.String{types.StringValue("test private location")},
+ Enabled: types.BoolPointerValue(tBool),
+ Tags: nil,
+ Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}},
+ APMServiceName: types.StringValue("test-service-tcp"),
+ TimeoutSeconds: types.Int64Value(30),
+ Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`),
+ ICMP: &tfICMPMonitorFieldsV0{
+ Host: types.StringValue("example.com:9200"),
+ Wait: types.Int64Value(30),
+ },
+ },
+ },
+ {
+ name: "Browser monitor",
+ input: kbapi.SyntheticsMonitor{
+ Id: "test-id-browser",
+ Name: "test-name-browser",
+ Namespace: "default",
+ Enabled: tBool,
+ Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}},
+ Schedule: &kbapi.MonitorScheduleConfig{Number: "5", Unit: "m"},
+ Tags: nil,
+ APMServiceName: "test-service-tcp",
+ Timeout: json.Number("30"),
+ Locations: []kbapi.MonitorLocationConfig{
+ {Label: "test private location", IsServiceManaged: false},
+ },
+ Origin: "origin",
+ Params: kbapi.JsonObject{"param1": "value1"},
+ MaxAttempts: 3,
+ Revision: 1,
+ Ui: kbapi.JsonObject{"is_tls_enabled": false},
+ Type: kbapi.Browser,
+ SslVerificationMode: "full",
+ SslSupportedProtocols: []string{"TLSv1.2", "TLSv1.3"},
+ ProxyUrl: "http://proxy.com",
+ Screenshots: "off",
+ IgnoreHttpsErrors: tBool,
+ InlineScript: `step('Go to https://google.com.co', () => page.goto('https://www.google.com'))`,
+ SyntheticsArgs: []string{"--no-sandbox", "--disable-setuid-sandbox"},
+ PlaywrightOptions: map[string]interface{}{
+ "ignoreHTTPSErrors": false,
+ "httpCredentials": map[string]interface{}{
+ "username": "test",
+ "password": "test",
+ },
+ },
+ },
+ expected: tfModelV0{
+ ID: types.StringValue("default/test-id-browser"),
+ Name: types.StringValue("test-name-browser"),
+ SpaceID: types.StringValue("default"),
+ Schedule: types.Int64Value(5),
+ Locations: nil,
+ PrivateLocations: []types.String{types.StringValue("test private location")},
+ Enabled: types.BoolPointerValue(tBool),
+ Tags: nil,
+ Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}},
+ APMServiceName: types.StringValue("test-service-tcp"),
+ TimeoutSeconds: types.Int64Value(30),
+ Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`),
+ Browser: &tfBrowserMonitorFieldsV0{
+ Screenshots: types.StringValue("off"),
+ IgnoreHttpsErrors: types.BoolPointerValue(tBool),
+ InlineScript: types.StringValue(`step('Go to https://google.com.co', () => page.goto('https://www.google.com'))`),
+ SyntheticsArgs: []types.String{types.StringValue("--no-sandbox"), types.StringValue("--disable-setuid-sandbox")},
+ PlaywrightOptions: jsontypes.NewNormalizedValue(`{"httpCredentials":{"password":"test","username":"test"},"ignoreHTTPSErrors":false}`),
+ },
+ },
+ },
}
for _, tt := range testcases {
@@ -236,6 +379,26 @@ func TestToKibanaAPIRequest(t *testing.T) {
config: kbapi.SyntheticsMonitorConfig{},
},
},
+ {
+ name: "Empty ICMP monitor",
+ input: tfModelV0{
+ ICMP: &tfICMPMonitorFieldsV0{},
+ },
+ expected: kibanaAPIRequest{
+ fields: kbapi.ICMPMonitorFields{},
+ config: kbapi.SyntheticsMonitorConfig{},
+ },
+ },
+ {
+ name: "Empty Browser monitor",
+ input: tfModelV0{
+ Browser: &tfBrowserMonitorFieldsV0{},
+ },
+ expected: kibanaAPIRequest{
+ fields: kbapi.BrowserMonitorFields{},
+ config: kbapi.SyntheticsMonitorConfig{},
+ },
+ },
{
name: "HTTP monitor",
input: tfModelV0{
@@ -348,6 +511,98 @@ func TestToKibanaAPIRequest(t *testing.T) {
},
},
},
+ {
+ name: "ICMP monitor",
+ input: tfModelV0{
+ ID: types.StringValue("test-id-icmp"),
+ Name: types.StringValue("test-name-icmp"),
+ SpaceID: types.StringValue("default"),
+ Schedule: types.Int64Value(5),
+ Locations: []types.String{types.StringValue("us_east")},
+ PrivateLocations: nil,
+ Enabled: types.BoolPointerValue(tBool),
+ Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")},
+ Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}},
+ APMServiceName: types.StringValue("test-service-tcp"),
+ TimeoutSeconds: types.Int64Value(30),
+ Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`),
+ ICMP: &tfICMPMonitorFieldsV0{
+ Host: types.StringValue("example.com:9200"),
+ Wait: types.Int64Value(30),
+ },
+ },
+ expected: kibanaAPIRequest{
+ config: kbapi.SyntheticsMonitorConfig{
+ Name: "test-name-icmp",
+ Schedule: kbapi.MonitorSchedule(5),
+ Locations: []kbapi.MonitorLocation{"us_east"},
+ PrivateLocations: nil,
+ Enabled: tBool,
+ Tags: []string{"tag1", "tag2"},
+ Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}},
+ APMServiceName: "test-service-tcp",
+ Namespace: "default",
+ TimeoutSeconds: 30,
+ Params: kbapi.JsonObject{"param1": "value1"},
+ },
+ fields: kbapi.ICMPMonitorFields{
+ Host: "example.com:9200",
+ Wait: "30",
+ },
+ },
+ },
+ {
+ name: "Browser monitor",
+ input: tfModelV0{
+ ID: types.StringValue("test-id-browser"),
+ Name: types.StringValue("test-name-browser"),
+ SpaceID: types.StringValue("default"),
+ Schedule: types.Int64Value(5),
+ Locations: []types.String{types.StringValue("us_east")},
+ PrivateLocations: nil,
+ Enabled: types.BoolPointerValue(tBool),
+ Tags: []types.String{types.StringValue("tag1"), types.StringValue("tag2")},
+ Alert: &tfAlertConfigV0{Status: &tfStatusConfigV0{Enabled: types.BoolPointerValue(tBool)}},
+ APMServiceName: types.StringValue("test-service-tcp"),
+ TimeoutSeconds: types.Int64Value(30),
+ Params: jsontypes.NewNormalizedValue(`{"param1":"value1"}`),
+ Browser: &tfBrowserMonitorFieldsV0{
+ Screenshots: types.StringValue("off"),
+ IgnoreHttpsErrors: types.BoolPointerValue(tBool),
+ InlineScript: types.StringValue(`step('Go to https://google.com.co', () => page.goto('https://www.google.com'))`),
+ SyntheticsArgs: []types.String{types.StringValue("--no-sandbox"), types.StringValue("--disable-setuid-sandbox")},
+ PlaywrightOptions: jsontypes.NewNormalizedValue(`{"httpCredentials":{"password":"test","username":"test"},"ignoreHTTPSErrors":false}`),
+ },
+ },
+ expected: kibanaAPIRequest{
+ config: kbapi.SyntheticsMonitorConfig{
+ Name: "test-name-browser",
+ Schedule: kbapi.MonitorSchedule(5),
+ Locations: []kbapi.MonitorLocation{"us_east"},
+ PrivateLocations: nil,
+ Enabled: tBool,
+ Tags: []string{"tag1", "tag2"},
+ Alert: &kbapi.MonitorAlertConfig{Status: &kbapi.SyntheticsStatusConfig{Enabled: tBool}},
+ APMServiceName: "test-service-tcp",
+ Namespace: "default",
+ TimeoutSeconds: 30,
+ Params: kbapi.JsonObject{"param1": "value1"},
+ },
+ fields: kbapi.BrowserMonitorFields{
+ Screenshots: "off",
+ IgnoreHttpsErrors: tBool,
+ InlineScript: `step('Go to https://google.com.co', () => page.goto('https://www.google.com'))`,
+ SyntheticsArgs: []string{"--no-sandbox", "--disable-setuid-sandbox"},
+ PlaywrightOptions: map[string]interface{}{
+ "ignoreHTTPSErrors": false,
+ "httpCredentials": map[string]interface{}{
+ "username": "test",
+ "password": "test",
+ },
+ },
+ },
+ },
+ },
}
for _, tt := range testcases {
@@ -433,6 +688,31 @@ func TestToModelV0MergeAttributes(t *testing.T) {
},
},
},
+ {
+ name: "Browser monitor",
+ state: tfModelV0{
+ Browser: &tfBrowserMonitorFieldsV0{
+ InlineScript: types.StringValue("aaa"),
+ SyntheticsArgs: []types.String{types.StringValue("aaa"), types.StringValue("bbb")},
+ },
+ },
+ input: kbapi.SyntheticsMonitor{
+ Type: kbapi.Browser,
+ },
+ expected: tfModelV0{
+ ID: types.StringValue("/"),
+ Name: types.StringValue(""),
+ SpaceID: types.StringValue(""),
+ Schedule: types.Int64Value(0),
+ APMServiceName: types.StringValue(""),
+ TimeoutSeconds: types.Int64Value(0),
+ Browser: &tfBrowserMonitorFieldsV0{
+ InlineScript: types.StringValue("aaa"),
+ SyntheticsArgs: []types.String{types.StringValue("aaa"), types.StringValue("bbb")},
+ Screenshots: types.StringValue(""),
+ },
+ },
+ },
}
for _, tt := range testcases {
diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go
index 54a2f3404..d4cb97855 100644
--- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go
+++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics.go
@@ -46,6 +46,10 @@ const (
ModeAll HttpMonitorMode = "all"
ModeAny = "any"
+
+ ScreenshotOn ScreenshotOption = "on"
+ ScreenshotOff = "off"
+ ScreenshotOnlyOfFailures = "only-on-failure"
)
var plMu sync.Mutex
@@ -65,6 +69,7 @@ type MonitorType string
type MonitorLocation string
type MonitorSchedule int
type HttpMonitorMode string
+type ScreenshotOption string
type JsonObject map[string]interface{}
@@ -90,6 +95,19 @@ type MonitorAlertConfig struct {
Tls *SyntheticsStatusConfig `json:"tls,omitempty"`
}
+type ICMPMonitorFields struct {
+ Host string `json:"host"`
+ Wait string `json:"wait,omitempty"`
+}
+
+type BrowserMonitorFields struct {
+ InlineScript string `json:"inline_script"`
+ Screenshots ScreenshotOption `json:"screenshots,omitempty"`
+ SyntheticsArgs []string `json:"synthetics_args,omitempty"`
+ IgnoreHttpsErrors *bool `json:"ignore_https_errors,omitempty"`
+ PlaywrightOptions JsonObject `json:"playwright_options,omitempty"`
+}
+
type TCPMonitorFields struct {
Host string `json:"host"`
SslVerificationMode string `json:"ssl.verification_mode,omitempty"`
@@ -207,11 +225,20 @@ type SyntheticsMonitor struct {
ProxyUrl string `json:"proxy_url,omitempty"`
SslVerificationMode string `json:"ssl.verification_mode"`
SslSupportedProtocols []string `json:"ssl.supported_protocols"`
+ //tcp and icmp
+ Host string `json:"host,omitempty"`
//tcp
- Host string `json:"host,omitempty"`
ProxyUseLocalResolver *bool `json:"proxy_use_local_resolver,omitempty"`
CheckSend string `json:"check.send,omitempty"`
CheckReceive string `json:"check.receive,omitempty"`
+ //icmp
+ Wait json.Number `json:"wait,omitempty"`
+ //browser
+ Screenshots string `json:"screenshots,omitempty"`
+ IgnoreHttpsErrors *bool `json:"ignore_https_errors,omitempty"`
+ InlineScript string `json:"inline_script"`
+ SyntheticsArgs []string `json:"synthetics_args,omitempty"`
+ PlaywrightOptions JsonObject `json:"playwright_options,omitempty"`
}
type MonitorTypeConfig struct {
@@ -248,6 +275,36 @@ func (f TCPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{}
}
}
+func (f ICMPMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} {
+
+ mType := MonitorTypeConfig{Type: Icmp}
+
+ return struct {
+ SyntheticsMonitorConfig
+ MonitorTypeConfig
+ ICMPMonitorFields
+ }{
+ config,
+ mType,
+ f,
+ }
+}
+
+func (f BrowserMonitorFields) APIRequest(config SyntheticsMonitorConfig) interface{} {
+
+ mType := MonitorTypeConfig{Type: Browser}
+
+ return struct {
+ SyntheticsMonitorConfig
+ MonitorTypeConfig
+ BrowserMonitorFields
+ }{
+ config,
+ mType,
+ f,
+ }
+}
+
type KibanaSyntheticsMonitorAdd func(ctx context.Context, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error)
type KibanaSyntheticsMonitorUpdate func(ctx context.Context, id MonitorID, config SyntheticsMonitorConfig, fields MonitorFields, namespace string) (*SyntheticsMonitor, error)
diff --git a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go
index 351c39e5e..fd527cf17 100644
--- a/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go
+++ b/libs/go-kibana-rest/kbapi/api.kibana_synthetics_test.go
@@ -236,6 +236,129 @@ func (s *KBAPITestSuite) TestKibanaSyntheticsMonitorAPI() {
},
},
},
+ {
+ name: "bare minimum icmp monitor",
+ input: TestConfig{
+ config: SyntheticsMonitorConfig{
+ Name: fmt.Sprintf("test synthetics icmp monitor %s", testUuid),
+ PrivateLocations: []string{location.Label},
+ },
+ fields: ICMPMonitorFields{
+ Host: "localhost",
+ },
+ },
+ update: TestConfig{
+ config: SyntheticsMonitorConfig{},
+ fields: ICMPMonitorFields{
+ Host: "127.0.0.1",
+ },
+ },
+ },
+ {
+ name: "all fields icmp monitor",
+ input: TestConfig{
+ config: SyntheticsMonitorConfig{
+ Name: fmt.Sprintf("test all fields icmp monitor %s", testUuid),
+ Schedule: Every10Minutes,
+ PrivateLocations: []string{location.Label},
+ Enabled: f,
+ Tags: []string{"aaa", "bbb"},
+ Alert: &MonitorAlertConfig{
+ Status: &SyntheticsStatusConfig{Enabled: t},
+ Tls: &SyntheticsStatusConfig{Enabled: f},
+ },
+ APMServiceName: "APMServiceName",
+ TimeoutSeconds: 42,
+ Namespace: space,
+ Params: map[string]interface{}{
+ "param1": "some-params",
+ "my_url": "http://localhost:8080",
+ },
+ RetestOnFailure: f,
+ },
+ fields: ICMPMonitorFields{
+ Host: "localhost",
+ Wait: "10",
+ },
+ },
+ update: TestConfig{
+ config: SyntheticsMonitorConfig{
+ Name: fmt.Sprintf("update all fields icmp monitor %s", testUuid),
+ Schedule: Every30Minutes,
+ },
+ fields: ICMPMonitorFields{
+ Host: "127.0.0.1",
+ Wait: "5",
+ },
+ },
+ },
+ {
+ name: "bare minimum browser monitor",
+ input: TestConfig{
+ config: SyntheticsMonitorConfig{
+ Name: fmt.Sprintf("test synthetics browser monitor %s", testUuid),
+ PrivateLocations: []string{location.Label},
+ },
+ fields: BrowserMonitorFields{
+ InlineScript: `step('Go to https://google.com.co', () => page.goto('https://www.google.com'))`,
+ },
+ },
+ update: TestConfig{
+ config: SyntheticsMonitorConfig{
+ Name: fmt.Sprintf("test synthetics browser monitor %s", testUuid),
+ },
+ fields: BrowserMonitorFields{
+ InlineScript: `step('Go to https://www.google.de', () => page.goto('https://www.google.de'))`,
+ },
+ },
+ },
+ {
+ name: "all fields browser monitor",
+ input: TestConfig{
+ config: SyntheticsMonitorConfig{
+ Name: fmt.Sprintf("test all fields browser monitor %s", testUuid),
+ Schedule: Every10Minutes,
+ PrivateLocations: []string{location.Label},
+ Enabled: f,
+ Tags: []string{"aaa", "bbb"},
+ Alert: &MonitorAlertConfig{
+ Status: &SyntheticsStatusConfig{Enabled: t},
+ Tls: &SyntheticsStatusConfig{Enabled: f},
+ },
+ APMServiceName: "APMServiceName",
+ TimeoutSeconds: 42,
+ Namespace: space,
+ Params: map[string]interface{}{
+ "param1": "some-params",
+ "my_url": "http://localhost:8080",
+ },
+ RetestOnFailure: f,
+ },
+ fields: BrowserMonitorFields{
+ InlineScript: `step('Go to https://google.com.co', () => page.goto('https://www.google.com'))`,
+ Screenshots: ScreenshotOn,
+ SyntheticsArgs: []string{"a", "b"},
+ IgnoreHttpsErrors: t,
+ PlaywrightOptions: map[string]interface{}{
+ "ignoreHTTPSErrors": false,
+ "httpCredentials": map[string]interface{}{
+ "username": "test",
+ "password": "test",
+ },
+ },
+ },
+ },
+ update: TestConfig{
+ config: SyntheticsMonitorConfig{
+ Name: fmt.Sprintf("update all fields browser monitor %s", testUuid),
+ Schedule: Every30Minutes,
+ },
+ fields: BrowserMonitorFields{
+ InlineScript: `step('Go to https://google.de', () => page.goto('https://www.google.de'))`,
+ Screenshots: ScreenshotOff,
+ },
+ },
+ },
}
for _, tc := range testCases {
@@ -298,6 +421,8 @@ func updateDueToKibanaAPIDiff(m *SyntheticsMonitor) {
m.CheckRequestHeaders = nil
m.CheckSend = ""
m.CheckReceive = ""
+ m.InlineScript = ""
+ m.SyntheticsArgs = nil
}
func (s *KBAPITestSuite) TestKibanaSyntheticsPrivateLocationAPI() {
diff --git a/templates/resources/kibana_synthetics_monitor.md.tmpl b/templates/resources/kibana_synthetics_monitor.md.tmpl
index 8700aa561..a5ba09ee1 100644
--- a/templates/resources/kibana_synthetics_monitor.md.tmpl
+++ b/templates/resources/kibana_synthetics_monitor.md.tmpl
@@ -14,6 +14,8 @@ See [API docs](https://www.elastic.co/guide/en/kibana/current/add-monitor-api.ht
## Supported monitor types
* `http`
* `tcp`
+ * `icmp`
+ * `browser`
**NOTE:** Due-to nature of partial update API, reset values to defaults is not supported.
In case you would like to reset an optional monitor value, please set it explicitly or delete and create new monitor.
@@ -34,4 +36,4 @@ Import is supported using the following syntax:
**NOTE:** Not all monitor fields are supported during the import due-to API limitation.
Full field support could be implemented after this [kibana issue](https://github.com/elastic/kibana/issues/189906) is resolved.
-Currently not supported fields during the import: `params`, `retest_on_failure`, `http.proxy_header`, `http.username`, `http.password`, `http.check`, `http.response`, `tcp.check_send`, `tcp.check_receive`
+Currently not supported fields during the import: `params`, `retest_on_failure`, `http.proxy_header`, `http.username`, `http.password`, `http.check`, `http.response`, `tcp.check_send`, `tcp.check_receive` and monitor type `browser`