Skip to content

Commit

Permalink
synthetics icmp and browser monitors support (#772)
Browse files Browse the repository at this point in the history
* feat: add icmp and browser synthetic monitors
 implements #610

---------
Co-authored-by: Toby Brain <[email protected]>
  • Loading branch information
biscout42 authored Sep 18, 2024
1 parent 92f3019 commit b38ec22
Show file tree
Hide file tree
Showing 9 changed files with 949 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
33 changes: 32 additions & 1 deletion docs/resources/kibana_synthetics_monitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -107,6 +111,21 @@ Optional:



<a id="nestedatt--browser"></a>
### 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.


<a id="nestedatt--http"></a>
### Nested Schema for `http`

Expand All @@ -130,6 +149,18 @@ Optional:
- `username` (String) The username for authenticating with the server. The credentials are passed with the request.


<a id="nestedatt--icmp"></a>
### 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`


<a id="nestedatt--tcp"></a>
### Nested Schema for `tcp`

Expand Down Expand Up @@ -157,4 +188,4 @@ terraform import elasticstack_kibana_synthetics_monitor.my_monitor <space id>/<m
**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`
265 changes: 263 additions & 2 deletions internal/kibana/synthetics/acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
url = "http://localhost:5601"
ssl_verification_mode = "full"
ssl_supported_protocols = ["TLSv1.0", "TLSv1.1", "TLSv1.2"]
max_redirects = 10
mode = "any"
ipv4 = true
ipv6 = false
Expand Down Expand Up @@ -165,7 +164,108 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
check_receive = "World Updated"
}
}
`

icmpMonitorConfig = `
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestIcmpMonitorResource - %s"
space_id = "testacc"
schedule = 5
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = true
tags = ["a", "b"]
alert = {
status = {
enabled = true
}
tls = {
enabled = true
}
}
service_name = "test apm service"
timeout = 30
icmp = {
host = "localhost"
}
}
`

icmpMonitorUpdated = `
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestIcmpMonitorResource Updated - %s"
space_id = "testacc"
schedule = 10
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = false
tags = ["c", "d", "e"]
alert = {
status = {
enabled = true
}
tls = {
enabled = false
}
}
service_name = "test apm service"
timeout = 30
icmp = {
host = "google.com"
wait = 10
}
}
`
browserMonitorConfig = `
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestBrowserMonitorResource - %s"
space_id = "testacc"
schedule = 5
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = true
tags = ["a", "b"]
alert = {
status = {
enabled = true
}
tls = {
enabled = true
}
}
service_name = "test apm service"
timeout = 30
browser = {
inline_script = "step('Go to https://google.com.co', () => page.goto('https://www.google.com'))"
}
}
`

browserMonitorUpdated = `
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestBrowserMonitorResource Updated - %s"
space_id = "testacc"
schedule = 10
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = false
tags = ["c", "d", "e"]
alert = {
status = {
enabled = true
}
tls = {
enabled = false
}
}
service_name = "test apm service"
timeout = 30
browser = {
inline_script = "step('Go to https://google.de', () => page.goto('https://www.google.de'))"
synthetics_args = ["--no-sandbox", "--disable-setuid-sandbox"]
screenshots = "off"
ignore_https_errors = true
playwright_options = jsonencode({"httpCredentials":{"password":"test","username":"test"},"ignoreHTTPSErrors":false})
}
}
`
)

Expand Down Expand Up @@ -205,7 +305,7 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) {
resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.0", "TLSv1.0"),
resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.1", "TLSv1.1"),
resource.TestCheckResourceAttr(httpMonitorId, "http.ssl_supported_protocols.2", "TLSv1.2"),
resource.TestCheckResourceAttr(httpMonitorId, "http.max_redirects", "10"),
resource.TestCheckResourceAttr(httpMonitorId, "http.max_redirects", "0"),
resource.TestCheckResourceAttr(httpMonitorId, "http.mode", "any"),
resource.TestCheckResourceAttr(httpMonitorId, "http.ipv4", "true"),
resource.TestCheckResourceAttr(httpMonitorId, "http.ipv6", "false"),
Expand Down Expand Up @@ -251,6 +351,8 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) {
resource.TestCheckResourceAttr(httpMonitorId, "http.ipv6", "true"),
resource.TestCheckResourceAttr(httpMonitorId, "http.proxy_url", "http://localhost"),
resource.TestCheckNoResourceAttr(httpMonitorId, "tcp"),
resource.TestCheckNoResourceAttr(httpMonitorId, "browser"),
resource.TestCheckNoResourceAttr(httpMonitorId, "icmp"),
//check for merge attributes
resource.TestCheckResourceAttr(httpMonitorId, "http.proxy_header", `{"header-name":"header-value-updated"}`),
resource.TestCheckResourceAttr(httpMonitorId, "http.username", "testupdated"),
Expand Down Expand Up @@ -343,6 +445,8 @@ func TestSyntheticMonitorTCPResource(t *testing.T) {
resource.TestCheckResourceAttr(tcpMonitorId, "tcp.proxy_url", "http://localhost"),
resource.TestCheckResourceAttr(tcpMonitorId, "tcp.proxy_use_local_resolver", "false"),
resource.TestCheckNoResourceAttr(tcpMonitorId, "http"),
resource.TestCheckNoResourceAttr(tcpMonitorId, "browser"),
resource.TestCheckNoResourceAttr(tcpMonitorId, "icmp"),
//check for merge attributes
resource.TestCheckResourceAttr(tcpMonitorId, "tcp.check_send", "Hello Updated"),
resource.TestCheckResourceAttr(tcpMonitorId, "tcp.check_receive", "World Updated"),
Expand All @@ -354,6 +458,163 @@ func TestSyntheticMonitorTCPResource(t *testing.T) {
})
}

func TestSyntheticMonitorICMPResource(t *testing.T) {

name := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum)
id := "icmp-monitor"
icmpMonitorId, config := testMonitorConfig(id, icmpMonitorConfig, name)
_, configUpdated := testMonitorConfig(id, icmpMonitorUpdated, name)

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProtoV6ProviderFactories: acctest.Providers,
Steps: []resource.TestStep{

// Create and Read icmp monitor
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion),
Config: config,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(icmpMonitorId, "id"),
resource.TestCheckResourceAttr(icmpMonitorId, "name", "TestIcmpMonitorResource - "+name),
resource.TestCheckResourceAttr(icmpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(icmpMonitorId, "schedule", "5"),
resource.TestCheckResourceAttr(icmpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(icmpMonitorId, "private_locations.0"),
resource.TestCheckResourceAttr(icmpMonitorId, "enabled", "true"),
resource.TestCheckResourceAttr(icmpMonitorId, "tags.#", "2"),
resource.TestCheckResourceAttr(icmpMonitorId, "tags.0", "a"),
resource.TestCheckResourceAttr(icmpMonitorId, "tags.1", "b"),
resource.TestCheckResourceAttr(icmpMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(icmpMonitorId, "alert.tls.enabled", "true"),
resource.TestCheckResourceAttr(icmpMonitorId, "service_name", "test apm service"),
resource.TestCheckResourceAttr(icmpMonitorId, "timeout", "30"),
resource.TestCheckResourceAttr(icmpMonitorId, "icmp.host", "localhost"),
),
},
// ImportState testing
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion),
ResourceName: icmpMonitorId,
ImportState: true,
ImportStateVerify: true,
Config: config,
},
// Update and Read icmp monitor
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion),
ResourceName: icmpMonitorId,
Config: configUpdated,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(icmpMonitorId, "id"),
resource.TestCheckResourceAttr(icmpMonitorId, "name", "TestIcmpMonitorResource Updated - "+name),
resource.TestCheckResourceAttr(icmpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(icmpMonitorId, "schedule", "10"),
resource.TestCheckResourceAttr(icmpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(icmpMonitorId, "private_locations.0"),
resource.TestCheckResourceAttr(icmpMonitorId, "enabled", "false"),
resource.TestCheckResourceAttr(icmpMonitorId, "tags.#", "3"),
resource.TestCheckResourceAttr(icmpMonitorId, "tags.0", "c"),
resource.TestCheckResourceAttr(icmpMonitorId, "tags.1", "d"),
resource.TestCheckResourceAttr(icmpMonitorId, "tags.2", "e"),
resource.TestCheckResourceAttr(icmpMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(icmpMonitorId, "alert.tls.enabled", "false"),
resource.TestCheckResourceAttr(icmpMonitorId, "service_name", "test apm service"),
resource.TestCheckResourceAttr(icmpMonitorId, "timeout", "30"),
resource.TestCheckResourceAttr(icmpMonitorId, "icmp.host", "google.com"),
resource.TestCheckResourceAttr(icmpMonitorId, "icmp.wait", "10"),
resource.TestCheckNoResourceAttr(icmpMonitorId, "http"),
resource.TestCheckNoResourceAttr(icmpMonitorId, "browser"),
resource.TestCheckNoResourceAttr(icmpMonitorId, "tcp"),
),
},
// Delete testing automatically occurs in TestCase

},
})
}

func TestSyntheticMonitorBrowserResource(t *testing.T) {

name := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum)
id := "browser-monitor"
browserMonitorId, config := testMonitorConfig(id, browserMonitorConfig, name)
_, configUpdated := testMonitorConfig(id, browserMonitorUpdated, name)

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProtoV6ProviderFactories: acctest.Providers,
Steps: []resource.TestStep{

// Create and Read browser monitor
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minKibanaVersion),
Config: config,
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(browserMonitorId, "id"),
resource.TestCheckResourceAttr(browserMonitorId, "name", "TestBrowserMonitorResource - "+name),
resource.TestCheckResourceAttr(browserMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(browserMonitorId, "schedule", "5"),
resource.TestCheckResourceAttr(browserMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(browserMonitorId, "private_locations.0"),
resource.TestCheckResourceAttr(browserMonitorId, "enabled", "true"),
resource.TestCheckResourceAttr(browserMonitorId, "tags.#", "2"),
resource.TestCheckResourceAttr(browserMonitorId, "tags.0", "a"),
resource.TestCheckResourceAttr(browserMonitorId, "tags.1", "b"),
resource.TestCheckResourceAttr(browserMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(browserMonitorId, "alert.tls.enabled", "true"),
resource.TestCheckResourceAttr(browserMonitorId, "service_name", "test apm service"),
resource.TestCheckResourceAttr(browserMonitorId, "timeout", "30"),
resource.TestCheckResourceAttr(browserMonitorId, "browser.inline_script", "step('Go to https://google.com.co', () => 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
Expand Down
3 changes: 2 additions & 1 deletion internal/kibana/synthetics/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
Loading

0 comments on commit b38ec22

Please sign in to comment.