diff --git a/GNUmakefile b/GNUmakefile
index 920c116..4f6e645 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -33,11 +33,9 @@ dev:
cd local && TF_LOG=TRACE terraform init -upgrade
fmt:
- go fmt ./checkly
- terraform fmt
-
-doc:
- ./tools/tfplugindocs
+ go fmt ./...
+ terraform fmt -recursive
+# Generate docs
generate:
- go generate ./...
+ cd tools; go generate ./...
diff --git a/checkly/data_source_static_ips.go b/checkly/data_source_static_ips.go
deleted file mode 100644
index c5cbaa5..0000000
--- a/checkly/data_source_static_ips.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package checkly
-
-import (
- "context"
- "fmt"
- "slices"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- checkly "github.com/checkly/checkly-go-sdk"
-)
-
-func dataSourceStaticIPs() *schema.Resource {
- return &schema.Resource{
- Read: dataSourceStaticIPsRead,
- Schema: map[string]*schema.Schema{
- "id": {
- Type: schema.TypeString,
- Computed: true,
- Description: "ID of the static IPs data source.",
- },
- "addresses": {
- Type: schema.TypeSet,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- Computed: true,
- Description: "Static IP addresses for Checkly's runner infrastructure.",
- },
- "locations": {
- Type: schema.TypeSet,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- Optional: true,
- Description: "Specify the locations you want to get static IPs for.",
- },
- "ip_family": {
- Type: schema.TypeString,
- Optional: true,
- Description: "Specify the IP families you want to get static IPs for. Only `IPv6` or `IPv4` are valid options.",
- ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
- value := val.(string)
- if !slices.Contains([]string{"IPv6", "IPv4"}, value) {
- errs = append(errs, fmt.Errorf("%q must be either \"IPv6\" or \"IPv4\"", key))
- }
- return
- },
- },
- },
- }
-}
-
-func dataSourceStaticIPsRead(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- staticIPs, err := client.(checkly.Client).GetStaticIPs(ctx)
- defer cancel()
- if err != nil {
- return fmt.Errorf("dateSourceStaticIPsRead: API error: %w", err)
- }
- return dataSourceFromStaticIPs(staticIPs, d)
-}
-
-func dataSourceFromStaticIPs(s []checkly.StaticIP, d *schema.ResourceData) error {
- var staticIPs []checkly.StaticIP
- var addresses []string
-
- locations := stringsFromSet(d.Get("locations").(*schema.Set))
- ip_family := d.Get("ip_family").(string)
-
- // only locations is set for filtering
- if len(locations) > 0 && ip_family == "" {
- for _, ip := range s {
- if slices.Contains(locations, ip.Region) {
- staticIPs = append(staticIPs, ip)
- }
- }
- // only ip_family is set for filtering
- } else if ip_family != "" && len(locations) == 0 {
- for _, ip := range s {
- if ip_family == "IPv4" && ip.Address.Addr().Is4() {
- staticIPs = append(staticIPs, ip)
- } else if ip_family == "IPv6" && ip.Address.Addr().Is6() {
- staticIPs = append(staticIPs, ip)
- }
- }
- // both region and ip_family are set for filtering
- } else if len(locations) > 0 && ip_family != "" {
- for _, ip := range s {
- if ip_family == "IPv4" && ip.Address.Addr().Is4() && slices.Contains(locations, ip.Region) {
- staticIPs = append(staticIPs, ip)
- } else if ip_family == "IPv6" && ip.Address.Addr().Is6() && slices.Contains(locations, ip.Region) {
- staticIPs = append(staticIPs, ip)
- }
- }
- // no region nor ip_family filters set
- } else {
- staticIPs = s
- }
-
- for _, ip := range staticIPs {
- addresses = append(addresses, ip.Address.String())
- }
-
- d.Set("addresses", addresses)
- d.SetId("checkly_static_ips_data_source_id")
-
- return nil
-}
diff --git a/checkly/data_source_static_ips_test.go b/checkly/data_source_static_ips_test.go
deleted file mode 100644
index 0ba4bf4..0000000
--- a/checkly/data_source_static_ips_test.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package checkly
-
-import (
- "regexp"
- "testing"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
-)
-
-func TestAccStaticIPsAll(t *testing.T) {
- config := `data "checkly_static_ips" "test" {}`
-
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "data.checkly_static_ips.test",
- "addresses.#",
- "162",
- ),
- ),
- },
- })
-}
-
-func TestAccStaticIPsTwoRegionsOnly(t *testing.T) {
- config := `data "checkly_static_ips" "test" {
- locations = ["us-east-1","ap-southeast-1"]
- }`
-
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "data.checkly_static_ips.test",
- "addresses.#",
- "20",
- ),
- ),
- },
- })
-}
-
-func TestAccStaticIPsIPv6Only(t *testing.T) {
- config := `data "checkly_static_ips" "test" {
- ip_family = "IPv6"
- }`
-
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "data.checkly_static_ips.test",
- "addresses.#",
- "22",
- ),
- ),
- },
- })
-}
-
-func TestAccStaticIPsIPv4Only(t *testing.T) {
- config := `data "checkly_static_ips" "test" {
- ip_family = "IPv4"
- }`
-
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "data.checkly_static_ips.test",
- "addresses.#",
- "140",
- ),
- ),
- },
- })
-}
-
-func TestAccStaticIPsIPv6AndOneRegionOnly(t *testing.T) {
- config := `data "checkly_static_ips" "test" {
- ip_family = "IPv6"
- locations = ["us-east-1"]
- }`
-
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "data.checkly_static_ips.test",
- "addresses.#",
- "1",
- ),
- ),
- },
- })
-}
-
-func TestAccStaticIPsIPv4AndOneRegionOnly(t *testing.T) {
- config := `data "checkly_static_ips" "test" {
- ip_family = "IPv4"
- locations = ["us-east-1"]
- }`
-
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "data.checkly_static_ips.test",
- "addresses.#",
- "12",
- ),
- ),
- },
- })
-}
-
-func TestAccStaticIPsInvalidIPFamily(t *testing.T) {
- config := `data "checkly_static_ips" "test" {
- ip_family = "invalid"
- }`
-
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`"ip_family" must be either "IPv6" or "IPv4"`),
- },
- })
-}
diff --git a/checkly/error_with_log.go b/checkly/error_with_log.go
deleted file mode 100644
index 25acb78..0000000
--- a/checkly/error_with_log.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package checkly
-
-import (
- "encoding/json"
- "fmt"
-)
-
-// ErrorLog defines ErrorLog type
-type ErrorLog map[string]interface{}
-
-// ErrorWithLog defines checkly error type
-type ErrorWithLog struct {
- Err string
- Data *ErrorLog
-}
-
-func (e ErrorWithLog) Error() string {
- data, _ := json.Marshal(e.Data)
- return fmt.Sprintf("%s [%s]", e.Err, data)
-}
-
-func makeError(err string, l *ErrorLog) ErrorWithLog {
- return ErrorWithLog{err, l}
-}
diff --git a/checkly/helpers.go b/checkly/helpers.go
deleted file mode 100644
index a9233e4..0000000
--- a/checkly/helpers.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package checkly
-
-import (
- "os"
- "strconv"
- "time"
-)
-
-func apiCallTimeout() time.Duration {
- timeout := os.Getenv("API_CALL_TIMEOUT")
- if timeout != "" {
- v, err := strconv.ParseInt(timeout, 10, 64)
- if err != nil || v < 1 {
- panic("Invalid API_CALL_TIMEOUT value, must be a positive number")
- } else {
- return time.Duration(v) * time.Second
- }
- }
- return 15 * time.Second
-}
diff --git a/checkly/integration_test.go b/checkly/integration_test.go
deleted file mode 100644
index fae3f59..0000000
--- a/checkly/integration_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-//go:build integration
-// +build integration
-
-package checkly
-
-import (
- "os"
- "testing"
-
- "github.com/gruntwork-io/terratest/modules/terraform"
-)
-
-func getAPIKey(t *testing.T) string {
- key := os.Getenv("CHECKLY_API_KEY")
- if key == "" {
- t.Fatal("'CHECKLY_API_KEY' must be set for integration tests")
- }
-
- return key
-}
-
-func getAccountId(t *testing.T) string {
- key := os.Getenv("CHECKLY_ACCOUNT_ID")
- if key == "" {
- t.Fatal("'CHECKLY_ACCOUNT_ID' must be set for integration tests")
- }
- return key
-}
-
-func TestChecklyTerraformIntegration(t *testing.T) {
- t.Parallel()
- terraformOptions := &terraform.Options{
- TerraformDir: "../",
- Vars: map[string]interface{}{
- "checkly_api_key": getAPIKey(t),
- "checkly_account_id": getAccountId(t),
- },
- }
- defer terraform.Destroy(t, terraformOptions)
- terraform.InitAndApply(t, terraformOptions)
- planPath := "./test.plan"
- exit, err := terraform.GetExitCodeForTerraformCommandE(t, terraformOptions, terraform.FormatArgs(terraformOptions, "plan", "--out="+planPath, "-input=false", "-lock=true", "-detailed-exitcode")...)
- if err != nil {
- t.Fatal(err)
- }
- defer os.Remove(planPath)
- if exit != terraform.DefaultSuccessExitCode {
- t.Fatalf("want DefaultSuccessExitCode, got %d", exit)
- }
-}
diff --git a/checkly/provider.go b/checkly/provider.go
deleted file mode 100644
index 797126b..0000000
--- a/checkly/provider.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package checkly
-
-import (
- "fmt"
- "io"
- "os"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- "github.com/checkly/checkly-go-sdk"
-)
-
-// Provider makes the provider available to Terraform.
-func Provider() *schema.Provider {
- return &schema.Provider{
- Schema: map[string]*schema.Schema{
- "api_key": {
- Type: schema.TypeString,
- Required: true,
- DefaultFunc: schema.EnvDefaultFunc("CHECKLY_API_KEY", nil),
- },
- "api_url": {
- Type: schema.TypeString,
- Optional: true,
- DefaultFunc: schema.EnvDefaultFunc("CHECKLY_API_URL", nil),
- },
- "account_id": {
- Type: schema.TypeString,
- Optional: true,
- DefaultFunc: schema.EnvDefaultFunc("CHECKLY_ACCOUNT_ID", nil),
- },
- },
- ResourcesMap: map[string]*schema.Resource{
- "checkly_check": resourceCheck(),
- "checkly_heartbeat": resourceHeartbeat(),
- "checkly_check_group": resourceCheckGroup(),
- "checkly_snippet": resourceSnippet(),
- "checkly_dashboard": resourceDashboard(),
- "checkly_maintenance_windows": resourceMaintenanceWindow(),
- "checkly_alert_channel": resourceAlertChannel(),
- "checkly_trigger_check": resourceTriggerCheck(),
- "checkly_trigger_group": resourceTriggerGroup(),
- "checkly_environment_variable": resourceEnvironmentVariable(),
- "checkly_private_location": resourcePrivateLocation(),
- },
- DataSourcesMap: map[string]*schema.Resource{
- "checkly_static_ips": dataSourceStaticIPs(),
- },
- ConfigureFunc: func(r *schema.ResourceData) (interface{}, error) {
- debugLog := os.Getenv("CHECKLY_DEBUG_LOG")
- var debugOutput io.Writer
- if debugLog != "" {
- debugFile, err := os.OpenFile(debugLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
- if err != nil {
- panic(fmt.Sprintf("can't write to debug log file: %v", err))
- }
- debugOutput = debugFile
- }
-
- apiKey := ""
- switch v := r.Get("api_key").(type) {
- case string:
- apiKey = v
- }
-
- apiUrl := ""
- switch v := r.Get("api_url").(type) {
- case string:
- apiUrl = v
- }
-
- if apiUrl == "" {
- apiUrl = "https://api.checklyhq.com"
- }
-
- client := checkly.NewClient(
- apiUrl,
- apiKey,
- nil,
- debugOutput,
- )
-
- accountId := ""
- switch v := r.Get("account_id").(type) {
- case string:
- accountId = v
- }
- if accountId != "" {
- client.SetAccountId(accountId)
- }
-
- checklyApiSource := os.Getenv("CHECKLY_API_SOURCE")
- if checklyApiSource != "" {
- client.SetChecklySource(checklyApiSource)
- } else {
- client.SetChecklySource("TF")
- }
-
- return client, nil
- },
- }
-}
diff --git a/checkly/provider_test.go b/checkly/provider_test.go
deleted file mode 100644
index 4704aa9..0000000
--- a/checkly/provider_test.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package checkly
-
-import (
- "testing"
-)
-
-func TestProvider(t *testing.T) {
- if err := Provider().InternalValidate(); err != nil {
- t.Fatalf("err: %s", err)
- }
-}
diff --git a/checkly/resource_alert_channel.go b/checkly/resource_alert_channel.go
deleted file mode 100644
index ebcfdec..0000000
--- a/checkly/resource_alert_channel.go
+++ /dev/null
@@ -1,572 +0,0 @@
-package checkly
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- "github.com/checkly/checkly-go-sdk"
-)
-
-const (
- AcFieldEmail = "email"
- AcFieldEmailAddress = "address"
- AcFieldSlack = "slack"
- AcFieldSlackURL = "url"
- AcFieldSlackChannel = "channel"
- AcFieldSMS = "sms"
- AcFieldSMSName = "name"
- AcFieldSMSNumber = "number"
- AcFieldWebhook = "webhook"
- AcFieldWebhookName = "name"
- AcFieldWebhookMethod = "method"
- AcFieldWebhookHeaders = "headers"
- AcFieldWebhookQueryParams = "query_parameters"
- AcFieldWebhookTemplate = "template"
- AcFieldWebhookURL = "url"
- AcFieldWebhookSecret = "webhook_secret"
- AcFieldWebhookType = "webhook_type"
- AcFieldOpsgenie = "opsgenie"
- AcFieldOpsgenieName = "name"
- AcFieldOpsgenieAPIKey = "api_key"
- AcFieldOpsgenieRegion = "region"
- AcFieldOpsgeniePriority = "priority"
- AcFieldPagerduty = "pagerduty"
- AcFieldPagerdutyAccount = "account"
- AcFieldPagerdutyServiceKey = "service_key"
- AcFieldPagerdutyServiceName = "service_name"
- AcFieldSendRecovery = "send_recovery"
- AcFieldSendFailure = "send_failure"
- AcFieldSendDegraded = "send_degraded"
- AcFieldSSLExpiry = "ssl_expiry"
- AcFieldSSLExpiryThreshold = "ssl_expiry_threshold"
- AcFieldCall = "call"
- AcFieldCallName = "name"
- AcFieldCallNumber = "number"
-)
-
-func resourceAlertChannel() *schema.Resource {
- return &schema.Resource{
- Description: "Allows you to define alerting channels for the checks and groups in your account",
- Create: resourceAlertChannelCreate,
- Read: resourceAlertChannelRead,
- Update: resourceAlertChannelUpdate,
- Delete: resourceAlertChannelDelete,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
- Schema: map[string]*schema.Schema{
- AcFieldEmail: {
- Type: schema.TypeSet,
- Optional: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- AcFieldEmailAddress: {
- Type: schema.TypeString,
- Required: true,
- Description: "The email address of this email alert channel.",
- },
- },
- },
- },
- AcFieldSlack: {
- Type: schema.TypeSet,
- Optional: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- AcFieldSlackURL: {
- Type: schema.TypeString,
- Required: true,
- Description: "The Slack webhook URL",
- },
- AcFieldSlackChannel: {
- Type: schema.TypeString,
- Required: true,
- Description: "The name of the alert's Slack channel",
- },
- },
- },
- },
- AcFieldSMS: {
- Type: schema.TypeSet,
- Optional: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- AcFieldSMSName: {
- Type: schema.TypeString,
- Required: true,
- Description: "The name of this alert channel",
- },
- AcFieldSMSNumber: {
- Type: schema.TypeString,
- Required: true,
- Description: "The mobile number to receive the alerts",
- },
- },
- },
- },
- AcFieldCall: {
- Type: schema.TypeSet,
- Optional: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- AcFieldCallName: {
- Type: schema.TypeString,
- Required: true,
- Description: "The name of this alert channel",
- },
- AcFieldCallNumber: {
- Type: schema.TypeString,
- Required: true,
- Description: "The mobile number to receive the alerts",
- },
- },
- },
- },
- AcFieldWebhook: {
- Type: schema.TypeSet,
- Optional: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- AcFieldWebhookName: {
- Type: schema.TypeString,
- Required: true,
- },
- AcFieldWebhookMethod: {
- Type: schema.TypeString,
- Optional: true,
- Default: "POST",
- Description: "(Default `POST`)",
- },
- AcFieldWebhookHeaders: {
- Type: schema.TypeMap,
- Optional: true,
- Computed: true,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- },
- AcFieldWebhookQueryParams: {
- Type: schema.TypeMap,
- Optional: true,
- Computed: true,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- },
- AcFieldWebhookTemplate: {
- Type: schema.TypeString,
- Optional: true,
- },
- AcFieldWebhookURL: {
- Type: schema.TypeString,
- Required: true,
- },
- AcFieldWebhookSecret: {
- Type: schema.TypeString,
- Optional: true,
- },
- AcFieldWebhookType: {
- Type: schema.TypeString,
- Optional: true,
- Description: "Type of the webhook. Possible values are 'WEBHOOK_DISCORD', 'WEBHOOK_FIREHYDRANT', 'WEBHOOK_GITLAB_ALERT', 'WEBHOOK_SPIKESH', 'WEBHOOK_SPLUNK', 'WEBHOOK_MSTEAMS' and 'WEBHOOK_TELEGRAM'.",
- ValidateFunc: func(value interface{}, key string) (warns []string, errs []error) {
- v := value.(string)
- isValid := false
- options := []string{"WEBHOOK_DISCORD", "WEBHOOK_FIREHYDRANT", "WEBHOOK_GITLAB_ALERT", "WEBHOOK_SPIKESH", "WEBHOOK_SPLUNK", "WEBHOOK_MSTEAMS", "WEBHOOK_TELEGRAM"}
- for _, option := range options {
- if v == option {
- isValid = true
- }
- }
- if !isValid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got %s", key, options, v))
- }
- return warns, errs
- },
- },
- },
- },
- },
- AcFieldOpsgenie: {
- Type: schema.TypeSet,
- Optional: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- AcFieldOpsgenieName: {
- Type: schema.TypeString,
- Required: true,
- },
- AcFieldOpsgenieAPIKey: {
- Type: schema.TypeString,
- Required: true,
- },
- AcFieldOpsgenieRegion: {
- Type: schema.TypeString,
- Required: true,
- },
- AcFieldOpsgeniePriority: {
- Type: schema.TypeString,
- Required: true,
- },
- },
- },
- },
- AcFieldPagerduty: {
- Type: schema.TypeSet,
- Optional: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- AcFieldPagerdutyServiceKey: {
- Type: schema.TypeString,
- Required: true,
- },
- AcFieldPagerdutyServiceName: {
- Type: schema.TypeString,
- Optional: true,
- },
- AcFieldPagerdutyAccount: {
- Type: schema.TypeString,
- Optional: true,
- },
- },
- },
- },
- AcFieldSendRecovery: {
- Type: schema.TypeBool,
- Optional: true,
- Default: true,
- Description: "(Default `true`)",
- },
- AcFieldSendFailure: {
- Type: schema.TypeBool,
- Optional: true,
- Default: true,
- Description: "(Default `true`)",
- },
- AcFieldSendDegraded: {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- Description: "(Default `false`)",
- },
- AcFieldSSLExpiry: {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- Description: "(Default `false`)",
- },
- AcFieldSSLExpiryThreshold: {
- Type: schema.TypeInt,
- Optional: true,
- Default: 30,
- ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
- min := 1
- max := 30
- v := val.(int)
- if v < min || v > max {
- errs = append(errs, fmt.Errorf("%q must be between %d and %d, got: %d", key, min, max, v))
- }
- return warns, errs
- },
- Description: "Value must be between 1 and 30 (Default `30`)",
- },
- },
- }
-}
-
-func resourceAlertChannelCreate(d *schema.ResourceData, client interface{}) error {
- ac, err := alertChannelFromResourceData(d)
- if err != nil {
- return makeError("resourceAlertChannelCreate.1", &ErrorLog{"err": err.Error()})
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- resp, err := client.(checkly.Client).CreateAlertChannel(ctx, ac)
- if err != nil {
- cjson, _ := json.Marshal(ac.GetConfig())
- return makeError("resourceAlertChannelCreate.2", &ErrorLog{
- "err": err.Error(),
- "type": ac.Type,
- "config": string(cjson),
- })
- }
- d.SetId(fmt.Sprintf("%d", resp.ID))
- return resourceAlertChannelRead(d, client)
-}
-
-func resourceAlertChannelRead(d *schema.ResourceData, client interface{}) error {
- ID, err := resourceIDToInt(d.Id())
- if err != nil {
- return makeError("resourceAlertChannelRead.1", &ErrorLog{"err": err.Error()})
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- ac, err := client.(checkly.Client).GetAlertChannel(ctx, ID)
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- //if resource is deleted remotely, then mark it as
- //successfully gone by unsetting it's ID
- d.SetId("")
- return nil
- }
- return makeError("resourceAlertChannelRead.2", &ErrorLog{"err": err.Error()})
- }
- return resourceDataFromAlertChannel(ac, d)
-}
-
-func resourceAlertChannelUpdate(d *schema.ResourceData, client interface{}) error {
- ac, err := alertChannelFromResourceData(d)
- if err != nil {
- return makeError("resourceAlertChannelUpdate.1", &ErrorLog{"err": err.Error()})
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- _, err = client.(checkly.Client).UpdateAlertChannel(ctx, ac.ID, ac)
- if err != nil {
- return makeError("resourceAlertChannelUpdate.2", &ErrorLog{"err": err.Error()})
- }
- d.SetId(fmt.Sprintf("%d", ac.ID))
- return resourceAlertChannelRead(d, client)
-}
-
-func resourceAlertChannelDelete(d *schema.ResourceData, client interface{}) error {
- ID, err := resourceIDToInt(d.Id())
- if err != nil {
- return makeError("resourceAlertChannelDelete.1", &ErrorLog{"err": err.Error()})
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- if err := client.(checkly.Client).DeleteAlertChannel(ctx, ID); err != nil {
- return makeError("resourceAlertChannelDelete.2", &ErrorLog{"err": err.Error()})
- }
- return nil
-}
-
-func resourceDataFromAlertChannel(it *checkly.AlertChannel, d *schema.ResourceData) error {
- d.Set(AcFieldEmail, setFromEmail(it.Email))
- d.Set(AcFieldSMS, setFromSMS(it.SMS))
- d.Set(AcFieldCall, setFromCall(it.CALL))
- d.Set(AcFieldSlack, setFromSlack(it.Slack))
- d.Set(AcFieldWebhook, setFromWebhook(it.Webhook))
- d.Set(AcFieldOpsgenie, setFromOpsgenie(it.Opsgenie))
- d.Set(AcFieldPagerduty, setFromPagerduty(it.Pagerduty))
- if it.SendRecovery != nil {
- d.Set(AcFieldSendRecovery, *it.SendRecovery)
- }
- if it.SendFailure != nil {
- d.Set(AcFieldSendFailure, *it.SendFailure)
- }
- if it.SendDegraded != nil {
- d.Set(AcFieldSendDegraded, *it.SendDegraded)
- }
- if it.SSLExpiry != nil {
- d.Set(AcFieldSSLExpiry, *it.SSLExpiry)
- }
- if it.SSLExpiryThreshold != nil {
- d.Set(AcFieldSSLExpiryThreshold, *it.SSLExpiryThreshold)
- }
- return nil
-}
-
-func alertChannelFromResourceData(d *schema.ResourceData) (checkly.AlertChannel, error) {
- ac := checkly.AlertChannel{}
- ID, err := resourceIDToInt(d.Id())
- if err != nil {
- return ac, makeError("alertChannelFromResourceData.1", &ErrorLog{"err": err.Error()})
- }
- if err == nil {
- ac.ID = ID
- }
-
- sendRecovery := d.Get(AcFieldSendRecovery).(bool)
- ac.SendRecovery = &sendRecovery
-
- sendFailure := d.Get(AcFieldSendFailure).(bool)
- ac.SendFailure = &sendFailure
-
- sndDegraded := d.Get(AcFieldSendDegraded).(bool)
- ac.SendDegraded = &sndDegraded
-
- sslExpiry := d.Get(AcFieldSSLExpiry).(bool)
- ac.SSLExpiry = &sslExpiry
-
- if v, ok := d.GetOk(AcFieldSSLExpiryThreshold); ok {
- i := v.(int)
- ac.SSLExpiryThreshold = &i
- }
-
- fields := []string{AcFieldEmail, AcFieldSMS, AcFieldCall, AcFieldSlack, AcFieldWebhook, AcFieldOpsgenie, AcFieldPagerduty}
- setCount := 0
- for _, field := range fields {
- cfgSet := (d.Get(field)).(*schema.Set)
- if cfgSet.Len() > 0 {
- ac.Type = strings.ToUpper(field)
- c, err := alertChannelConfigFromSet(ac.Type, cfgSet)
- if err != nil {
- return ac, makeError("alertChannelFromResourceData.2", &ErrorLog{"err": err.Error()})
- }
- ac.SetConfig(c)
- setCount++
- }
- }
- if setCount > 1 {
- return ac, makeError("Alert-Channel config can't contain more than one Channel", nil)
- }
- return ac, nil
-}
-
-func alertChannelConfigFromSet(channelType string, s *schema.Set) (interface{}, error) {
- if s.Len() == 0 {
- return nil, nil
- }
- cfg := s.List()[0].(map[string]interface{})
- switch channelType {
- case checkly.AlertTypeEmail:
- return &checkly.AlertChannelEmail{
- Address: cfg[AcFieldEmailAddress].(string),
- }, nil
- case checkly.AlertTypeSMS:
- return &checkly.AlertChannelSMS{
- Name: cfg[AcFieldSMSName].(string),
- Number: cfg[AcFieldSMSNumber].(string),
- }, nil
- case checkly.AlertTypeCall:
- return &checkly.AlertChannelCall{
- Name: cfg[AcFieldCallName].(string),
- Number: cfg[AcFieldCallNumber].(string),
- }, nil
- case checkly.AlertTypeSlack:
- return &checkly.AlertChannelSlack{
- Channel: cfg[AcFieldSlackChannel].(string),
- WebhookURL: cfg[AcFieldSlackURL].(string),
- }, nil
- case checkly.AlertTypeOpsgenie:
- return &checkly.AlertChannelOpsgenie{
- Name: cfg[AcFieldOpsgenieName].(string),
- APIKey: cfg[AcFieldOpsgenieAPIKey].(string),
- Region: cfg[AcFieldOpsgenieRegion].(string),
- Priority: cfg[AcFieldOpsgeniePriority].(string),
- }, nil
- case checkly.AlertTypePagerduty:
- return &checkly.AlertChannelPagerduty{
- Account: cfg[AcFieldPagerdutyAccount].(string),
- ServiceKey: cfg[AcFieldPagerdutyServiceKey].(string),
- ServiceName: cfg[AcFieldPagerdutyServiceName].(string),
- }, nil
- case checkly.AlertTypeWebhook:
- return &checkly.AlertChannelWebhook{
- Name: cfg[AcFieldWebhookName].(string),
- Method: cfg[AcFieldWebhookMethod].(string),
- Template: cfg[AcFieldWebhookTemplate].(string),
- URL: cfg[AcFieldWebhookURL].(string),
- WebhookSecret: cfg[AcFieldWebhookSecret].(string),
- WebhookType: cfg[AcFieldWebhookType].(string),
- Headers: keyValuesFromMap(cfg[AcFieldWebhookHeaders].(tfMap)),
- QueryParameters: keyValuesFromMap(cfg[AcFieldWebhookQueryParams].(tfMap)),
- }, nil
- }
- return nil, makeError("alertChannelConfigFromSet:unkownType", nil)
-}
-
-func setFromEmail(cfg *checkly.AlertChannelEmail) []tfMap {
- if cfg == nil {
- return []tfMap{}
- }
- return []tfMap{
- {
- AcFieldEmailAddress: cfg.Address,
- },
- }
-}
-
-func setFromSMS(cfg *checkly.AlertChannelSMS) []tfMap {
- if cfg == nil {
- return []tfMap{}
- }
- return []tfMap{
- {
- AcFieldSMSName: cfg.Name,
- AcFieldSMSNumber: cfg.Number,
- },
- }
-}
-
-func setFromCall(cfg *checkly.AlertChannelCall) []tfMap {
- if cfg == nil {
- return []tfMap{}
- }
- return []tfMap{
- {
- AcFieldCallName: cfg.Name,
- AcFieldCallNumber: cfg.Number,
- },
- }
-}
-
-func setFromSlack(cfg *checkly.AlertChannelSlack) []tfMap {
- if cfg == nil {
- return []tfMap{}
- }
- return []tfMap{
- {
- AcFieldSlackChannel: cfg.Channel,
- AcFieldSlackURL: cfg.WebhookURL,
- },
- }
-}
-
-func setFromWebhook(cfg *checkly.AlertChannelWebhook) []tfMap {
- if cfg == nil {
- return []tfMap{}
- }
- return []tfMap{
- {
- AcFieldWebhookName: cfg.Name,
- AcFieldWebhookMethod: cfg.Method,
- AcFieldWebhookHeaders: mapFromKeyValues(cfg.Headers),
- AcFieldWebhookQueryParams: mapFromKeyValues(cfg.QueryParameters),
- AcFieldWebhookTemplate: cfg.Template,
- AcFieldWebhookURL: cfg.URL,
- AcFieldWebhookSecret: cfg.WebhookSecret,
- AcFieldWebhookType: cfg.WebhookType,
- },
- }
-}
-
-func setFromOpsgenie(cfg *checkly.AlertChannelOpsgenie) []tfMap {
- if cfg == nil {
- return []tfMap{}
- }
- return []tfMap{
- {
- AcFieldOpsgenieName: cfg.Name,
- AcFieldOpsgenieAPIKey: cfg.APIKey,
- AcFieldOpsgenieRegion: cfg.Region,
- AcFieldOpsgeniePriority: cfg.Priority,
- },
- }
-}
-
-func setFromPagerduty(cfg *checkly.AlertChannelPagerduty) []tfMap {
- if cfg == nil {
- return []tfMap{}
- }
- return []tfMap{
- {
- AcFieldPagerdutyAccount: cfg.Account,
- AcFieldPagerdutyServiceKey: cfg.ServiceKey,
- AcFieldPagerdutyServiceName: cfg.ServiceName,
- },
- }
-}
diff --git a/checkly/resource_alert_channel_test.go b/checkly/resource_alert_channel_test.go
deleted file mode 100644
index 884c0c0..0000000
--- a/checkly/resource_alert_channel_test.go
+++ /dev/null
@@ -1,202 +0,0 @@
-package checkly
-
-import (
- "fmt"
- "regexp"
- "testing"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
-)
-
-func TestAccEmail(t *testing.T) {
- t.Parallel()
- accTestCase(t, []resource.TestStep{
- {
- Config: `resource "checkly_alert_channel" "t1" {
- email {
- address = "info@example.com"
- }
- send_recovery = false
- send_failure = false
- send_degraded = false
- ssl_expiry = false
- ssl_expiry_threshold = 10
- }`,
- },
- })
-}
-
-func TestAccSlack(t *testing.T) {
- t.Parallel()
- accTestCase(t, []resource.TestStep{
- {
- Config: `resource "checkly_alert_channel" "slack_ac" {
- slack {
- channel = "checkly_alerts"
- url = "https://hooks.slack.com/services/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"
- }
- send_recovery = true
- send_failure = true
- send_degraded = false
- ssl_expiry = true
- ssl_expiry_threshold = 11
- }`,
- },
- })
-}
-
-func TestAccSMS(t *testing.T) {
- t.Parallel()
- accTestCase(t, []resource.TestStep{
- {
- Config: `resource "checkly_alert_channel" "sms_ac" {
- sms {
- name = "smsalerts"
- number = "4917512345678"
- }
- }`,
- },
- })
-}
-
-func TestAccOpsgenie(t *testing.T) {
- t.Parallel()
- accTestCase(t, []resource.TestStep{
- {
- Config: `resource "checkly_alert_channel" "opsgenie_ac" {
- opsgenie {
- name = "opsalert"
- api_key = "key1"
- region = "EU"
- priority = "P1"
- }
- }`,
- },
- })
-}
-
-func TestAccPagerduty(t *testing.T) {
- t.Parallel()
- accTestCase(t, []resource.TestStep{
- {
- Config: `resource "checkly_alert_channel" "pagerduty_ac" {
- pagerduty {
- account = "checkly"
- service_key = "key1"
- service_name = "pdalert"
- }
- }`,
- },
- })
-}
-
-func TestAccWebhook(t *testing.T) {
- t.Parallel()
- accTestCase(t, []resource.TestStep{
- {
- Config: `resource "checkly_alert_channel" "webhook_ac" {
- webhook {
- name = "webhhookalerts"
- method = "get"
- headers = {
- X-HEADER-1 = "foo"
- }
- query_parameters = {
- query1 = "bar"
- }
- template = "tmpl"
- url = "https://example.com/webhook"
- webhook_secret = "foo-secret"
- }
- }`,
- },
- })
-}
-
-func TestAccFail(t *testing.T) {
- t.Parallel()
- cases := []struct {
- Config string
- Error string
- }{
- {
- Config: `resource "checkly_alert_channel" "t1" {
- email { }
- }`,
- Error: `The argument "address" is required`,
- },
- {
- Config: `resource "checkly_alert_channel" "t1" {
- sms {
- }
- }`,
- Error: `The argument "number" is required`,
- },
- {
- Config: `resource "checkly_alert_channel" "t1" {
- slack {
- }
- }`,
- Error: `The argument "channel" is required`,
- },
- {
- Config: `resource "checkly_alert_channel" "t1" {
- slack {
- }
- }`,
- Error: `Missing required argument`,
- },
- {
- Config: `resource "checkly_alert_channel" "t1" {
- webhook {
- }
- }`,
- Error: `The argument "name" is required`,
- },
- {
- Config: `resource "checkly_alert_channel" "t1" {
- webhook {
- }
- }`,
- Error: `The argument "url" is required`,
- },
- {
- Config: `resource "checkly_alert_channel" "t1" {
- opsgenie {
- }
- }`,
- Error: `The argument "api_key" is required`,
- },
- {
- Config: `resource "checkly_alert_channel" "t1" {
- opsgenie {
- }
- }`,
- Error: `The argument "priority" is required`,
- },
- {
- Config: `resource "checkly_alert_channel" "t1" {
- opsgenie {
- }
- }`,
- Error: `The argument "region" is required`,
- },
- {
- Config: `resource "checkly_alert_channel" "t1" {
- pagerduty {
- }
- }`,
- Error: `The argument "service_key" is required`,
- },
- }
- for key, tc := range cases {
- t.Run(fmt.Sprintf("%d", key), func(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: tc.Config,
- ExpectError: regexp.MustCompile(tc.Error),
- },
- })
- })
- }
-}
diff --git a/checkly/resource_check.go b/checkly/resource_check.go
deleted file mode 100644
index 7d390b8..0000000
--- a/checkly/resource_check.go
+++ /dev/null
@@ -1,1115 +0,0 @@
-package checkly
-
-import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "sort"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- "github.com/checkly/checkly-go-sdk"
-)
-
-// tfMap is a shorthand alias for convenience; Terraform uses this type a *lot*.
-type tfMap = map[string]interface{}
-
-func resourceCheck() *schema.Resource {
- return &schema.Resource{
- Create: resourceCheckCreate,
- Read: resourceCheckRead,
- Update: resourceCheckUpdate,
- Delete: resourceCheckDelete,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
- Description: "Checks allows you to monitor key webapp flows, backend API's and set up alerting, so you get a notification when things break or slow down.",
- Schema: map[string]*schema.Schema{
- "name": {
- Type: schema.TypeString,
- Required: true,
- Description: "The name of the check.",
- },
- "type": {
- Type: schema.TypeString,
- Required: true,
- Description: "The type of the check. Possible values are `API`, `BROWSER`, and `MULTI_STEP`.",
- },
- "frequency": {
- Type: schema.TypeInt,
- Required: true,
- ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
- v := val.(int)
- valid := false
- validFreqs := []int{0, 1, 2, 5, 10, 15, 30, 60, 120, 180, 360, 720, 1440}
- for _, i := range validFreqs {
- if v == i {
- valid = true
- }
- }
- if !valid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got %d", key, validFreqs, v))
- }
- return warns, errs
- },
- Description: "The frequency in minutes to run the check. Possible values are `0`, `1`, `2`, `5`, `10`, `15`, `30`, `60`, `120`, `180`, `360`, `720`, and `1440`.",
- },
- "frequency_offset": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "This property only valid for API high frequency checks. To create a hight frequency check, the property `frequency` must be `0` and `frequency_offset` could be `10`, `20` or `30`.",
- },
- "activated": {
- Type: schema.TypeBool,
- Required: true,
- Description: "Determines if the check is running or not. Possible values `true`, and `false`.",
- },
- "muted": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Determines if any notifications will be sent out when a check fails/degrades/recovers.",
- },
- "should_fail": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Allows to invert the behaviour of when a check is considered to fail. Allows for validating error status like 404.",
- },
- "run_parallel": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Determines if the check should run in all selected locations in parallel or round-robin.",
- },
- "locations": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- Description: "An array of one or more data center locations where to run the this check. (Default [\"us-east-1\"])",
- },
- "script": {
- Type: schema.TypeString,
- Optional: true,
- Default: "",
- Description: "A valid piece of Node.js JavaScript code describing a browser interaction with the Puppeteer/Playwright framework or a reference to an external JavaScript file.",
- },
- "degraded_response_time": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 15000,
- ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
- // https://checklyhq.com/docs/api-checks/limits/
- v := val.(int)
- if v < 0 || v > 30000 {
- errs = append(errs, fmt.Errorf("%q must be 0-30000 ms, got %d", key, v))
- }
- return warns, errs
- },
- Description: "The response time in milliseconds starting from which a check should be considered degraded. Possible values are between 0 and 30000. (Default `15000`).",
- },
- "max_response_time": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 30000,
- ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
- v := val.(int)
- // https://checklyhq.com/docs/api-checks/limits/
- if v < 0 || v > 30000 {
- errs = append(errs, fmt.Errorf("%q must be 0-30000 ms, got: %d", key, v))
- }
- return warns, errs
- },
- Description: "The response time in milliseconds starting from which a check should be considered failing. Possible values are between 0 and 30000. (Default `30000`).",
- },
- "environment_variables": {
- Type: schema.TypeMap,
- Optional: true,
- Deprecated: "The property `environment_variables` is deprecated and will be removed in a future version. Consider using the new `environment_variable` list.",
- Description: "Key/value pairs for setting environment variables during check execution. These are only relevant for browser checks. Use global environment variables whenever possible.",
- },
- "environment_variable": {
- Type: schema.TypeList,
- Optional: true,
- Description: "Key/value pairs for setting environment variables during check execution, add locked = true to keep value hidden, add secret = true to create a secret variable. These are only relevant for browser checks. Use global environment variables whenever possible.",
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "key": {
- Type: schema.TypeString,
- Required: true,
- },
- "value": {
- Type: schema.TypeString,
- Required: true,
- },
- "locked": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- },
- "secret": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- },
- },
- },
- },
- "double_check": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Setting this to `true` will trigger a retry when a check fails from the failing region and another, randomly selected region before marking the check as failed.",
- Deprecated: "The property `double_check` is deprecated and will be removed in a future version. To enable retries for failed check runs, use the `retry_strategy` property instead.",
- },
- "tags": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- Description: "A list of tags for organizing and filtering checks.",
- },
- "ssl_check": {
- Type: schema.TypeBool,
- Optional: true,
- Deprecated: "The property `ssl_check` is deprecated and it's ignored by the Checkly Public API. It will be removed in a future version.",
- Description: "Determines if the SSL certificate should be validated for expiry.",
- },
- "ssl_check_domain": {
- Type: schema.TypeString,
- Optional: true,
- Description: "A valid fully qualified domain name (FQDN) to check its SSL certificate.",
- },
- "setup_snippet_id": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "An ID reference to a snippet to use in the setup phase of an API check.",
- },
- "teardown_snippet_id": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "An ID reference to a snippet to use in the teardown phase of an API check.",
- },
- "local_setup_script": {
- Type: schema.TypeString,
- Optional: true,
- Description: "A valid piece of Node.js code to run in the setup phase.",
- },
- "local_teardown_script": {
- Type: schema.TypeString,
- Optional: true,
- Description: "A valid piece of Node.js code to run in the teardown phase.",
- },
- "runtime_id": {
- Type: schema.TypeString,
- Optional: true,
- Default: nil,
- Description: "The id of the runtime to use for this check.",
- },
- "alert_channel_subscription": {
- Type: schema.TypeList,
- Optional: true,
- Description: "An array of channel IDs and whether they're activated or not. If you don't set at least one alert subscription for your check, we won't be able to alert you in case something goes wrong with it.",
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "channel_id": {
- Type: schema.TypeInt,
- Required: true,
- },
- "activated": {
- Type: schema.TypeBool,
- Required: true,
- },
- },
- },
- },
- "private_locations": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- Description: "An array of one or more private locations slugs.",
- },
- "alert_settings": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "escalation_type": {
- Type: schema.TypeString,
- Optional: true,
- Description: "Determines what type of escalation to use. Possible values are `RUN_BASED` or `TIME_BASED`.",
- },
- "run_based_escalation": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "failed_run_threshold": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "After how many failed consecutive check runs an alert notification should be sent. Possible values are between 1 and 5. (Default `1`).",
- },
- },
- },
- },
- "time_based_escalation": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "minutes_failing_threshold": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "After how many minutes after a check starts failing an alert should be sent. Possible values are `5`, `10`, `15`, and `30`. (Default `5`).",
- },
- },
- },
- },
- "reminders": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "amount": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "How many reminders to send out after the initial alert notification. Possible values are `0`, `1`, `2`, `3`, `4`, `5`, and `100000`",
- },
- "interval": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 5,
- Description: "Possible values are `5`, `10`, `15`, and `30`. (Default `5`).",
- },
- },
- },
- },
- "parallel_run_failure_threshold": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "enabled": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- Description: "Applicable only for checks scheduled in parallel in multiple locations.",
- },
- "percentage": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 10,
- Description: "Possible values are `10`, `20`, `30`, `40`, `50`, `60`, `70`, `80`, `100`, and `100`. (Default `10`).",
- },
- },
- },
- },
- "ssl_certificates": {
- Type: schema.TypeSet,
- Optional: true,
- Deprecated: "This property is deprecated and it's ignored by the Checkly Public API. It will be removed in a future version.",
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "enabled": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Determines if alert notifications should be sent for expiring SSL certificates. Possible values `true`, and `false`. (Default `false`).",
- },
- "alert_threshold": {
- Type: schema.TypeInt,
- Optional: true,
- ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
- v := val.(int)
- valid := false
- validFreqs := []int{3, 7, 14, 30}
- for _, i := range validFreqs {
- if v == i {
- valid = true
- }
- }
- if !valid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got: %d", key, validFreqs, v))
- }
- return warns, errs
- },
- Description: "How long before SSL certificate expiry to send alerts. Possible values `3`, `7`, `14`, `30`. (Default `3`).",
- },
- },
- Description: "At what interval the reminders should be sent.",
- },
- },
- },
- },
- },
- "use_global_alert_settings": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "When true, the account level alert settings will be used, not the alert setting defined on this check.",
- },
- "request": {
- Type: schema.TypeSet,
- Optional: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "method": {
- Type: schema.TypeString,
- Optional: true,
- Default: "GET",
- Description: "The HTTP method to use for this API check. Possible values are `GET`, `POST`, `PUT`, `HEAD`, `DELETE`, `PATCH`. (Default `GET`).",
- },
- "url": {
- Type: schema.TypeString,
- Required: true,
- },
- "follow_redirects": {
- Type: schema.TypeBool,
- Optional: true,
- },
- "skip_ssl": {
- Type: schema.TypeBool,
- Optional: true,
- },
- "headers": {
- Type: schema.TypeMap,
- Optional: true,
- Computed: true,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- },
- "query_parameters": {
- Type: schema.TypeMap,
- Optional: true,
- Computed: true,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- },
- "body": {
- Type: schema.TypeString,
- Optional: true,
- Description: "The body of the request.",
- },
- "body_type": {
- Type: schema.TypeString,
- Optional: true,
- Default: "NONE",
- Description: "The `Content-Type` header of the request. Possible values `NONE`, `JSON`, `FORM`, `RAW`, and `GRAPHQL`.",
- ValidateFunc: func(value interface{}, key string) (warns []string, errs []error) {
- v := value.(string)
- isValid := false
- options := []string{"NONE", "JSON", "FORM", "RAW", "GRAPHQL"}
- for _, option := range options {
- if v == option {
- isValid = true
- }
- }
- if !isValid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got %s", key, options, v))
- }
- return warns, errs
- },
- },
- "assertion": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "source": {
- Type: schema.TypeString,
- Required: true,
- Description: "The source of the asserted value. Possible values `STATUS_CODE`, `JSON_BODY`, `HEADERS`, `TEXT_BODY`, and `RESPONSE_TIME`.",
- },
- "property": {
- Type: schema.TypeString,
- Optional: true,
- },
- "comparison": {
- Type: schema.TypeString,
- Required: true,
- Description: "The type of comparison to be executed between expected and actual value of the assertion. Possible values `EQUALS`, `NOT_EQUALS`, `HAS_KEY`, `NOT_HAS_KEY`, `HAS_VALUE`, `NOT_HAS_VALUE`, `IS_EMPTY`, `NOT_EMPTY`, `GREATER_THAN`, `LESS_THAN`, `CONTAINS`, `NOT_CONTAINS`, `IS_NULL`, and `NOT_NULL`.",
- },
- "target": {
- Type: schema.TypeString,
- Optional: true,
- },
- },
- },
- Description: "A request can have multiple assertions.",
- },
- "basic_auth": {
- Type: schema.TypeSet,
- MaxItems: 1,
- Optional: true,
- Computed: true,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "username": {
- Type: schema.TypeString,
- Required: true,
- },
- "password": {
- Type: schema.TypeString,
- Required: true,
- },
- },
- },
- Description: "Set up HTTP basic authentication (username & password).",
- },
- "ip_family": {
- Type: schema.TypeString,
- Optional: true,
- Default: "IPv4",
- Description: "IP Family to be used when executing the api check. The value can be either IPv4 or IPv6.",
- ValidateFunc: func(value interface{}, key string) (warns []string, errs []error) {
- v := value.(string)
- isValid := false
- options := []string{"IPv4", "IPv6"}
- for _, option := range options {
- if v == option {
- isValid = true
- }
- }
- if !isValid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got %s", key, options, v))
- }
- return warns, errs
- },
- },
- },
- },
- Description: "An API check might have one request config.",
- },
- "group_id": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "The id of the check group this check is part of.",
- },
- "group_order": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "The position of this check in a check group. It determines in what order checks are run when a group is triggered from the API or from CI/CD.",
- },
- "retry_strategy": {
- Type: schema.TypeSet,
- Optional: true,
- Computed: true,
- MaxItems: 1,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- Description: "A strategy for retrying failed check runs.",
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "type": {
- Type: schema.TypeString,
- Required: true,
- Description: "Determines which type of retry strategy to use. Possible values are `FIXED`, `LINEAR`, or `EXPONENTIAL`.",
- },
- "base_backoff_seconds": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 60,
- Description: "The number of seconds to wait before the first retry attempt.",
- },
- "max_retries": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 2,
- Description: "The maximum number of times to retry the check. Value must be between 1 and 10.",
- },
- "max_duration_seconds": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 600,
- Description: "The total amount of time to continue retrying the check (maximum 600 seconds).",
- },
- "same_region": {
- Type: schema.TypeBool,
- Optional: true,
- Default: true,
- Description: "Whether retries should be run in the same region as the initial check run.",
- },
- },
- },
- },
- },
- }
-}
-
-func resourceCheckCreate(d *schema.ResourceData, client interface{}) error {
- check, err := checkFromResourceData(d)
- if err != nil {
- return fmt.Errorf("translation error: %w", err)
- }
-
- validationErr := validateRuntimeSupport(check, client)
- if validationErr != nil {
- return validationErr
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- newCheck, err := client.(checkly.Client).CreateCheck(ctx, check)
-
- if err != nil {
- checkJSON, _ := json.Marshal(check)
- return fmt.Errorf("API error 1: %w, Check: %s", err, string(checkJSON))
- }
- d.SetId(newCheck.ID)
- return resourceCheckRead(d, client)
-}
-
-func resourceCheckRead(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- check, err := client.(checkly.Client).GetCheck(ctx, d.Id())
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- //if resource is deleted remotely, then mark it as
- //successfully gone by unsetting it's ID
- d.SetId("")
- return nil
- }
- return fmt.Errorf("API error 2: %w", err)
- }
- return resourceDataFromCheck(check, d)
-}
-
-func resourceCheckUpdate(d *schema.ResourceData, client interface{}) error {
- check, err := checkFromResourceData(d)
-
- if err != nil {
- return fmt.Errorf("translation error: %w", err)
- }
-
- validationErr := validateRuntimeSupport(check, client)
- if validationErr != nil {
- return validationErr
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- _, err = client.(checkly.Client).UpdateCheck(ctx, check.ID, check)
- if err != nil {
- checkJSON, _ := json.Marshal(check)
- return fmt.Errorf("API error 3: Couldn't update check, Error: %w, \nCheck: %s", err, checkJSON)
- }
- d.SetId(check.ID)
- return resourceCheckRead(d, client)
-}
-
-func resourceCheckDelete(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- if err := client.(checkly.Client).DeleteCheck(ctx, d.Id()); err != nil {
- return fmt.Errorf("API error 4: Couldn't delete Check %s, Error: %w", d.Id(), err)
- }
- return nil
-}
-
-func resourceDataFromCheck(c *checkly.Check, d *schema.ResourceData) error {
- d.Set("name", c.Name)
- d.Set("type", c.Type)
- d.Set("activated", c.Activated)
- d.Set("muted", c.Muted)
- d.Set("should_fail", c.ShouldFail)
- d.Set("run_parallel", c.RunParallel)
- d.Set("locations", c.Locations)
- d.Set("script", c.Script)
- d.Set("degraded_response_time", c.DegradedResponseTime)
- d.Set("max_response_time", c.MaxResponseTime)
- d.Set("double_check", c.DoubleCheck)
- d.Set("setup_snippet_id", c.SetupSnippetID)
- d.Set("teardown_snippet_id", c.TearDownSnippetID)
- d.Set("local_setup_script", c.LocalSetupScript)
- d.Set("local_teardown_script", c.LocalTearDownScript)
-
- sort.Strings(c.Tags)
- d.Set("tags", c.Tags)
-
- d.Set("frequency", c.Frequency)
- if c.Frequency == 0 {
- d.Set("frequency_offset", c.FrequencyOffset)
- }
-
- if c.RuntimeID != nil {
- d.Set("runtime_id", *c.RuntimeID)
- }
-
- // ssl_check_domain is only supported for Browser checks
- if c.Type == "BROWSER" && c.SSLCheckDomain != "" {
- d.Set("ssl_check_domain", c.SSLCheckDomain)
- }
-
- environmentVariables := environmentVariablesFromSet(d.Get("environment_variable").([]interface{}))
- if len(environmentVariables) > 0 {
- d.Set("environment_variable", c.EnvironmentVariables)
- } else if err := d.Set("environment_variables", setFromEnvVars(c.EnvironmentVariables)); err != nil {
- return fmt.Errorf("error setting environment variables for resource %s: %s", d.Id(), err)
- }
-
- if err := d.Set("alert_settings", setFromAlertSettings(c.AlertSettings)); err != nil {
- return fmt.Errorf("error setting alert settings for resource %s: %w", d.Id(), err)
- }
- d.Set("use_global_alert_settings", c.UseGlobalAlertSettings)
-
- if c.Type == checkly.TypeAPI {
- err := d.Set("request", setFromRequest(c.Request))
- if err != nil {
- return fmt.Errorf("error setting request for resource %s: %w", d.Id(), err)
- }
- }
- d.Set("group_id", c.GroupID)
- d.Set("group_order", c.GroupOrder)
- d.Set("private_locations", c.PrivateLocations)
- d.Set("alert_channel_subscription", c.AlertChannelSubscriptions)
- d.Set("retry_strategy", setFromRetryStrategy(c.RetryStrategy))
- d.SetId(d.Id())
- return nil
-}
-
-func setFromEnvVars(evs []checkly.EnvironmentVariable) tfMap {
- var s = tfMap{}
- for _, ev := range evs {
- s[ev.Key] = ev.Value
- }
- return s
-}
-
-func setFromAlertSettings(as checkly.AlertSettings) []tfMap {
- if as.EscalationType == checkly.RunBased {
- return []tfMap{
- {
- "escalation_type": as.EscalationType,
- "run_based_escalation": []tfMap{
- {
- "failed_run_threshold": as.RunBasedEscalation.FailedRunThreshold,
- },
- },
- "reminders": []tfMap{
- {
- "amount": as.Reminders.Amount,
- "interval": as.Reminders.Interval,
- },
- },
- "parallel_run_failure_threshold": []tfMap{
- {
- "enabled": as.ParallelRunFailureThreshold.Enabled,
- "percentage": as.ParallelRunFailureThreshold.Percentage,
- },
- },
- },
- }
- } else {
- return []tfMap{
- {
- "escalation_type": as.EscalationType,
- "time_based_escalation": []tfMap{
- {
- "minutes_failing_threshold": as.TimeBasedEscalation.MinutesFailingThreshold,
- },
- },
- "reminders": []tfMap{
- {
- "amount": as.Reminders.Amount,
- "interval": as.Reminders.Interval,
- },
- },
- "parallel_run_failure_threshold": []tfMap{
- {
- "enabled": as.ParallelRunFailureThreshold.Enabled,
- "percentage": as.ParallelRunFailureThreshold.Percentage,
- },
- },
- },
- }
- }
-}
-
-func setFromRequest(r checkly.Request) []tfMap {
- s := tfMap{}
- s["method"] = r.Method
- s["url"] = r.URL
- s["follow_redirects"] = r.FollowRedirects
- s["skip_ssl"] = r.SkipSSL
- s["body"] = r.Body
- s["body_type"] = r.BodyType
- s["headers"] = mapFromKeyValues(r.Headers)
- s["query_parameters"] = mapFromKeyValues(r.QueryParameters)
- s["assertion"] = setFromAssertions(r.Assertions)
- s["basic_auth"] = setFromBasicAuth(r.BasicAuth)
- s["ip_family"] = r.IPFamily
- return []tfMap{s}
-}
-
-func setFromAssertions(assertions []checkly.Assertion) []tfMap {
- s := make([]tfMap, len(assertions))
- for i, a := range assertions {
- as := tfMap{}
- as["source"] = a.Source
- as["property"] = a.Property
- as["comparison"] = a.Comparison
- as["target"] = a.Target
- s[i] = as
- }
- return s
-}
-
-func mapFromKeyValues(kvs []checkly.KeyValue) tfMap {
- var s = tfMap{}
- for _, item := range kvs {
- s[item.Key] = item.Value
- }
- return s
-}
-
-func setFromBasicAuth(b *checkly.BasicAuth) []tfMap {
- if b == nil {
- return []tfMap{}
- }
- return []tfMap{
- {
- "username": b.Username,
- "password": b.Password,
- },
- }
-}
-
-func setFromRetryStrategy(rs *checkly.RetryStrategy) []tfMap {
- if rs == nil {
- return []tfMap{}
- }
- return []tfMap{
- {
- "type": rs.Type,
- "base_backoff_seconds": rs.BaseBackoffSeconds,
- "max_retries": rs.MaxRetries,
- "max_duration_seconds": rs.MaxDurationSeconds,
- "same_region": rs.SameRegion,
- },
- }
-}
-
-func checkFromResourceData(d *schema.ResourceData) (checkly.Check, error) {
- check := checkly.Check{
- ID: d.Id(),
- Name: d.Get("name").(string),
- Type: d.Get("type").(string),
- Frequency: d.Get("frequency").(int),
- Activated: d.Get("activated").(bool),
- Muted: d.Get("muted").(bool),
- ShouldFail: d.Get("should_fail").(bool),
- RunParallel: d.Get("run_parallel").(bool),
- Locations: stringsFromSet(d.Get("locations").(*schema.Set)),
- Script: d.Get("script").(string),
- DegradedResponseTime: d.Get("degraded_response_time").(int),
- MaxResponseTime: d.Get("max_response_time").(int),
- DoubleCheck: d.Get("double_check").(bool),
- Tags: stringsFromSet(d.Get("tags").(*schema.Set)),
- SSLCheck: d.Get("ssl_check").(bool),
- SSLCheckDomain: d.Get("ssl_check_domain").(string),
- SetupSnippetID: int64(d.Get("setup_snippet_id").(int)),
- TearDownSnippetID: int64(d.Get("teardown_snippet_id").(int)),
- LocalSetupScript: d.Get("local_setup_script").(string),
- LocalTearDownScript: d.Get("local_teardown_script").(string),
- AlertSettings: alertSettingsFromSet(d.Get("alert_settings").([]interface{})),
- UseGlobalAlertSettings: d.Get("use_global_alert_settings").(bool),
- GroupID: int64(d.Get("group_id").(int)),
- GroupOrder: d.Get("group_order").(int),
- AlertChannelSubscriptions: alertChannelSubscriptionsFromSet(d.Get("alert_channel_subscription").([]interface{})),
- RetryStrategy: retryStrategyFromSet(d.Get("retry_strategy").(*schema.Set)),
- }
-
- runtimeId := d.Get("runtime_id").(string)
- if runtimeId == "" {
- check.RuntimeID = nil
- } else {
- check.RuntimeID = &runtimeId
- }
-
- environmentVariables, err := getResourceEnvironmentVariables(d)
- if err != nil {
- return checkly.Check{}, err
- }
- check.EnvironmentVariables = environmentVariables
-
- privateLocations := stringsFromSet(d.Get("private_locations").(*schema.Set))
- check.PrivateLocations = &privateLocations
-
- if check.Type == checkly.TypeAPI {
- // this will prevent subsequent apply from causing a tf config change in browser checks
- check.Request = requestFromSet(d.Get("request").(*schema.Set))
- check.FrequencyOffset = d.Get("frequency_offset").(int)
-
- if check.Frequency == 0 && (check.FrequencyOffset != 10 && check.FrequencyOffset != 20 && check.FrequencyOffset != 30) {
- return check, errors.New("when property frequency is 0, frequency_offset must be 10, 20 or 30")
- }
-
- if check.SSLCheckDomain != "" {
- return check, errors.New("ssl_check_domain is allowed only for Browser checks")
- }
- }
-
- if check.Type == checkly.TypeBrowser && check.Frequency == 0 {
- return check, errors.New("property frequency could only be 0 for API checks")
- }
-
- return check, nil
-}
-
-func stringsFromSet(s *schema.Set) []string {
- r := make([]string, s.Len())
- for i, item := range s.List() {
- r[i] = item.(string)
- }
- return r
-}
-
-func assertionsFromSet(s *schema.Set) []checkly.Assertion {
- r := make([]checkly.Assertion, s.Len())
- for i, item := range s.List() {
- res := item.(tfMap)
- r[i] = checkly.Assertion{
- Source: res["source"].(string),
- Property: res["property"].(string),
- Comparison: res["comparison"].(string),
- Target: res["target"].(string),
- }
- }
- return r
-}
-
-func basicAuthFromSet(s *schema.Set) *checkly.BasicAuth {
- if s.Len() == 0 {
- return nil
- }
- res := s.List()[0].(tfMap)
- return &checkly.BasicAuth{
- Username: res["username"].(string),
- Password: res["password"].(string),
- }
-}
-
-func alertSettingsFromSet(s []interface{}) checkly.AlertSettings {
- if len(s) == 0 {
- return checkly.AlertSettings{
- EscalationType: checkly.RunBased,
- RunBasedEscalation: checkly.RunBasedEscalation{
- FailedRunThreshold: 1,
- },
- }
- }
- res := s[0].(tfMap)
- alertSettings := checkly.AlertSettings{
- EscalationType: res["escalation_type"].(string),
- Reminders: remindersFromSet(res["reminders"].([]interface{})),
- ParallelRunFailureThreshold: parallelRunFailureThresholdFromSet(res["parallel_run_failure_threshold"].([]interface{})),
- }
-
- if alertSettings.EscalationType == checkly.RunBased {
- alertSettings.RunBasedEscalation = runBasedEscalationFromSet(res["run_based_escalation"].([]interface{}))
- } else {
- alertSettings.TimeBasedEscalation = timeBasedEscalationFromSet(res["time_based_escalation"].([]interface{}))
- }
-
- return alertSettings
-}
-
-func retryStrategyFromSet(s *schema.Set) *checkly.RetryStrategy {
- if s.Len() == 0 {
- return nil
- }
- res := s.List()[0].(tfMap)
- return &checkly.RetryStrategy{
- Type: res["type"].(string),
- BaseBackoffSeconds: res["base_backoff_seconds"].(int),
- MaxRetries: res["max_retries"].(int),
- MaxDurationSeconds: res["max_duration_seconds"].(int),
- SameRegion: res["same_region"].(bool),
- }
-}
-
-func alertChannelSubscriptionsFromSet(s []interface{}) []checkly.AlertChannelSubscription {
- res := []checkly.AlertChannelSubscription{}
- if len(s) == 0 {
- return res
- }
- for _, it := range s {
- tm := it.(tfMap)
- chid := tm["channel_id"].(int)
- activated := tm["activated"].(bool)
- res = append(res, checkly.AlertChannelSubscription{
- Activated: activated,
- ChannelID: int64(chid),
- })
- }
- return res
-}
-
-func environmentVariablesFromSet(s []interface{}) []checkly.EnvironmentVariable {
- res := []checkly.EnvironmentVariable{}
- if len(s) == 0 {
- return res
- }
- for _, it := range s {
- tm := it.(tfMap)
- key := tm["key"].(string)
- value := tm["value"].(string)
- locked := tm["locked"].(bool)
- secret := tm["secret"].(bool)
- res = append(res, checkly.EnvironmentVariable{
- Key: key,
- Value: value,
- Locked: locked,
- Secret: secret,
- })
- }
-
- return res
-}
-
-func runBasedEscalationFromSet(s []interface{}) checkly.RunBasedEscalation {
- if len(s) == 0 {
- return checkly.RunBasedEscalation{}
- }
- res := s[0].(tfMap)
- return checkly.RunBasedEscalation{
- FailedRunThreshold: res["failed_run_threshold"].(int),
- }
-}
-
-func timeBasedEscalationFromSet(s []interface{}) checkly.TimeBasedEscalation {
- if len(s) == 0 {
- return checkly.TimeBasedEscalation{}
- }
- res := s[0].(tfMap)
- return checkly.TimeBasedEscalation{
- MinutesFailingThreshold: res["minutes_failing_threshold"].(int),
- }
-}
-
-func remindersFromSet(s []interface{}) checkly.Reminders {
- if len(s) == 0 {
- return checkly.Reminders{}
- }
- res := s[0].(tfMap)
- return checkly.Reminders{
- Amount: res["amount"].(int),
- Interval: res["interval"].(int),
- }
-}
-
-func parallelRunFailureThresholdFromSet(s []interface{}) checkly.ParallelRunFailureThreshold {
- if len(s) == 0 {
- return checkly.ParallelRunFailureThreshold{}
- }
- res := s[0].(tfMap)
- return checkly.ParallelRunFailureThreshold{
- Enabled: res["enabled"].(bool),
- Percentage: res["percentage"].(int),
- }
-}
-
-func requestFromSet(s *schema.Set) checkly.Request {
- if s.Len() == 0 {
- return checkly.Request{}
- }
- res := s.List()[0].(tfMap)
- return checkly.Request{
- Method: res["method"].(string),
- URL: res["url"].(string),
- FollowRedirects: res["follow_redirects"].(bool),
- SkipSSL: res["skip_ssl"].(bool),
- Body: res["body"].(string),
- BodyType: res["body_type"].(string),
- Headers: keyValuesFromMap(res["headers"].(tfMap)),
- QueryParameters: keyValuesFromMap(res["query_parameters"].(tfMap)),
- Assertions: assertionsFromSet(res["assertion"].(*schema.Set)),
- BasicAuth: basicAuthFromSet(res["basic_auth"].(*schema.Set)),
- IPFamily: res["ip_family"].(string),
- }
-}
-
-func envVarsFromMap(m map[string]interface{}) []checkly.EnvironmentVariable {
- r := make([]checkly.EnvironmentVariable, 0, len(m))
- for k, v := range m {
- s, ok := v.(string)
- if !ok {
- panic(fmt.Errorf("could not convert environment variable value %v to string", v))
- }
-
- r = append(r, checkly.EnvironmentVariable{
- Key: k,
- Value: s,
- })
-
- }
- return r
-}
-
-func keyValuesFromMap(m map[string]interface{}) []checkly.KeyValue {
- r := make([]checkly.KeyValue, 0, len(m))
- for k, v := range m {
- s, ok := v.(string)
- if !ok {
- panic(fmt.Errorf("could not convert environment variable value %v to string", v))
- }
- r = append(r, checkly.KeyValue{
- Key: k,
- Value: s,
- })
- }
- return r
-}
-
-func getResourceEnvironmentVariables(d *schema.ResourceData) ([]checkly.EnvironmentVariable, error) {
- deprecatedEnvironmentVariables := envVarsFromMap(d.Get("environment_variables").(tfMap))
- environmentVariables := environmentVariablesFromSet(d.Get("environment_variable").([]interface{}))
-
- if len(environmentVariables) > 0 && len(deprecatedEnvironmentVariables) > 0 {
- return nil, errors.New("can't use both \"environment_variables\" and \"environment_variable\" on checkly_check_group resource")
- }
-
- if len(environmentVariables) > 0 {
- return environmentVariables, nil
- }
-
- return deprecatedEnvironmentVariables, nil
-}
-
-func validateRuntimeSupport(check checkly.Check, client interface{}) error {
- // If the check has a runtime ID set, then validate that the runtime supports multistep.
- // Note that if the runtime ID is coming from the account defaults or group, then we don't validate it.
- // Adding validation there as well would be a nice improvement, though.
- if check.Type == "MULTI_STEP" && check.RuntimeID != nil {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- checkRuntime, err := client.(checkly.Client).GetRuntime(ctx, *check.RuntimeID)
-
- if err != nil {
- return fmt.Errorf("API error while fetching runtimes: %w", err)
- }
-
- if !checkRuntime.MultiStepSupport {
- return fmt.Errorf("runtime %s does not support MULTI_STEP checks", *check.RuntimeID)
- }
- }
- return nil
-}
diff --git a/checkly/resource_check_group.go b/checkly/resource_check_group.go
deleted file mode 100644
index e43df9c..0000000
--- a/checkly/resource_check_group.go
+++ /dev/null
@@ -1,621 +0,0 @@
-package checkly
-
-import (
- "context"
- "fmt"
- "sort"
- "strconv"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- checkly "github.com/checkly/checkly-go-sdk"
-)
-
-func resourceCheckGroup() *schema.Resource {
- return &schema.Resource{
- Create: resourceCheckGroupCreate,
- Read: resourceCheckGroupRead,
- Update: resourceCheckGroupUpdate,
- Delete: resourceCheckGroupDelete,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
- Description: "Check groups allow you to group together a set of related checks, which can also share default settings for various attributes.",
- Schema: map[string]*schema.Schema{
- "name": {
- Type: schema.TypeString,
- Required: true,
- Description: "The name of the check group.",
- },
- "concurrency": {
- Type: schema.TypeInt,
- Required: true,
- Description: "Determines how many checks are run concurrently when triggering a check group from CI/CD or through the API.",
- },
- "activated": {
- Type: schema.TypeBool,
- Required: true,
- Description: "Determines if the checks in the group are running or not.",
- },
- "muted": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Determines if any notifications will be sent out when a check in this group fails and/or recovers.",
- },
- "run_parallel": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Determines if the checks in the group should run in all selected locations in parallel or round-robin.",
- },
- "locations": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- Description: "An array of one or more data center locations where to run the checks.",
- },
- "private_locations": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- Description: "An array of one or more private locations slugs.",
- },
- "environment_variables": {
- Type: schema.TypeMap,
- Optional: true,
- Deprecated: "The property `environment_variables` is deprecated and will be removed in a future version. Consider using the new `environment_variable` list.",
- Description: "Key/value pairs for setting environment variables during check execution. These are only relevant for browser checks. Use global environment variables whenever possible.",
- },
- "environment_variable": {
- Type: schema.TypeList,
- Optional: true,
- Description: "Key/value pairs for setting environment variables during check execution, add locked = true to keep value hidden, add secret = true to create a secret variable. These are only relevant for browser checks. Use global environment variables whenever possible.",
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "key": {
- Type: schema.TypeString,
- Required: true,
- },
- "value": {
- Type: schema.TypeString,
- Required: true,
- },
- "locked": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- },
- "secret": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- },
- },
- },
- },
- "double_check": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Setting this to `true` will trigger a retry when a check fails from the failing region and another, randomly selected region before marking the check as failed.",
- Deprecated: "The property `double_check` is deprecated and will be removed in a future version. To enable retries for failed check runs, use the `retry_strategy` property instead.",
- },
- "tags": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- Description: "Tags for organizing and filtering checks.",
- },
- "setup_snippet_id": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "An ID reference to a snippet to use in the setup phase of an API check.",
- },
- "teardown_snippet_id": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "An ID reference to a snippet to use in the teardown phase of an API check.",
- },
- "local_setup_script": {
- Type: schema.TypeString,
- Optional: true,
- Description: "A valid piece of Node.js code to run in the setup phase of an API check in this group.",
- },
- "local_teardown_script": {
- Type: schema.TypeString,
- Optional: true,
- Description: "A valid piece of Node.js code to run in the teardown phase of an API check in this group.",
- },
- "runtime_id": {
- Type: schema.TypeString,
- Optional: true,
- Default: nil,
- Description: "The id of the runtime to use for this group.",
- },
- "alert_channel_subscription": {
- Type: schema.TypeList,
- Optional: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "channel_id": {
- Type: schema.TypeInt,
- Required: true,
- },
- "activated": {
- Type: schema.TypeBool,
- Required: true,
- },
- },
- },
- },
- "alert_settings": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "escalation_type": {
- Type: schema.TypeString,
- Optional: true,
- Default: checkly.RunBased,
- Description: "Determines what type of escalation to use. Possible values are `RUN_BASED` or `TIME_BASED`.",
- },
- "run_based_escalation": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "failed_run_threshold": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "After how many failed consecutive check runs an alert notification should be sent. Possible values are between 1 and 5. (Default `1`).",
- },
- },
- },
- },
- "time_based_escalation": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "minutes_failing_threshold": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "After how many minutes after a check starts failing an alert should be sent. Possible values are `5`, `10`, `15`, and `30`. (Default `5`).",
- },
- },
- },
- },
- "reminders": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "amount": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "How many reminders to send out after the initial alert notification. Possible values are `0`, `1`, `2`, `3`, `4`, `5`, and `100000`",
- },
- "interval": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 5,
- Description: "Possible values are `5`, `10`, `15`, and `30`. (Default `5`).",
- },
- },
- },
- },
- "parallel_run_failure_threshold": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "enabled": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- Description: "Applicable only for checks scheduled in parallel in multiple locations.",
- },
- "percentage": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 10,
- Description: "Possible values are `10`, `20`, `30`, `40`, `50`, `60`, `70`, `80`, `100`, and `100`. (Default `10`).",
- },
- },
- },
- },
- "ssl_certificates": {
- Type: schema.TypeSet,
- Optional: true,
- Deprecated: "This property is deprecated and it's ignored by the Checkly Public API. It will be removed in a future version.",
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "enabled": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Determines if alert notifications should be sent for expiring SSL certificates.",
- },
- "alert_threshold": {
- Type: schema.TypeInt,
- Optional: true,
- ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
- v := val.(int)
- valid := false
- validFreqs := []int{3, 7, 14, 30}
- for _, i := range validFreqs {
- if v == i {
- valid = true
- }
- }
- if !valid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got: %d", key, validFreqs, v))
- }
- return warns, errs
- },
- Description: "At what moment in time to start alerting on SSL certificates. Possible values `3`, `7`, `14`, `30`. (Default `3`).",
- },
- },
- },
- },
- },
- },
- },
- "use_global_alert_settings": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "When true, the account level alert settings will be used, not the alert setting defined on this check group.",
- },
- "api_check_defaults": {
- Type: schema.TypeSet,
- MaxItems: 1,
- Optional: true,
- Computed: true,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{
- tfMap{
- "url": "",
- "headers": []tfMap{},
- "query_parameters": []tfMap{},
- "basic_auth": tfMap{},
- }}, nil
- },
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "url": {
- Type: schema.TypeString,
- Required: true,
- Description: "The base url for this group which you can reference with the `GROUP_BASE_URL` variable in all group checks.",
- },
- "headers": {
- Type: schema.TypeMap,
- Optional: true,
- Computed: true,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- },
- "query_parameters": {
- Type: schema.TypeMap,
- Optional: true,
- Computed: true,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- },
- "assertion": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "source": {
- Type: schema.TypeString,
- Required: true,
- Description: "The source of the asserted value. Possible values `STATUS_CODE`, `JSON_BODY`, `HEADERS`, `TEXT_BODY`, and `RESPONSE_TIME`.",
- },
- "property": {
- Type: schema.TypeString,
- Optional: true,
- },
- "comparison": {
- Type: schema.TypeString,
- Required: true,
- Description: "The type of comparison to be executed between expected and actual value of the assertion. Possible values `EQUALS`, `NOT_EQUALS`, `HAS_KEY`, `NOT_HAS_KEY`, `HAS_VALUE`, `NOT_HAS_VALUE`, `IS_EMPTY`, `NOT_EMPTY`, `GREATER_THAN`, `LESS_THAN`, `CONTAINS`, `NOT_CONTAINS`, `IS_NULL`, and `NOT_NULL`.",
- },
- "target": {
- Type: schema.TypeString,
- Required: true,
- },
- },
- },
- },
- "basic_auth": {
- Type: schema.TypeSet,
- MaxItems: 1,
- Optional: true,
- Computed: true,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "username": {
- Type: schema.TypeString,
- Required: true,
- },
- "password": {
- Type: schema.TypeString,
- Required: true,
- },
- },
- },
- },
- },
- },
- },
- "retry_strategy": {
- Type: schema.TypeSet,
- Optional: true,
- Computed: true,
- MaxItems: 1,
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- Description: "A strategy for retrying failed check runs.",
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "type": {
- Type: schema.TypeString,
- Required: true,
- Description: "Determines which type of retry strategy to use. Possible values are `FIXED`, `LINEAR`, or `EXPONENTIAL`.",
- },
- "base_backoff_seconds": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 60,
- Description: "The number of seconds to wait before the first retry attempt.",
- },
- "max_retries": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 2,
- Description: "The maximum number of times to retry the check. Value must be between 1 and 10.",
- },
- "max_duration_seconds": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 600,
- Description: "The total amount of time to continue retrying the check (maximum 600 seconds).",
- },
- "same_region": {
- Type: schema.TypeBool,
- Optional: true,
- Default: true,
- Description: "Whether retries should be run in the same region as the initial check run.",
- },
- },
- },
- },
- },
- }
-}
-
-func resourceCheckGroupCreate(d *schema.ResourceData, client interface{}) error {
- group, err := checkGroupFromResourceData(d)
- if err != nil {
- return fmt.Errorf("translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- gotGroup, err := client.(checkly.Client).CreateGroup(ctx, group)
- if err != nil {
- return fmt.Errorf("API error11: %w", err)
- }
- d.SetId(fmt.Sprintf("%d", gotGroup.ID))
- return resourceCheckGroupRead(d, client)
-}
-
-func resourceCheckGroupRead(d *schema.ResourceData, client interface{}) error {
- ID, err := strconv.ParseInt(d.Id(), 10, 64)
- if err != nil {
- return fmt.Errorf("ID %s is not numeric: %w", d.Id(), err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- group, err := client.(checkly.Client).GetGroup(ctx, ID)
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- //if resource is deleted remotely, then mark it as
- //successfully gone by unsetting it's ID
- d.SetId("")
- return nil
- }
- return fmt.Errorf("API error12: %w", err)
- }
- return resourceDataFromCheckGroup(group, d)
-}
-
-func resourceCheckGroupUpdate(d *schema.ResourceData, client interface{}) error {
- group, err := checkGroupFromResourceData(d)
- if err != nil {
- return fmt.Errorf("translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- _, err = client.(checkly.Client).UpdateGroup(ctx, group.ID, group)
- if err != nil {
- return fmt.Errorf("API error13: %w", err)
- }
- d.SetId(fmt.Sprintf("%d", group.ID))
- return resourceCheckGroupRead(d, client)
-}
-
-func resourceCheckGroupDelete(d *schema.ResourceData, client interface{}) error {
- ID, err := strconv.ParseInt(d.Id(), 10, 64)
- if err != nil {
- return fmt.Errorf("ID %s is not numeric: %w", d.Id(), err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- if err := client.(checkly.Client).DeleteGroup(ctx, ID); err != nil {
- return fmt.Errorf("API error14: %w", err)
- }
- return nil
-}
-
-func resourceDataFromCheckGroup(g *checkly.Group, d *schema.ResourceData) error {
- d.Set("name", g.Name)
- d.Set("concurrency", g.Concurrency)
- d.Set("activated", g.Activated)
- d.Set("muted", g.Muted)
- d.Set("run_parallel", g.RunParallel)
- d.Set("locations", g.Locations)
- d.Set("double_check", g.DoubleCheck)
- d.Set("setup_snippet_id", g.SetupSnippetID)
- d.Set("teardown_snippet_id", g.TearDownSnippetID)
- d.Set("local_setup_script", g.LocalSetupScript)
- d.Set("local_teardown_script", g.LocalTearDownScript)
- d.Set("alert_channel_subscription", g.AlertChannelSubscriptions)
- d.Set("private_locations", g.PrivateLocations)
-
- sort.Strings(g.Tags)
- d.Set("tags", g.Tags)
-
- environmentVariables := environmentVariablesFromSet(d.Get("environment_variable").([]interface{}))
- if len(environmentVariables) > 0 {
- d.Set("environment_variable", g.EnvironmentVariables)
- } else if err := d.Set("environment_variables", setFromEnvVars(g.EnvironmentVariables)); err != nil {
- return fmt.Errorf("error setting environment variables for resource %s: %s", d.Id(), err)
- }
-
- if g.RuntimeID != nil {
- d.Set("runtime_id", *g.RuntimeID)
- }
-
- if err := d.Set("alert_settings", setFromAlertSettings(g.AlertSettings)); err != nil {
- return fmt.Errorf("error setting alert settings for resource %s: %s", d.Id(), err)
- }
- d.Set("use_global_alert_settings", g.UseGlobalAlertSettings)
-
- if err := d.Set("api_check_defaults", setFromAPICheckDefaults(g.APICheckDefaults)); err != nil {
- return fmt.Errorf("error setting request for resource %s: %s", d.Id(), err)
- }
-
- d.Set("retry_strategy", setFromRetryStrategy(g.RetryStrategy))
-
- d.SetId(d.Id())
- return nil
-}
-
-func checkGroupFromResourceData(d *schema.ResourceData) (checkly.Group, error) {
- ID, err := strconv.ParseInt(d.Id(), 10, 64)
- if err != nil {
- if d.Id() != "" {
- return checkly.Group{}, err
- }
- ID = 0
- }
-
- group := checkly.Group{
- ID: ID,
- Name: d.Get("name").(string),
- Concurrency: d.Get("concurrency").(int),
- Activated: d.Get("activated").(bool),
- Muted: d.Get("muted").(bool),
- RunParallel: d.Get("run_parallel").(bool),
- Locations: stringsFromSet(d.Get("locations").(*schema.Set)),
- DoubleCheck: d.Get("double_check").(bool),
- Tags: stringsFromSet(d.Get("tags").(*schema.Set)),
- SetupSnippetID: int64(d.Get("setup_snippet_id").(int)),
- TearDownSnippetID: int64(d.Get("teardown_snippet_id").(int)),
- LocalSetupScript: d.Get("local_setup_script").(string),
- LocalTearDownScript: d.Get("local_teardown_script").(string),
- AlertSettings: alertSettingsFromSet(d.Get("alert_settings").([]interface{})),
- UseGlobalAlertSettings: d.Get("use_global_alert_settings").(bool),
- APICheckDefaults: apiCheckDefaultsFromSet(d.Get("api_check_defaults").(*schema.Set)),
- AlertChannelSubscriptions: alertChannelSubscriptionsFromSet(d.Get("alert_channel_subscription").([]interface{})),
- RetryStrategy: retryStrategyFromSet(d.Get("retry_strategy").(*schema.Set)),
- }
-
- runtimeId := d.Get("runtime_id").(string)
- if runtimeId == "" {
- group.RuntimeID = nil
- } else {
- group.RuntimeID = &runtimeId
- }
-
- environmentVariables, err := getResourceEnvironmentVariables(d)
- if err != nil {
- return checkly.Group{}, err
- }
- group.EnvironmentVariables = environmentVariables
-
- privateLocations := stringsFromSet(d.Get("private_locations").(*schema.Set))
- group.PrivateLocations = &privateLocations
-
- return group, nil
-}
-
-func setFromAPICheckDefaults(a checkly.APICheckDefaults) []tfMap {
- s := tfMap{}
- s["url"] = a.BaseURL
- s["headers"] = mapFromKeyValues(a.Headers)
- s["query_parameters"] = mapFromKeyValues(a.QueryParameters)
- s["assertion"] = setFromAssertions(a.Assertions)
- s["basic_auth"] = checkGroupSetFromBasicAuth(a.BasicAuth)
- return []tfMap{s}
-}
-
-func apiCheckDefaultsFromSet(s *schema.Set) checkly.APICheckDefaults {
- if s.Len() == 0 {
- return checkly.APICheckDefaults{}
- }
- res := s.List()[0].(tfMap)
-
- return checkly.APICheckDefaults{
- BaseURL: res["url"].(string),
- Headers: keyValuesFromMap(res["headers"].(tfMap)),
- QueryParameters: keyValuesFromMap(res["query_parameters"].(tfMap)),
- Assertions: assertionsFromSet(res["assertion"].(*schema.Set)),
- BasicAuth: checkGroupBasicAuthFromSet(res["basic_auth"].(*schema.Set)),
- }
-}
-
-func checkGroupSetFromBasicAuth(b checkly.BasicAuth) []tfMap {
- if b.Username == "" && b.Password == "" {
- return []tfMap{}
- }
- return []tfMap{
- {
- "username": b.Username,
- "password": b.Password,
- },
- }
-}
-
-func checkGroupBasicAuthFromSet(s *schema.Set) checkly.BasicAuth {
- if s.Len() == 0 {
- return checkly.BasicAuth{
- Username: "",
- Password: "",
- }
- }
- res := s.List()[0].(tfMap)
- return checkly.BasicAuth{
- Username: res["username"].(string),
- Password: res["password"].(string),
- }
-}
diff --git a/checkly/resource_check_group_test.go b/checkly/resource_check_group_test.go
deleted file mode 100644
index eabc14a..0000000
--- a/checkly/resource_check_group_test.go
+++ /dev/null
@@ -1,427 +0,0 @@
-package checkly
-
-import (
- "regexp"
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
-
- "github.com/checkly/checkly-go-sdk"
-)
-
-func TestEncodeDecodeGroupResource(t *testing.T) {
- res := resourceCheckGroup()
- data := res.TestResourceData()
- resourceDataFromCheckGroup(&wantGroup, data)
- gotGroup, err := checkGroupFromResourceData(data)
- if err != nil {
- t.Fatal(err)
- }
- if !cmp.Equal(wantGroup, gotGroup) {
- t.Error(cmp.Diff(wantGroup, gotGroup))
- }
-}
-
-var wantGroup = checkly.Group{
- Name: "test",
- Activated: true,
- Muted: false,
- Tags: []string{"auto"},
- Locations: []string{"eu-west-1"},
- PrivateLocations: &[]string{},
- Concurrency: 3,
- APICheckDefaults: checkly.APICheckDefaults{
- BaseURL: "example.com/api/test",
- Headers: []checkly.KeyValue{
- {
- Key: "X-Test",
- Value: "foo",
- },
- },
- QueryParameters: []checkly.KeyValue{
- {
- Key: "query",
- Value: "foo",
- },
- },
- Assertions: []checkly.Assertion{
- {
- Source: checkly.StatusCode,
- Comparison: checkly.Equals,
- Target: "200",
- },
- },
- BasicAuth: checkly.BasicAuth{
- Username: "user",
- Password: "pass",
- },
- },
- EnvironmentVariables: []checkly.EnvironmentVariable{
- {
- Key: "ENVTEST",
- Value: "Hello world",
- },
- },
- DoubleCheck: true,
- UseGlobalAlertSettings: false,
- AlertSettings: checkly.AlertSettings{
- EscalationType: checkly.RunBased,
- RunBasedEscalation: checkly.RunBasedEscalation{
- FailedRunThreshold: 1,
- },
- Reminders: checkly.Reminders{
- Amount: 0,
- Interval: 5,
- },
- },
- LocalSetupScript: "setup-test",
- LocalTearDownScript: "teardown-test",
- AlertChannelSubscriptions: []checkly.AlertChannelSubscription{},
-}
-
-func TestAccCheckGroupEmptyConfig(t *testing.T) {
- config := `resource "checkly_check_group" "test" {}`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "name" is required`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "concurrency" is required`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "activated" is required`),
- },
- })
-}
-
-func TestAccCheckGroupInvalid(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: testCheckGroup_invalid,
- ExpectError: regexp.MustCompile(`Inappropriate value for attribute "locations"`),
- },
- {
- Config: testCheckGroup_invalid,
- ExpectError: regexp.MustCompile(`Inappropriate value for attribute "muted"`),
- },
- {
- Config: testCheckGroup_invalid,
- ExpectError: regexp.MustCompile(`Inappropriate value for attribute "activated"`),
- },
- {
- Config: testCheckGroup_invalid,
- ExpectError: regexp.MustCompile(`The argument "concurrency" is required`),
- },
- {
- Config: testCheckGroup_invalid,
- ExpectError: regexp.MustCompile(`Missing required argument`),
- },
- })
-}
-
-func TestAccCheckGroupBasic(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: testCheckGroup_basic,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "name",
- "test",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "activated",
- "true",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "muted",
- "false",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "concurrency",
- "3",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "locations.#",
- "2",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "locations.*",
- "us-east-1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "locations.*",
- "eu-central-1",
- ),
- ),
- },
- })
-}
-
-func TestAccCheckGroupWithApiDefaults(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: testCheckGroup_withApiDefaults,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "name",
- "test",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.url",
- "http://api.example.com/",
- ),
- ),
- },
- })
-}
-
-func TestAccCheckGroupFull(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: testCheckGroup_full,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "name",
- "test",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "activated",
- "true",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "muted",
- "false",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "concurrency",
- "3",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "double_check",
- "true",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "use_global_alert_settings",
- "false",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "locations.#",
- "2",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "locations.*",
- "us-east-1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "locations.*",
- "eu-central-1",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check_group.test",
- "environment_variables.FOO",
- "BAR",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "alert_settings.*.escalation_type",
- "RUN_BASED",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "alert_settings.*.reminders.#",
- "1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "alert_settings.*.reminders.*.amount",
- "2",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "alert_settings.*.reminders.*.interval",
- "5",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "alert_settings.*.run_based_escalation.#",
- "1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "alert_settings.*.run_based_escalation.*.failed_run_threshold",
- "1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.#",
- "1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.assertion.#",
- "1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.assertion.*.comparison",
- "EQUALS",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.assertion.*.property",
- "",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.assertion.*.source",
- "STATUS_CODE",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.assertion.*.target",
- "200",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.basic_auth.#",
- "1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.basic_auth.*.password",
- "pass",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.basic_auth.*.username",
- "user",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.headers.X-Test",
- "foo",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.query_parameters.query",
- "foo",
- ),
- testCheckResourceAttrExpr(
- "checkly_check_group.test",
- "api_check_defaults.*.url",
- "http://example.com/",
- ),
- ),
- },
- })
-}
-
-const testCheckGroup_invalid = `
- resource "checkly_check_group" "test" {
- name = "test"
- activated = "invalid"
- muted = "invalid"
- locations = "invalid"
- }
-`
-
-const testCheckGroup_basic = `
- resource "checkly_check_group" "test" {
- name = "test"
- activated = true
- muted = false
- concurrency = 3
- locations = [
- "us-east-1",
- "eu-central-1",
- ]
- }
-`
-
-const testCheckGroup_withApiDefaults = `
- resource "checkly_check_group" "test" {
- name = "test"
- activated = true
- muted = false
- concurrency = 3
- locations = [
- "eu-west-1",
- "eu-west-2"
- ]
- api_check_defaults {
- url = "http://api.example.com/"
- }
- }
-`
-
-const testCheckGroup_full = `
- resource "checkly_check_group" "test" {
- name = "test"
- activated = true
- muted = false
- concurrency = 3
- double_check = true
- use_global_alert_settings = false
- locations = [ "us-east-1", "eu-central-1" ]
- api_check_defaults {
- url = "http://example.com/"
- headers = {
- X-Test = "foo"
- }
- query_parameters = {
- query = "foo"
- }
- assertion {
- source = "STATUS_CODE"
- property = ""
- comparison = "EQUALS"
- target = "200"
- }
- basic_auth {
- username = "user"
- password = "pass"
- }
- }
- environment_variables = {
- FOO = "BAR"
- }
- alert_settings {
- escalation_type = "RUN_BASED"
- run_based_escalation {
- failed_run_threshold = 1
- }
- reminders {
- amount = 2
- interval = 5
- }
- parallel_run_failure_threshold {
- enabled = false
- percentage = 10
- }
- }
- local_setup_script = "setup-test"
- local_teardown_script = "teardown-test"
- }
-`
diff --git a/checkly/resource_check_test.go b/checkly/resource_check_test.go
deleted file mode 100644
index f388f22..0000000
--- a/checkly/resource_check_test.go
+++ /dev/null
@@ -1,698 +0,0 @@
-package checkly
-
-import (
- "net/http"
- "regexp"
- "testing"
-
- "github.com/google/go-cmp/cmp"
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
-
- "github.com/checkly/checkly-go-sdk"
-)
-
-func TestAccCheckRequiredFields(t *testing.T) {
- config := `resource "checkly_check" "test" {}`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "type" is required, but no definition was found.`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "name" is required, but no definition was found.`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "activated" is required, but no definition was found.`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "frequency" is required, but no definition was found.`),
- },
- })
-}
-
-func TestAccBrowserCheckInvalidInputs(t *testing.T) {
- config := `resource "checkly_check" "test" {
- name = 1
- type = "BROWSER"
- activated = "invalid"
- should_fail = "invalid"
- double_check = "invalid"
- use_global_alert_settings = "invalid"
- locations = "invalid"
- script = 4
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`Inappropriate value for attribute "activated"`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "frequency" is required`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`Inappropriate value for attribute "should_fail"`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`Inappropriate value for attribute "use_global_alert_settings"`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`Inappropriate value for attribute "double_check"`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`Inappropriate value for attribute "locations"`),
- },
- })
-}
-
-func TestAccBrowserCheckMissingScript(t *testing.T) {
- config := `resource "checkly_check" "test" {
- type = "BROWSER"
- activated = true
- frequency = 10
- name = "browser check"
- locations = [ "us-west-1" ]
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`API error 1: unexpected response status 400`),
- },
- })
-}
-
-func TestAccBrowserCheckBasic(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: browserCheck_basic,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "name",
- "Browser Check",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "type",
- "BROWSER",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "activated",
- "true",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "script",
- "console.log('test')",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "locations.#",
- "2",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "locations.*",
- "us-east-1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "locations.*",
- "eu-central-1",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "tags.#",
- "2",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "tags.*",
- "browser",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "tags.*",
- "e2e",
- ),
- resource.TestCheckNoResourceAttr(
- "checkly_check.test",
- "request",
- ),
- ),
- },
- })
-}
-
-func TestAccApiCheckBasic(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: apiCheck_basic,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "name",
- "API Check 1",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "activated",
- "true",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "locations.*",
- "eu-central-1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "locations.*",
- "us-east-1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.method",
- "GET",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.url",
- "https://api.checklyhq.com/public-stats",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.assertion.*.comparison",
- "EQUALS",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.assertion.*.property",
- "",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.assertion.*.source",
- "STATUS_CODE",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.assertion.*.target",
- "200",
- ),
- ),
- },
- })
-}
-
-func TestAccMultiStepCheckRuntimeValidation(t *testing.T) {
- unsupportedRuntime := `resource "checkly_check" "test" {
- name = "test"
- type = "MULTI_STEP"
- activated = true
- frequency = 5
- locations = ["eu-central-1"]
- script = "console.log('test')"
- runtime_id = "2023.02"
- }`
- noSpecifiedRuntime := `resource "checkly_check" "test" {
- name = "test"
- type = "MULTI_STEP"
- activated = true
- frequency = 5
- locations = ["eu-central-1"]
- script = "console.log('test')"
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: unsupportedRuntime,
- ExpectError: regexp.MustCompile("Error: runtime 2023.02 does not support MULTI_STEP checks"),
- },
- {
- Config: noSpecifiedRuntime,
- Check: resource.TestCheckNoResourceAttr(
- "checkly_check.test",
- "runtime_id",
- ),
- },
- })
-}
-
-func TestAccMultiStepCheckBasic(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: multiStepCheck_basic,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "name",
- "MultiStep Check",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "type",
- "MULTI_STEP",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "runtime_id",
- "2023.09",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "activated",
- "true",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "script",
- "console.log('test')",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "locations.#",
- "2",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "locations.*",
- "us-east-1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "locations.*",
- "eu-central-1",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "tags.#",
- "2",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "tags.*",
- "browser",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "tags.*",
- "e2e",
- ),
- resource.TestCheckNoResourceAttr(
- "checkly_check.test",
- "request",
- ),
- ),
- },
- })
-}
-
-func TestAccApiCheckFull(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: apiCheck_full,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "degraded_response_time",
- "15000",
- ),
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "max_response_time",
- "30000",
- ),
- resource.TestCheckNoResourceAttr(
- "checkly_check.test",
- "environment_variables",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- `"locations.#"`,
- "3",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- `"request.*.headers.X-CUSTOM-1"`,
- "1",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.headers.X-CUSTOM-2",
- "FOO",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.query_parameters.param1",
- "123",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.query_parameters.param2",
- "bar",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.basic_auth.*.username",
- "user",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.basic_auth.*.password",
- "pass",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.assertion.#",
- "3",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.assertion.*.comparison",
- "EQUALS",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.assertion.*.comparison",
- "GREATER_THAN",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.assertion.*.target",
- "200",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.assertion.*.target",
- "no-cache",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.assertion.*.target",
- "100",
- ),
- ),
- },
- })
-}
-
-func TestAccApiCheckMore(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: apiCheck_post,
- Check: resource.ComposeTestCheckFunc(
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.method",
- "POST",
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.body",
- `{\"message\":\"hello checkly\",\"messageId\":1}`,
- ),
- testCheckResourceAttrExpr(
- "checkly_check.test",
- "request.*.body_type",
- "JSON",
- ),
- ),
- },
- {
- Config: apiCheck_withEmptyBasicAuth,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_check.test",
- "name",
- "api check with empty basic_auth",
- ),
- ),
- },
- })
-}
-
-var wantCheck = checkly.Check{
- Name: "My test check",
- Type: checkly.TypeAPI,
- Frequency: 1,
- Activated: true,
- Muted: false,
- ShouldFail: false,
- Locations: []string{"eu-west-1"},
- PrivateLocations: &[]string{},
- Script: "foo",
- DegradedResponseTime: 15000,
- MaxResponseTime: 30000,
- EnvironmentVariables: []checkly.EnvironmentVariable{
- {
- Key: "ENVTEST",
- Value: "Hello world",
- },
- },
- DoubleCheck: false,
- Tags: []string{
- "foo",
- "bar",
- },
- SSLCheck: false,
- LocalSetupScript: "bogus",
- LocalTearDownScript: "bogus",
- AlertSettings: checkly.AlertSettings{
- EscalationType: checkly.RunBased,
- RunBasedEscalation: checkly.RunBasedEscalation{
- FailedRunThreshold: 1,
- },
- Reminders: checkly.Reminders{
- Interval: 5,
- },
- },
- UseGlobalAlertSettings: false,
- Request: checkly.Request{
- Method: http.MethodGet,
- URL: "http://example.com",
- Headers: []checkly.KeyValue{
- {
- Key: "X-Test",
- Value: "foo",
- },
- },
- QueryParameters: []checkly.KeyValue{
- {
- Key: "query",
- Value: "foo",
- },
- },
- Assertions: []checkly.Assertion{
- {
- Source: checkly.StatusCode,
- Comparison: checkly.Equals,
- Target: "200",
- },
- },
- Body: "",
- BodyType: "NONE",
- BasicAuth: &checkly.BasicAuth{
- Username: "example",
- Password: "pass",
- },
- },
-}
-
-func TestEncodeDecodeResource(t *testing.T) {
- res := resourceCheck()
- data := res.TestResourceData()
- wantCheck.AlertChannelSubscriptions = []checkly.AlertChannelSubscription{}
- resourceDataFromCheck(&wantCheck, data)
- got, err := checkFromResourceData(data)
- if err != nil {
- t.Fatal(err)
- }
- if !cmp.Equal(wantCheck, got) {
- t.Error(cmp.Diff(wantCheck, got))
- }
-}
-
-const browserCheck_basic = `
- resource "checkly_check" "test" {
- name = "Browser Check"
- type = "BROWSER"
- activated = true
- should_fail = false
- frequency = 720
- double_check = true
- use_global_alert_settings = true
- locations = [ "us-east-1", "eu-central-1" ]
- tags = [ "browser", "e2e" ]
- script = "console.log('test')"
- }
-`
-const multiStepCheck_basic = `
- resource "checkly_check" "test" {
- name = "MultiStep Check"
- type = "MULTI_STEP"
- activated = true
- should_fail = false
- frequency = 720
- double_check = true
- use_global_alert_settings = true
- locations = [ "us-east-1", "eu-central-1" ]
- tags = [ "api", "multi-step" ]
- runtime_id = "2023.09"
- script = "console.log('test')"
- }
-`
-
-const apiCheck_basic = `
- resource "checkly_check" "test" {
- name = "API Check 1"
- type = "API"
- frequency = 60
- activated = true
- muted = true
- double_check = true
- max_response_time = 18000
- locations = [ "us-east-1", "eu-central-1" ]
- use_global_alert_settings = true
- request {
- method = "GET"
- url = "https://api.checklyhq.com/public-stats"
- assertion {
- comparison = "EQUALS"
- property = ""
- source = "STATUS_CODE"
- target = "200"
- }
- }
- }
-`
-
-const apiCheck_full = `
- resource "checkly_check" "test" {
- name = "apiCheck_full"
- type = "API"
- frequency = 120
- activated = true
- muted = true
- double_check = true
- degraded_response_time = 15000
- max_response_time = 30000
- environment_variables = null
- locations = [
- "eu-central-1",
- "us-east-1",
- "ap-northeast-1"
- ]
- request {
- method = "GET"
- url = "https://api.checklyhq.com/public-stats"
- follow_redirects = true
- headers = {
- X-CUSTOM-1 = 1
- X-CUSTOM-2 = "foo"
- }
- query_parameters = {
- param1 = 123
- param2 = "bar"
- }
- basic_auth {
- username = "user"
- password = "pass"
- }
- assertion {
- comparison = "EQUALS"
- property = ""
- source = "STATUS_CODE"
- target = "200"
- }
- assertion {
- comparison = "EQUALS"
- property = "cache-control"
- source = "HEADERS"
- target = "no-cache"
- }
- assertion {
- comparison = "GREATER_THAN"
- property = "$.apiCheckResults"
- source = "JSON_BODY"
- target = "100"
- }
- }
-
- alert_settings {
- escalation_type = "RUN_BASED"
- reminders {
- amount = 0
- interval = 5
- }
- run_based_escalation {
- failed_run_threshold = 1
- }
- parallel_run_failure_threshold {
- enabled = false
- percentage = 10
- }
- }
- }
-`
-
-const apiCheck_post = `
- resource "checkly_check" "test" {
- name = "apiCheck_post"
- type = "API"
- activated = true
- double_check = true
- frequency = 720
- locations = [ "eu-central-1", "us-east-2" ]
- max_response_time = 18000
- muted = true
- environment_variables = null
- request {
- method = "POST"
- url = "https://jsonplaceholder.typicode.com/posts"
- headers = {
- Content-type = "application/json; charset=UTF-8"
- }
- body = "{\"message\":\"hello checkly\",\"messageId\":1}"
- body_type = "JSON"
- }
- use_global_alert_settings = true
- }
-`
-
-const apiCheck_withEmptyBasicAuth = `
- resource "checkly_check" "test" {
- name = "api check with empty basic_auth"
- type = "API"
- activated = true
- should_fail = false
- frequency = 1
- degraded_response_time = 3000
- max_response_time = 6000
- tags = [
- "testing",
- "bug"
- ]
- locations = [ "eu-central-1" ]
- request {
- follow_redirects = false
- url = "https://api.checklyhq.com/public-stats"
- basic_auth {
- username = ""
- password = ""
- }
- assertion {
- source = "STATUS_CODE"
- property = ""
- comparison = "EQUALS"
- target = "200"
- }
- }
- }
-`
diff --git a/checkly/resource_dashboard.go b/checkly/resource_dashboard.go
deleted file mode 100644
index 68679c2..0000000
--- a/checkly/resource_dashboard.go
+++ /dev/null
@@ -1,275 +0,0 @@
-package checkly
-
-import (
- "context"
- "fmt"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- checkly "github.com/checkly/checkly-go-sdk"
-)
-
-func validateOptions(options []int) func(val interface{}, key string) (warns []string, errs []error) {
- return func(val interface{}, key string) (warns []string, errs []error) {
- v := val.(int)
- valid := false
- for _, i := range options {
- if v == i {
- valid = true
- }
- }
- if !valid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got: %d", key, options, v))
- }
- return warns, errs
- }
-}
-
-func resourceDashboard() *schema.Resource {
- return &schema.Resource{
- Create: resourceDashboardCreate,
- Read: resourceDashboardRead,
- Update: resourceDashboardUpdate,
- Delete: resourceDashboardDelete,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
- Schema: map[string]*schema.Schema{
- "custom_url": {
- Type: schema.TypeString,
- Required: true,
- Description: "A subdomain name under 'checklyhq.com'. Needs to be unique across all users.",
- },
- "custom_domain": {
- Type: schema.TypeString,
- Optional: true,
- Default: nil,
- Description: "A custom user domain, e.g. 'status.example.com'. See the docs on updating your DNS and SSL usage.",
- },
- "logo": {
- Type: schema.TypeString,
- Optional: true,
- Default: "",
- Description: "A URL pointing to an image file to use for the dashboard logo.",
- },
- "favicon": {
- Type: schema.TypeString,
- Optional: true,
- Default: "",
- Description: "A URL pointing to an image file to use as browser favicon.",
- },
- "link": {
- Type: schema.TypeString,
- Optional: true,
- Default: "",
- Description: "A link to for the dashboard logo.",
- },
- "description": {
- Type: schema.TypeString,
- Optional: true,
- Default: "",
- Description: "HTML description for the dashboard.",
- },
- "header": {
- Type: schema.TypeString,
- Optional: true,
- Default: "",
- Description: "A piece of text displayed at the top of your dashboard.",
- },
- "width": {
- Type: schema.TypeString,
- Optional: true,
- Default: "FULL",
- ValidateFunc: func(value interface{}, key string) (warns []string, errs []error) {
- full := "FULL"
- px960 := "960PX"
- v := value.(string)
- if v != full && v != px960 {
- errs = append(errs, fmt.Errorf("%q must %s and %s, got: %s", key, full, px960, v))
- }
- return warns, errs
- },
- Description: "Determines whether to use the full screen or focus in the center. Possible values `FULL` and `960PX`.",
- },
- "refresh_rate": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 60,
- ValidateFunc: validateOptions([]int{60, 300, 600}),
- Description: "How often to refresh the dashboard in seconds. Possible values `60`, '300' and `600`.",
- },
- "paginate": {
- Type: schema.TypeBool,
- Optional: true,
- Default: true,
- Description: "Determines if pagination is on or off.",
- },
- "checks_per_page": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 15,
- Description: "Determines how many checks to show per page.",
- },
- "pagination_rate": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 60,
- ValidateFunc: validateOptions([]int{30, 60, 300}),
- Description: "How often to trigger pagination in seconds. Possible values `30`, `60` and `300`.",
- },
- "tags": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- Description: "A list of one or more tags that filter which checks to display on the dashboard.",
- },
- "hide_tags": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- Description: "Show or hide the tags on the dashboard.",
- },
- "use_tags_and_operator": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- Description: "Set when to use AND operator for fetching dashboard tags.",
- },
- "is_private": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- Description: "Set your dashboard as private and generate key.",
- },
- // moving to TypeString here https://github.com/hashicorp/terraform-plugin-sdk/issues/792
- "key": {
- Type: schema.TypeString,
- Computed: true,
- Sensitive: true,
- Description: "The access key when the dashboard is private.",
- },
- },
- }
-}
-
-func dashboardFromResourceData(d *schema.ResourceData) (checkly.Dashboard, error) {
- a := checkly.Dashboard{
- CustomDomain: d.Get("custom_domain").(string),
- CustomUrl: d.Get("custom_url").(string),
- Logo: d.Get("logo").(string),
- Favicon: d.Get("favicon").(string),
- Link: d.Get("link").(string),
- Description: d.Get("description").(string),
- Header: d.Get("header").(string),
- RefreshRate: d.Get("refresh_rate").(int),
- Paginate: d.Get("paginate").(bool),
- ChecksPerPage: d.Get("checks_per_page").(int),
- PaginationRate: d.Get("pagination_rate").(int),
- HideTags: d.Get("hide_tags").(bool),
- Width: d.Get("width").(string),
- UseTagsAndOperator: d.Get("use_tags_and_operator").(bool),
- IsPrivate: d.Get("is_private").(bool),
- Tags: stringsFromSet(d.Get("tags").(*schema.Set)),
- }
-
- fmt.Printf("%v", a)
-
- return a, nil
-}
-
-func resourceDataFromDashboard(s *checkly.Dashboard, d *schema.ResourceData) error {
- d.Set("custom_domain", s.CustomDomain)
- d.Set("custom_url", s.CustomUrl)
- d.Set("logo", s.Logo)
- d.Set("favicon", s.Favicon)
- d.Set("link", s.Link)
- d.Set("description", s.Description)
- d.Set("header", s.Header)
- d.Set("refresh_rate", s.RefreshRate)
- d.Set("paginate", s.Paginate)
- d.Set("checks_per_page", s.ChecksPerPage)
- d.Set("pagination_rate", s.PaginationRate)
- d.Set("hide_tags", s.HideTags)
- d.Set("tags", s.Tags)
- d.Set("width", s.Width)
- d.Set("use_tags_and_operator", s.UseTagsAndOperator)
- d.Set("is_private", s.IsPrivate)
-
- // if the dashboard is private, we either do nothing
- // or set the key to a new value if there is any
- if s.IsPrivate {
- if len(s.Keys) > 0 {
- d.Set("key", s.Keys[0].RawKey)
- }
- } else {
- // if the dashboard is public, remove the key
- d.Set("key", nil)
- }
-
- return nil
-}
-
-func resourceDashboardCreate(d *schema.ResourceData, client interface{}) error {
- dashboard, err := dashboardFromResourceData(d)
- if err != nil {
- return fmt.Errorf("resourceDashboardCreate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- result, err := client.(checkly.Client).CreateDashboard(ctx, dashboard)
-
- if err != nil {
- return fmt.Errorf("CreateDashboard: API error: %w", err)
- }
-
- d.SetId(result.DashboardID)
-
- // we cannot take the detour through resourceDashboardRead since
- // we would not get the keys back from an additional GET call
- return resourceDataFromDashboard(result, d)
-}
-
-func resourceDashboardUpdate(d *schema.ResourceData, client interface{}) error {
- dashboard, err := dashboardFromResourceData(d)
- if err != nil {
- return fmt.Errorf("resourceDashboardUpdate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- result, err := client.(checkly.Client).UpdateDashboard(ctx, d.Id(), dashboard)
- if err != nil {
- return fmt.Errorf("resourceDashboardUpdate: API error: %w", err)
- }
- d.SetId(result.DashboardID)
-
- // we cannot take the detour through resourceDashboardRead since
- // we would not get the keys back from an additional GET call
- return resourceDataFromDashboard(result, d)
-}
-
-func resourceDashboardDelete(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- err := client.(checkly.Client).DeleteDashboard(ctx, d.Id())
- if err != nil {
- return fmt.Errorf("resourceDashboardDelete: API error: %w", err)
- }
- return nil
-}
-
-func resourceDashboardRead(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- dashboard, err := client.(checkly.Client).GetDashboard(ctx, d.Id())
- defer cancel()
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- d.SetId("")
- return nil
- }
- return fmt.Errorf("resourceDashboardRead: API error: %w", err)
- }
- return resourceDataFromDashboard(dashboard, d)
-}
diff --git a/checkly/resource_environment_variable.go b/checkly/resource_environment_variable.go
deleted file mode 100644
index fb55897..0000000
--- a/checkly/resource_environment_variable.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package checkly
-
-import (
- "context"
- "fmt"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- checkly "github.com/checkly/checkly-go-sdk"
-)
-
-func resourceEnvironmentVariable() *schema.Resource {
- return &schema.Resource{
- Create: resourceEnvironmentVariableCreate,
- Read: resourceEnvironmentVariableRead,
- Update: resourceEnvironmentVariableUpdate,
- Delete: resourceEnvironmentVariableDelete,
- Importer: &schema.ResourceImporter{
- State: schema.ImportStatePassthrough,
- },
- Schema: map[string]*schema.Schema{
- "key": {
- Type: schema.TypeString,
- Required: true,
- },
- "value": {
- Type: schema.TypeString,
- Required: true,
- },
- "locked": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- },
- "secret": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- },
- },
- }
-}
-
-func resourceEnvironmentVariableCreate(d *schema.ResourceData, client interface{}) error {
- envVar, err := environmentVariableFromResourceData(d)
- if err != nil {
- return fmt.Errorf("resourceEnvironmentVariableCreate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- _, err = client.(checkly.Client).CreateEnvironmentVariable(ctx, envVar)
- if err != nil {
- return fmt.Errorf("CreateEnvironmentVariable: API error: %w", err)
- }
- d.SetId(envVar.Key)
- return resourceEnvironmentVariableRead(d, client)
-}
-
-func environmentVariableFromResourceData(d *schema.ResourceData) (checkly.EnvironmentVariable, error) {
- return checkly.EnvironmentVariable{
- Key: d.Get("key").(string),
- Value: d.Get("value").(string),
- Locked: d.Get("locked").(bool),
- Secret: d.Get("secret").(bool),
- }, nil
-}
-
-func resourceDataFromEnvironmentVariable(s *checkly.EnvironmentVariable, d *schema.ResourceData) error {
- d.Set("key", s.Key)
- if !s.Secret {
- d.Set("value", s.Value)
- }
- d.Set("locked", s.Locked)
- d.Set("secret", s.Secret)
- return nil
-}
-
-func resourceEnvironmentVariableRead(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- envVar, err := client.(checkly.Client).GetEnvironmentVariable(ctx, d.Id())
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- //if resource is deleted remotely, then mark it as
- //successfully gone by unsetting it's ID
- d.SetId("")
- return nil
- }
- return fmt.Errorf("resourceEnvironmentVariableRead: API error: %w", err)
- }
- return resourceDataFromEnvironmentVariable(envVar, d)
-}
-
-func resourceEnvironmentVariableUpdate(d *schema.ResourceData, client interface{}) error {
- envVar, err := environmentVariableFromResourceData(d)
- if err != nil {
- return fmt.Errorf("resourceEnvironmentVariableUpdate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- _, err = client.(checkly.Client).UpdateEnvironmentVariable(ctx, d.Id(), envVar)
- if err != nil {
- return fmt.Errorf("resourceEnvironmentVariableUpdate: API error: %w", err)
- }
-
- return resourceEnvironmentVariableRead(d, client)
-}
-
-func resourceEnvironmentVariableDelete(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- err := client.(checkly.Client).DeleteEnvironmentVariable(ctx, d.Id())
- if err != nil {
- return fmt.Errorf("resourceEnvironmentVariableDelete: API error: %w", err)
- }
- return nil
-}
diff --git a/checkly/resource_environment_variable_test.go b/checkly/resource_environment_variable_test.go
deleted file mode 100644
index 09fbccf..0000000
--- a/checkly/resource_environment_variable_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package checkly
-
-import (
- "regexp"
- "testing"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
-)
-
-func TestAccEnvVarCheckRequiredFields(t *testing.T) {
- config := `resource "checkly_environment_variable" "test" {}`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "key" is required`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "value" is required`),
- },
- })
-}
-
-func TestAccEnvVarSuccess(t *testing.T) {
- config := `resource "checkly_environment_variable" "test" {
- key = "API_URL"
- value = "https://api.checklyhq.com"
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_environment_variable.test",
- "key",
- "API_URL",
- ),
- resource.TestCheckResourceAttr(
- "checkly_environment_variable.test",
- "value",
- "https://api.checklyhq.com",
- ),
- ),
- },
- })
-}
-
-func TestAccSecretEnvVarSuccess(t *testing.T) {
- accTestCase(t, []resource.TestStep{
- {
- Config: `resource "checkly_environment_variable" "test" {
- key = "SECRET"
- value = "https://api.checklyhq.com"
- secret = true
- }`,
- },
- })
-}
diff --git a/checkly/resource_heartbeat.go b/checkly/resource_heartbeat.go
deleted file mode 100644
index 3b2fa0b..0000000
--- a/checkly/resource_heartbeat.go
+++ /dev/null
@@ -1,428 +0,0 @@
-package checkly
-
-import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "sort"
- "strings"
- "time"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- "github.com/checkly/checkly-go-sdk"
-)
-
-func resourceHeartbeat() *schema.Resource {
- return &schema.Resource{
- Create: resourceHeartbeatCreate,
- Read: resourceHeartbeatRead,
- Update: resourceHeartbeatUpdate,
- Delete: resourceHeartbeatDelete,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
- Description: "Heartbeats allows you to monitor your cron jobs and set up alerting, so you get a notification when things break or slow down.",
- Schema: map[string]*schema.Schema{
- "name": {
- Type: schema.TypeString,
- Required: true,
- Description: "The name of the check.",
- },
- "activated": {
- Type: schema.TypeBool,
- Required: true,
- Description: "Determines if the check is running or not. Possible values `true`, and `false`.",
- },
- "muted": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Determines if any notifications will be sent out when a check fails/degrades/recovers.",
- },
- "tags": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- Description: "A list of tags for organizing and filtering checks.",
- },
- "alert_settings": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "escalation_type": {
- Type: schema.TypeString,
- Optional: true,
- Description: "Determines what type of escalation to use. Possible values are `RUN_BASED` or `TIME_BASED`.",
- },
- "run_based_escalation": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "failed_run_threshold": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "After how many failed consecutive check runs an alert notification should be sent. Possible values are between 1 and 5. (Default `1`).",
- },
- },
- },
- },
- "time_based_escalation": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "minutes_failing_threshold": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "After how many minutes after a check starts failing an alert should be sent. Possible values are `5`, `10`, `15`, and `30`. (Default `5`).",
- },
- },
- },
- },
- "reminders": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "amount": {
- Type: schema.TypeInt,
- Optional: true,
- Description: "How many reminders to send out after the initial alert notification. Possible values are `0`, `1`, `2`, `3`, `4`, `5`, and `100000`",
- },
- "interval": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 5,
- Description: "Possible values are `5`, `10`, `15`, and `30`. (Default `5`).",
- },
- },
- },
- },
- "parallel_run_failure_threshold": {
- Type: schema.TypeList,
- Optional: true,
- Computed: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "enabled": {
- Type: schema.TypeBool,
- Optional: true,
- Default: false,
- Description: "Applicable only for checks scheduled in parallel in multiple locations.",
- },
- "percentage": {
- Type: schema.TypeInt,
- Optional: true,
- Default: 10,
- Description: "Possible values are `10`, `20`, `30`, `40`, `50`, `60`, `70`, `80`, `100`, and `100`. (Default `10`).",
- },
- },
- },
- },
- "ssl_certificates": {
- Type: schema.TypeSet,
- Optional: true,
- Deprecated: "This property is deprecated and it's ignored by the Checkly Public API. It will be removed in a future version.",
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "enabled": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "Determines if alert notifications should be sent for expiring SSL certificates. Possible values `true`, and `false`. (Default `false`).",
- },
- "alert_threshold": {
- Type: schema.TypeInt,
- Optional: true,
- ValidateFunc: func(val interface{}, key string) (warns []string, errs []error) {
- v := val.(int)
- valid := false
- validFreqs := []int{3, 7, 14, 30}
- for _, i := range validFreqs {
- if v == i {
- valid = true
- }
- }
- if !valid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got: %d", key, validFreqs, v))
- }
- return warns, errs
- },
- Description: "How long before SSL certificate expiry to send alerts. Possible values `3`, `7`, `14`, `30`. (Default `3`).",
- },
- },
- Description: "At what interval the reminders should be sent.",
- },
- },
- },
- },
- },
- "use_global_alert_settings": {
- Type: schema.TypeBool,
- Optional: true,
- Description: "When true, the account level alert settings will be used, not the alert setting defined on this check.",
- },
- "heartbeat": {
- Type: schema.TypeSet,
- Required: true,
- MaxItems: 1,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "period": {
- Type: schema.TypeInt,
- Required: true,
- Description: "How often you expect a ping to the ping URL.",
- },
- "period_unit": {
- Type: schema.TypeString,
- Required: true,
- ValidateFunc: func(value interface{}, key string) (warns []string, errs []error) {
- v := value.(string)
- isValid := false
- options := []string{"seconds", "minutes", "hours", "days"}
- for _, option := range options {
- if v == option {
- isValid = true
- }
- }
- if !isValid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got %s", key, options, v))
- }
- return warns, errs
- },
- Description: "Possible values `seconds`, `minutes`, `hours` and `days`.",
- },
- "grace": {
- Type: schema.TypeInt,
- Required: true,
- Description: "How long Checkly should wait before triggering any alerts when a ping does not arrive within the set period.",
- },
- "grace_unit": {
- Type: schema.TypeString,
- Required: true,
- ValidateFunc: func(value interface{}, key string) (warns []string, errs []error) {
- v := value.(string)
- isValid := false
- options := []string{"seconds", "minutes", "hours", "days"}
- for _, option := range options {
- if v == option {
- isValid = true
- }
- }
- if !isValid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got %s", key, options, v))
- }
- return warns, errs
- },
- Description: "Possible values `seconds`, `minutes`, `hours` and `days`.",
- },
- "ping_token": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
- Description: "Custom token to generate your ping URL. Checkly will expect a ping to `https://ping.checklyhq.com/[PING_TOKEN]`.",
- },
- },
- },
- },
- "alert_channel_subscription": {
- Type: schema.TypeList,
- Optional: true,
- Elem: &schema.Resource{
- Schema: map[string]*schema.Schema{
- "channel_id": {
- Type: schema.TypeInt,
- Required: true,
- },
- "activated": {
- Type: schema.TypeBool,
- Required: true,
- },
- },
- },
- },
- },
- }
-}
-
-func resourceHeartbeatCreate(d *schema.ResourceData, client interface{}) error {
- check, err := heartbeatCheckFromResourceData(d)
-
- if err != nil {
- return fmt.Errorf("translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- newCheck, err := client.(checkly.Client).CreateHeartbeat(ctx, check)
-
- if err != nil {
- checkJSON, _ := json.Marshal(check)
- return fmt.Errorf("API error 1: %w, Check: %s", err, string(checkJSON))
- }
- d.SetId(newCheck.ID)
- return resourceHeartbeatRead(d, client)
-}
-
-func resourceHeartbeatRead(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- check, err := client.(checkly.Client).GetHeartbeatCheck(ctx, d.Id())
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- //if resource is deleted remotely, then mark it as
- //successfully gone by unsetting it's ID
- d.SetId("")
- return nil
- }
- return fmt.Errorf("API error 2: %w", err)
- }
- return resourceDataFromHeartbeat(check, d)
-}
-
-func resourceHeartbeatUpdate(d *schema.ResourceData, client interface{}) error {
- check, err := heartbeatCheckFromResourceData(d)
-
- if err != nil {
- return fmt.Errorf("translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- _, err = client.(checkly.Client).UpdateHeartbeat(ctx, check.ID, check)
- if err != nil {
- checkJSON, _ := json.Marshal(check)
- return fmt.Errorf("API error 3: Couldn't update check, Error: %w, \nCheck: %s", err, checkJSON)
- }
- d.SetId(check.ID)
- return resourceHeartbeatRead(d, client)
-}
-
-func resourceHeartbeatDelete(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- if err := client.(checkly.Client).Delete(ctx, d.Id()); err != nil {
- return fmt.Errorf("API error 4: Couldn't delete Check %s, Error: %w", d.Id(), err)
- }
- return nil
-}
-
-func heartbeatCheckFromResourceData(d *schema.ResourceData) (checkly.HeartbeatCheck, error) {
- check := checkly.HeartbeatCheck{
- ID: d.Id(),
- Name: d.Get("name").(string),
- Activated: d.Get("activated").(bool),
- Muted: d.Get("muted").(bool),
- Tags: stringsFromSet(d.Get("tags").(*schema.Set)),
- AlertSettings: alertSettingsFromSet(d.Get("alert_settings").([]interface{})),
- UseGlobalAlertSettings: d.Get("use_global_alert_settings").(bool),
- AlertChannelSubscriptions: alertChannelSubscriptionsFromSet(d.Get("alert_channel_subscription").([]interface{})),
- }
-
- // this will prevent subsequent apply from causing a tf config change in browser checks
- check.Heartbeat = heartbeatFromSet(d.Get("heartbeat").(*schema.Set))
-
- // Period / Grace validation
- periodDaysInHours := 0
- periodHours := 0
- periodMinutes := 0
- periodSseconds := 0
- graceDaysInHours := 0
- graceHours := 0
- graceMinutes := 0
- graceSseconds := 0
-
- if check.Heartbeat.PeriodUnit == "days" {
- periodDaysInHours = check.Heartbeat.Period * 24
- } else if check.Heartbeat.PeriodUnit == "hours" {
- periodHours = check.Heartbeat.Period
- } else if check.Heartbeat.PeriodUnit == "minutes" {
- periodMinutes = check.Heartbeat.Period
- } else {
- periodSseconds = check.Heartbeat.Period
- }
-
- if check.Heartbeat.GraceUnit == "days" {
- graceDaysInHours = check.Heartbeat.Grace * 24
- } else if check.Heartbeat.GraceUnit == "hours" {
- graceHours = check.Heartbeat.Grace
- } else if check.Heartbeat.GraceUnit == "minutes" {
- graceMinutes = check.Heartbeat.Grace
- } else {
- graceSseconds = check.Heartbeat.Grace
- }
-
- now := time.Now().Local()
- addedTimePeriod := time.Now().Local().Add(
- time.Hour*time.Duration(periodDaysInHours+periodHours) +
- time.Minute*time.Duration(periodMinutes) +
- time.Second*time.Duration(periodSseconds))
- addedTimeGrace := time.Now().Local().Add(
- time.Hour*time.Duration(graceDaysInHours+graceHours) +
- time.Minute*time.Duration(graceMinutes) +
- time.Second*time.Duration(graceSseconds))
-
- if addedTimePeriod.Sub(now).Hours()/float64(24) > 365 || addedTimePeriod.Sub(now).Seconds() < 30 {
- return check, errors.New(fmt.Sprintf("period must be between 30 seconds and 365 days"))
- }
-
- if addedTimeGrace.Sub(now).Hours()/float64(24) > 365 {
- return check, errors.New("grace must be less than 365 days")
- }
-
- return check, nil
-}
-
-func resourceDataFromHeartbeat(c *checkly.HeartbeatCheck, d *schema.ResourceData) error {
- d.Set("name", c.Name)
- d.Set("activated", c.Activated)
- d.Set("muted", c.Muted)
-
- sort.Strings(c.Tags)
- d.Set("tags", c.Tags)
- if err := d.Set("alert_settings", setFromAlertSettings(c.AlertSettings)); err != nil {
- return fmt.Errorf("error setting alert settings for resource %s: %w", d.Id(), err)
- }
- d.Set("use_global_alert_settings", c.UseGlobalAlertSettings)
-
- err := d.Set("heartbeat", setFromHeartbeat(c.Heartbeat))
- if err != nil {
- return fmt.Errorf("error setting heartbeat for resource %s: %w %v", d.Id(), err, c.Heartbeat)
- }
-
- d.Set("alert_channel_subscription", c.AlertChannelSubscriptions)
- d.SetId(d.Id())
-
- return nil
-}
-
-func setFromHeartbeat(r checkly.Heartbeat) []tfMap {
- s := tfMap{}
- s["period"] = r.Period
- s["period_unit"] = r.PeriodUnit
- s["grace_unit"] = r.GraceUnit
- s["grace"] = r.Grace
- s["ping_token"] = r.PingToken
- return []tfMap{s}
-}
-
-func heartbeatFromSet(s *schema.Set) checkly.Heartbeat {
- if s.Len() == 0 {
- return checkly.Heartbeat{}
- }
- res := s.List()[0].(tfMap)
- return checkly.Heartbeat{
- Period: res["period"].(int),
- PeriodUnit: res["period_unit"].(string),
- Grace: res["grace"].(int),
- GraceUnit: res["grace_unit"].(string),
- PingToken: res["ping_token"].(string),
- }
-}
diff --git a/checkly/resource_heartbeat_test.go b/checkly/resource_heartbeat_test.go
deleted file mode 100644
index 3d11882..0000000
--- a/checkly/resource_heartbeat_test.go
+++ /dev/null
@@ -1,192 +0,0 @@
-package checkly
-
-import (
- "regexp"
- "testing"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
-)
-
-func TestAccHeartbeatRequiredFields(t *testing.T) {
- config := `resource "checkly_heartbeat" "test" {}`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "name" is required, but no definition was found.`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "activated" is required, but no definition was found.`),
- },
- })
-}
-
-func TestAccHeartbeatCheckInvalidInputs(t *testing.T) {
- config := `resource "checkly_check" "test" {
- name = 1
- activated = "invalid"
- use_global_alert_settings = "invalid"
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`Inappropriate value for attribute "activated"`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`Inappropriate value for attribute "use_global_alert_settings"`),
- },
- })
-}
-
-func TestAccHeartbeatCheckMissingHeartbeatBlock(t *testing.T) {
- config := `resource "checkly_heartbeat" "test" {
- activated = true
- name = "heartbeat check"
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`At least 1 "heartbeat" blocks are required.`),
- },
- })
-}
-
-func TestAccHeartbeatCheckMissingHeartbeatFields(t *testing.T) {
- config := `resource "checkly_heartbeat" "test" {
- activated = true
- name = "heartbeat check"
- heartbeat {
-
- }
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "grace" is required, but no definition was found.`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "grace_unit" is required, but no definition was found.`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "period" is required, but no definition was found.`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "period_unit" is required, but no definition was found.`),
- },
- })
-}
-
-func TestAccHeartbeatCheckPeriodTooBig(t *testing.T) {
- config := `resource "checkly_heartbeat" "test" {
- activated = true
- name = "heartbeat check"
- heartbeat {
- period = 366
- period_unit = "days"
- grace = 0
- grace_unit = "seconds"
- }
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`translation error: period must be between 30 seconds and 365 days`),
- },
- })
-}
-
-func TestAccHeartbeatCheckPeriodTooSmall(t *testing.T) {
- config := `resource "checkly_heartbeat" "test" {
- activated = true
- name = "heartbeat check"
- heartbeat {
- period = 5
- period_unit = "seconds"
- grace = 0
- grace_unit = "seconds"
- }
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`translation error: period must be between 30 seconds and 365 days`),
- },
- })
-}
-
-func TestAccHeartbeatCheckInvalidPeriodUnit(t *testing.T) {
- config := `resource "checkly_heartbeat" "test" {
- activated = true
- name = "heartbeat check"
- heartbeat {
- period = 5
- period_unit = "lightyear"
- grace = 0
- grace_unit = "seconds"
- }
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`"heartbeat\.0\.period_unit" must be one of \[seconds minutes hours days\], got lightyear`),
- },
- })
-}
-
-func TestAccHeartbeatCheckInvalidGraceUnit(t *testing.T) {
- config := `resource "checkly_heartbeat" "test" {
- activated = true
- name = "heartbeat check"
- heartbeat {
- period = 5
- period_unit = "days"
- grace = 0
- grace_unit = "lightyear"
- }
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`"heartbeat\.0\.grace_unit" must be one of \[seconds minutes hours days\], got lightyear`),
- },
- })
-}
-
-func TestAccHeartbeatCheckCreate(t *testing.T) {
- config := `resource "checkly_heartbeat" "test" {
- activated = true
- name = "heartbeat check"
- heartbeat {
- period = 5
- period_unit = "days"
- grace = 0
- grace_unit = "seconds"
- }
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_heartbeat.test",
- "name",
- "heartbeat check",
- ),
- testCheckResourceAttrExpr(
- "checkly_heartbeat.test",
- "heartbeat.*.period",
- "5",
- ),
- testCheckResourceAttrExpr(
- "checkly_heartbeat.test",
- "heartbeat.*.period_unit",
- "days",
- ),
- ),
- },
- })
-}
diff --git a/checkly/resource_maintenance_window.go b/checkly/resource_maintenance_window.go
deleted file mode 100644
index b8f5a32..0000000
--- a/checkly/resource_maintenance_window.go
+++ /dev/null
@@ -1,183 +0,0 @@
-package checkly
-
-import (
- "context"
- "fmt"
- "strconv"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- checkly "github.com/checkly/checkly-go-sdk"
-)
-
-func resourceMaintenanceWindow() *schema.Resource {
- return &schema.Resource{
- Create: resourceMaintenanceWindowCreate,
- Read: resourceMaintenanceWindowRead,
- Update: resourceMaintenanceWindowUpdate,
- Delete: resourceMaintenanceWindowDelete,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
- Schema: map[string]*schema.Schema{
- "name": {
- Type: schema.TypeString,
- Required: true,
- Description: "The maintenance window name.",
- },
- "starts_at": {
- Type: schema.TypeString,
- Required: true,
- Description: "The start date of the maintenance window.",
- },
- "ends_at": {
- Type: schema.TypeString,
- Required: true,
- Description: "The end date of the maintenance window.",
- },
- "repeat_unit": {
- Type: schema.TypeString,
- Optional: true,
- Default: nil,
- ValidateFunc: func(value interface{}, key string) (warns []string, errs []error) {
- v := value.(string)
- isValid := false
- options := []string{"DAY", "WEEK", "MONTH"}
- for _, option := range options {
- if v == option {
- isValid = true
- }
- }
- if !isValid {
- errs = append(errs, fmt.Errorf("%q must be one of %v, got %s", key, options, v))
- }
- return warns, errs
- },
- Description: "The repeat cadence for the maintenance window. Possible values `DAY`, `WEEK` and `MONTH`.",
- },
- "repeat_interval": {
- Type: schema.TypeInt,
- Optional: true,
- Default: nil,
- Description: "The repeat interval of the maintenance window from the first occurrence.",
- },
- "repeat_ends_at": {
- Type: schema.TypeString,
- Optional: true,
- Default: nil,
- Description: "The date on which the maintenance window should stop repeating.",
- },
- "tags": {
- Type: schema.TypeSet,
- Optional: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- DefaultFunc: func() (interface{}, error) {
- return []tfMap{}, nil
- },
- Description: "The names of the checks and groups maintenance window should apply to.",
- },
- },
- }
-}
-
-func maintenanceWindowsFromResourceData(d *schema.ResourceData) (checkly.MaintenanceWindow, error) {
- ID, err := strconv.ParseInt(d.Id(), 10, 64)
- if err != nil {
- if d.Id() != "" {
- return checkly.MaintenanceWindow{}, err
- }
- ID = 0
- }
- a := checkly.MaintenanceWindow{
- ID: ID,
- Name: d.Get("name").(string),
- StartsAt: d.Get("starts_at").(string),
- EndsAt: d.Get("ends_at").(string),
- RepeatUnit: d.Get("repeat_unit").(string),
- RepeatEndsAt: d.Get("repeat_ends_at").(string),
- RepeatInterval: d.Get("repeat_interval").(int),
- Tags: stringsFromSet(d.Get("tags").(*schema.Set)),
- }
-
- fmt.Printf("%v", a)
-
- return a, nil
-}
-
-func resourceDataFromMaintenanceWindows(s *checkly.MaintenanceWindow, d *schema.ResourceData) error {
- d.Set("name", s.Name)
- d.Set("starts_at", s.StartsAt)
- d.Set("ends_at", s.EndsAt)
- d.Set("repeat_unit", s.RepeatUnit)
- d.Set("repeat_ends_at", s.RepeatEndsAt)
- d.Set("repeat_interval", s.RepeatInterval)
- d.Set("tags", s.Tags)
- return nil
-}
-
-func resourceMaintenanceWindowCreate(d *schema.ResourceData, client interface{}) error {
- mw, err := maintenanceWindowsFromResourceData(d)
- if err != nil {
- return fmt.Errorf("resourceMaintenanceWindowCreate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- result, err := client.(checkly.Client).CreateMaintenanceWindow(ctx, mw)
-
- if err != nil {
- return fmt.Errorf("CreateMaintenanceWindows: API error: %w", err)
- }
-
- d.SetId(fmt.Sprintf("%d", result.ID))
- return resourceMaintenanceWindowRead(d, client)
-}
-
-func resourceMaintenanceWindowUpdate(d *schema.ResourceData, client interface{}) error {
- mw, err := maintenanceWindowsFromResourceData(d)
- if err != nil {
- return fmt.Errorf("resourceMaintenanceWindowUpdate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- _, err = client.(checkly.Client).UpdateMaintenanceWindow(ctx, mw.ID, mw)
- if err != nil {
- return fmt.Errorf("resourceMaintenanceWindowUpdate: API error: %w", err)
- }
- d.SetId(fmt.Sprintf("%d", mw.ID))
- return resourceMaintenanceWindowRead(d, client)
-}
-
-func resourceMaintenanceWindowDelete(d *schema.ResourceData, client interface{}) error {
- ID, err := strconv.ParseInt(d.Id(), 10, 64)
- if err != nil {
- return fmt.Errorf("resourceMaintenanceWindowDelete: ID %s is not numeric: %w", d.Id(), err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- err = client.(checkly.Client).DeleteMaintenanceWindow(ctx, ID)
- if err != nil {
- return fmt.Errorf("resourceMaintenanceWindowDelete: API error: %w", err)
- }
- return nil
-}
-
-func resourceMaintenanceWindowRead(d *schema.ResourceData, client interface{}) error {
- ID, err := strconv.ParseInt(d.Id(), 10, 64)
- if err != nil {
- return fmt.Errorf("resourceMaintenanceWindowRead: ID %s is not numeric: %w", d.Id(), err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- mw, err := client.(checkly.Client).GetMaintenanceWindow(ctx, ID)
- defer cancel()
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- d.SetId("")
- return nil
- }
- return fmt.Errorf("resourceMaintenanceWindowRead: API error: %w", err)
- }
- return resourceDataFromMaintenanceWindows(mw, d)
-}
diff --git a/checkly/resource_private_locations.go b/checkly/resource_private_locations.go
deleted file mode 100644
index d0e3f37..0000000
--- a/checkly/resource_private_locations.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package checkly
-
-import (
- "context"
- "fmt"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- checkly "github.com/checkly/checkly-go-sdk"
-)
-
-func resourcePrivateLocation() *schema.Resource {
- return &schema.Resource{
- Create: resourcePrivateLocationCreate,
- Read: resourcePrivateLocationRead,
- Update: resourcePrivateLocationUpdate,
- Delete: resourcePrivateLocationDelete,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
- Schema: map[string]*schema.Schema{
- "name": {
- Type: schema.TypeString,
- Required: true,
- Description: "The private location name.",
- },
- "slug_name": {
- Type: schema.TypeString,
- Required: true,
- Description: "Valid slug name.",
- },
- "icon": {
- Type: schema.TypeString,
- Optional: true,
- Default: "location",
- Description: "Icon assigned to the private location.",
- },
- "keys": {
- Type: schema.TypeSet,
- Computed: true,
- Sensitive: true,
- Elem: &schema.Schema{
- Type: schema.TypeString,
- },
- Description: "Private location API keys.",
- },
- },
- }
-}
-
-func privateLocationFromResourceData(d *schema.ResourceData) (checkly.PrivateLocation, error) {
- return checkly.PrivateLocation{
- Name: d.Get("name").(string),
- SlugName: d.Get("slug_name").(string),
- Icon: d.Get("icon").(string),
- }, nil
-}
-
-func resourcePrivateLocationCreate(d *schema.ResourceData, client interface{}) error {
- pl, err := privateLocationFromResourceData(d)
- if err != nil {
- return fmt.Errorf("resourcePrivateLocationCreate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- result, err := client.(checkly.Client).CreatePrivateLocation(ctx, pl)
- if err != nil {
- return fmt.Errorf("CreatePrivateLocation: API error: %w", err)
- }
- d.SetId(result.ID)
-
- var keys = []string{result.Keys[0].RawKey}
- d.Set("keys", keys)
- return resourcePrivateLocationRead(d, client)
-}
-
-func resourceDataFromPrivateLocation(pl *checkly.PrivateLocation, d *schema.ResourceData) error {
- d.Set("name", pl.Name)
- d.Set("slug_name", pl.SlugName)
- d.Set("icon", pl.Icon)
- return nil
-}
-
-func resourcePrivateLocationUpdate(d *schema.ResourceData, client interface{}) error {
- pl, err := privateLocationFromResourceData(d)
- if err != nil {
- return fmt.Errorf("resourcePrivateLocationUpdate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- _, err = client.(checkly.Client).UpdatePrivateLocation(ctx, d.Id(), pl)
- if err != nil {
- return fmt.Errorf("resourcePrivateLocationUpdate: API error: %w", err)
- }
- return resourcePrivateLocationRead(d, client)
-}
-
-func resourcePrivateLocationRead(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- pl, err := client.(checkly.Client).GetPrivateLocation(ctx, d.Id())
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- //if resource is deleted remotely, then mark it as
- //successfully gone by unsetting it's ID
- d.SetId("")
- return nil
- }
- return fmt.Errorf("resourcePrivateLocationRead: %w", err)
- }
- return resourceDataFromPrivateLocation(pl, d)
-}
-
-func resourcePrivateLocationDelete(d *schema.ResourceData, client interface{}) error {
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- err := client.(checkly.Client).DeletePrivateLocation(ctx, d.Id())
- if err != nil {
- return fmt.Errorf("resourcePrivateLocationDelete: API error: %w", err)
- }
- return nil
-}
diff --git a/checkly/resource_private_locations_test.go b/checkly/resource_private_locations_test.go
deleted file mode 100644
index d5e5f4e..0000000
--- a/checkly/resource_private_locations_test.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package checkly
-
-import (
- "regexp"
- "testing"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
-)
-
-func TestAccPrivateLocationCheckRequiredFields(t *testing.T) {
- config := `resource "checkly_private_location" "test" {}`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "name" is required`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "slug_name" is required`),
- },
- })
-}
-
-func TestAccPrivateLocationSuccess(t *testing.T) {
- config := `resource "checkly_private_location" "test" {
- name = "New Private Location"
- slug_name = "new-private-location"
- icon = "bell-fill"
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_private_location.test",
- "name",
- "New Private Location",
- ),
- resource.TestCheckResourceAttr(
- "checkly_private_location.test",
- "slug_name",
- "new-private-location",
- ),
- resource.TestCheckResourceAttr(
- "checkly_private_location.test",
- "icon",
- "bell-fill",
- ),
- ),
- },
- })
-}
-
-func TestAccPrivateLocationDefaultIcon(t *testing.T) {
- config := `resource "checkly_private_location" "without_icon" {
- name = "New Private Location"
- slug_name = "new-private-location"
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_private_location.without_icon",
- "name",
- "New Private Location",
- ),
- resource.TestCheckResourceAttr(
- "checkly_private_location.without_icon",
- "slug_name",
- "new-private-location",
- ),
- resource.TestCheckResourceAttr(
- "checkly_private_location.without_icon",
- "icon",
- "location",
- ),
- ),
- },
- })
-}
diff --git a/checkly/resource_snippet.go b/checkly/resource_snippet.go
deleted file mode 100644
index 3ea903a..0000000
--- a/checkly/resource_snippet.go
+++ /dev/null
@@ -1,129 +0,0 @@
-package checkly
-
-import (
- "context"
- "fmt"
- "strconv"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- checkly "github.com/checkly/checkly-go-sdk"
-)
-
-func resourceSnippet() *schema.Resource {
- return &schema.Resource{
- Create: resourceSnippetCreate,
- Read: resourceSnippetRead,
- Update: resourceSnippetUpdate,
- Delete: resourceSnippetDelete,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
- Schema: map[string]*schema.Schema{
- "name": {
- Type: schema.TypeString,
- Required: true,
- Description: "The name of the snippet",
- },
- "script": {
- Type: schema.TypeString,
- Required: true,
- Description: "Your Node.js code that interacts with the API check lifecycle, or functions as a partial for browser checks.",
- },
- },
- }
-}
-
-func resourceSnippetCreate(d *schema.ResourceData, client interface{}) error {
- snippet, err := snippetFromResourceData(d)
- if err != nil {
- return fmt.Errorf("resourceSnippetCreate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- result, err := client.(checkly.Client).CreateSnippet(ctx, snippet)
- if err != nil {
- return fmt.Errorf("CreateSnippet: API error: %w", err)
- }
- d.SetId(fmt.Sprintf("%d", result.ID))
- return resourceSnippetRead(d, client)
-}
-
-func snippetFromResourceData(d *schema.ResourceData) (checkly.Snippet, error) {
- id, err := resourceIDToInt(d.Id())
- if err != nil {
- return checkly.Snippet{}, err
- }
- return checkly.Snippet{
- ID: id,
- Name: d.Get("name").(string),
- Script: d.Get("script").(string),
- }, nil
-}
-
-func resourceDataFromSnippet(s *checkly.Snippet, d *schema.ResourceData) error {
- d.Set("name", s.Name)
- d.Set("script", s.Script)
- return nil
-}
-
-func resourceIDToInt(id string) (int64, error) {
- if id == "" {
- return 0, nil
- }
- res, err := strconv.ParseInt(id, 10, 64)
- if err != nil {
- return 0, err
- }
- return res, nil
-}
-
-func resourceSnippetRead(d *schema.ResourceData, client interface{}) error {
- ID, err := strconv.ParseInt(d.Id(), 10, 64)
- if err != nil {
- return fmt.Errorf("resourceSnippetRead: ID %s is not numeric: %w", d.Id(), err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- snippet, err := client.(checkly.Client).GetSnippet(ctx, ID)
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- //if resource is deleted remotely, then mark it as
- //successfully gone by unsetting it's ID
- d.SetId("")
- return nil
- }
- return fmt.Errorf("resourceSnippetRead: API error: %w", err)
- }
- return resourceDataFromSnippet(snippet, d)
-}
-
-func resourceSnippetUpdate(d *schema.ResourceData, client interface{}) error {
- snippet, err := snippetFromResourceData(d)
- if err != nil {
- return fmt.Errorf("resourceSnippetUpdate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- _, err = client.(checkly.Client).UpdateSnippet(ctx, snippet.ID, snippet)
- if err != nil {
- return fmt.Errorf("resourceSnippetUpdate: API error: %w", err)
- }
- d.SetId(fmt.Sprintf("%d", snippet.ID))
- return resourceSnippetRead(d, client)
-}
-
-func resourceSnippetDelete(d *schema.ResourceData, client interface{}) error {
- ID, err := strconv.ParseInt(d.Id(), 10, 64)
- if err != nil {
- return fmt.Errorf("resourceSnippetDelete: ID %s is not numeric: %w", d.Id(), err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- err = client.(checkly.Client).DeleteSnippet(ctx, ID)
- if err != nil {
- return fmt.Errorf("resourceSnippetDelete: API error: %w", err)
- }
- return nil
-}
diff --git a/checkly/resource_snippets_test.go b/checkly/resource_snippets_test.go
deleted file mode 100644
index 08536db..0000000
--- a/checkly/resource_snippets_test.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package checkly
-
-import (
- "regexp"
- "testing"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
-)
-
-func TestAccSnippetCheckRequiredFields(t *testing.T) {
- config := `resource "checkly_snippet" "test" {}`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "name" is required`),
- },
- {
- Config: config,
- ExpectError: regexp.MustCompile(`The argument "script" is required`),
- },
- })
-}
-
-func TestAccSnippetSuccess(t *testing.T) {
- config := `resource "checkly_snippet" "test" {
- name = "foo"
- script = "console.log('bar')"
- }`
- accTestCase(t, []resource.TestStep{
- {
- Config: config,
- Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttr(
- "checkly_snippet.test",
- "name",
- "foo",
- ),
- resource.TestCheckResourceAttr(
- "checkly_snippet.test",
- "script",
- "console.log('bar')",
- ),
- ),
- },
- })
-}
diff --git a/checkly/resource_trigger_check.go b/checkly/resource_trigger_check.go
deleted file mode 100644
index 18d60ee..0000000
--- a/checkly/resource_trigger_check.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package checkly
-
-import (
- "context"
- "fmt"
- "strconv"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- checkly "github.com/checkly/checkly-go-sdk"
-)
-
-func resourceTriggerCheck() *schema.Resource {
- return &schema.Resource{
- Create: resourceTriggerCheckCreate,
- Read: resourceTriggerCheckRead,
- Delete: resourceTriggerCheckDelete,
- Update: resourceTriggerCheckUpdate,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
- Schema: map[string]*schema.Schema{
- "check_id": {
- Type: schema.TypeString,
- Required: true,
- Description: "The id of the check that you want to attach the trigger to.",
- },
- "token": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
- Description: "The token value created to trigger the check",
- },
- "url": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
- Description: "The request URL to trigger the check run.",
- },
- },
- }
-}
-
-func triggerCheckFromResourceData(data *schema.ResourceData) (checkly.TriggerCheck, error) {
- ID, err := strconv.ParseInt(data.Id(), 10, 64)
- if err != nil {
- if data.Id() != "" {
- return checkly.TriggerCheck{}, err
- }
- ID = 0
- }
- return checkly.TriggerCheck{
- ID: ID,
- CheckId: data.Get("check_id").(string),
- Token: data.Get("token").(string),
- URL: data.Get("url").(string),
- }, nil
-}
-
-func resourceDataFromTriggerCheck(trigger *checkly.TriggerCheck, data *schema.ResourceData) error {
- data.Set("check_id", trigger.CheckId)
- data.Set("token", trigger.Token)
- data.Set("url", trigger.URL)
- return nil
-}
-
-func resourceTriggerCheckCreate(data *schema.ResourceData, client interface{}) error {
- trigger, err := triggerCheckFromResourceData(data)
- if err != nil {
- return fmt.Errorf("resourceTriggerCheckCreate: translation error: %w", err)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- result, err := client.(checkly.Client).CreateTriggerCheck(ctx, trigger.CheckId)
- if err != nil {
- return fmt.Errorf("CreateTriggerCheck: API error: %w", err)
- }
-
- data.SetId(fmt.Sprintf("%d", result.ID))
-
- return resourceTriggerCheckRead(data, client)
-}
-
-func resourceTriggerCheckDelete(data *schema.ResourceData, client interface{}) error {
- trigger, err := triggerCheckFromResourceData(data)
- if err != nil {
- return fmt.Errorf("resourceTriggerCheckDelete: translation error: %w", err)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- err = client.(checkly.Client).DeleteTriggerCheck(ctx, trigger.CheckId)
- if err != nil {
- return fmt.Errorf("DeleteTriggerCheck: API error: %w", err)
- }
-
- return nil
-}
-
-func resourceTriggerCheckRead(data *schema.ResourceData, client interface{}) error {
- trigger, err := triggerCheckFromResourceData(data)
- if err != nil {
- return fmt.Errorf("resourceTriggerCheckRead: ID %s is not numeric: %w", data.Id(), err)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- result, err := client.(checkly.Client).GetTriggerCheck(ctx, trigger.CheckId)
- defer cancel()
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- data.SetId("")
- return nil
- }
- return fmt.Errorf("GetTriggerCheck: API error: %w", err)
- }
-
- return resourceDataFromTriggerCheck(result, data)
-}
-
-func resourceTriggerCheckUpdate(data *schema.ResourceData, client interface{}) error {
- return resourceTriggerCheckRead(data, client)
-}
diff --git a/checkly/resource_trigger_group.go b/checkly/resource_trigger_group.go
deleted file mode 100644
index 1b344df..0000000
--- a/checkly/resource_trigger_group.go
+++ /dev/null
@@ -1,123 +0,0 @@
-package checkly
-
-import (
- "context"
- "fmt"
- "strconv"
- "strings"
-
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
- checkly "github.com/checkly/checkly-go-sdk"
-)
-
-func resourceTriggerGroup() *schema.Resource {
- return &schema.Resource{
- Create: resourceTriggerGroupCreate,
- Read: resourceTriggerGroupRead,
- Delete: resourceTriggerGroupDelete,
- Update: resourceTriggerGroupUpdate,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
- Schema: map[string]*schema.Schema{
- "group_id": {
- Type: schema.TypeInt,
- Required: true,
- Description: "The id of the group that you want to attach the trigger to.",
- },
- "token": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
- Description: "The token value created to trigger the group",
- },
- "url": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
- Description: "The request URL to trigger the group run.",
- },
- },
- }
-}
-
-func triggerGroupFromResourceData(data *schema.ResourceData) (checkly.TriggerGroup, error) {
- ID, err := strconv.ParseInt(data.Id(), 10, 64)
- if err != nil {
- if data.Id() != "" {
- return checkly.TriggerGroup{}, err
- }
- ID = 0
- }
-
- return checkly.TriggerGroup{
- ID: ID,
- GroupId: int64(data.Get("group_id").(int)),
- Token: data.Get("token").(string),
- }, nil
-}
-
-func resourceDataFromTriggerGroup(trigger *checkly.TriggerGroup, data *schema.ResourceData) error {
- data.Set("group_id", trigger.GroupId)
- data.Set("token", trigger.Token)
- data.Set("url", trigger.URL)
- return nil
-}
-
-func resourceTriggerGroupCreate(data *schema.ResourceData, client interface{}) error {
- tc, err := triggerGroupFromResourceData(data)
- if err != nil {
- return fmt.Errorf("resourceTriggerGroupCreate: translation error: %w", err)
- }
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- result, err := client.(checkly.Client).CreateTriggerGroup(ctx, tc.GroupId)
-
- if err != nil {
- return fmt.Errorf("CreateTriggerGroup: API error: %w", err)
- }
-
- data.SetId(fmt.Sprintf("%d", result.ID))
-
- return resourceTriggerGroupRead(data, client)
-}
-
-func resourceTriggerGroupDelete(data *schema.ResourceData, client interface{}) error {
- tc, err := triggerGroupFromResourceData(data)
- if err != nil {
- return fmt.Errorf("resourceTriggerGroupDelete: translation error: %w", err)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- defer cancel()
- err = client.(checkly.Client).DeleteTriggerGroup(ctx, tc.GroupId)
- if err != nil {
- return fmt.Errorf("DeleteTriggerGroup: API error: %w", err)
- }
-
- return nil
-}
-
-func resourceTriggerGroupRead(data *schema.ResourceData, client interface{}) error {
- trigger, err := triggerGroupFromResourceData(data)
- if err != nil {
- return fmt.Errorf("resourceTriggerGroupRead: ID %s is not numeric: %w", data.Id(), err)
- }
-
- ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
- result, err := client.(checkly.Client).GetTriggerGroup(ctx, trigger.GroupId)
- defer cancel()
- if err != nil {
- if strings.Contains(err.Error(), "404") {
- data.SetId("")
- return nil
- }
- return fmt.Errorf("GetTriggerGroup: API error: %w", err)
- }
- return resourceDataFromTriggerGroup(result, data)
-}
-
-func resourceTriggerGroupUpdate(data *schema.ResourceData, client interface{}) error {
- return resourceTriggerCheckRead(data, client)
-}
diff --git a/docs/data-sources/static_ips.md b/docs/data-sources/static_ips.md
index c7c073a..5c61dc6 100644
--- a/docs/data-sources/static_ips.md
+++ b/docs/data-sources/static_ips.md
@@ -23,4 +23,4 @@ description: |-
### Read-Only
- `addresses` (Set of String) Static IP addresses for Checkly's runner infrastructure.
-- `id` (String) ID of the static IPs data source.
+- `id` (String) The ID of this data source.
diff --git a/docs/index.md b/docs/index.md
index e487bc6..316481b 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -61,14 +61,14 @@ resource "checkly_check" "example_check" {
"us-west-1"
]
- request {
+ request = {
url = "https://api.example.com/"
follow_redirects = true
- assertion {
+ assertions = [{
source = "STATUS_CODE"
comparison = "EQUALS"
target = "200"
- }
+ }]
}
}
```
@@ -76,13 +76,10 @@ resource "checkly_check" "example_check" {
## Schema
-### Required
-
-- `api_key` (String)
-
### Optional
- `account_id` (String)
+- `api_key` (String, Sensitive)
- `api_url` (String)
> For additional documentation and examples, check the Resources sections.
\ No newline at end of file
diff --git a/docs/resources/alert_channel.md b/docs/resources/alert_channel.md
index 2672f4f..4596edf 100644
--- a/docs/resources/alert_channel.md
+++ b/docs/resources/alert_channel.md
@@ -3,19 +3,19 @@
page_title: "checkly_alert_channel Resource - terraform-provider-checkly"
subcategory: ""
description: |-
- Allows you to define alerting channels for the checks and groups in your account
+ Allows you to define alerting channels for the checks and groups in your account.
---
# checkly_alert_channel (Resource)
-Allows you to define alerting channels for the checks and groups in your account
+Allows you to define alerting channels for the checks and groups in your account.
## Example Usage
```terraform
# An Email alert channel
resource "checkly_alert_channel" "email_ac" {
- email {
+ email = {
address = "john@example.com"
}
send_recovery = true
@@ -27,7 +27,7 @@ resource "checkly_alert_channel" "email_ac" {
# A SMS alert channel
resource "checkly_alert_channel" "sms_ac" {
- sms {
+ sms = {
name = "john"
number = "+5491100001111"
}
@@ -37,7 +37,7 @@ resource "checkly_alert_channel" "sms_ac" {
# A Slack alert channel
resource "checkly_alert_channel" "slack_ac" {
- slack {
+ slack = {
channel = "#checkly-notifications"
url = "https://hooks.slack.com/services/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"
}
@@ -45,7 +45,7 @@ resource "checkly_alert_channel" "slack_ac" {
# An Opsgenie alert channel
resource "checkly_alert_channel" "opsgenie_ac" {
- opsgenie {
+ opsgenie = {
name = "opsalerts"
api_key = "fookey"
region = "fooregion"
@@ -55,7 +55,7 @@ resource "checkly_alert_channel" "opsgenie_ac" {
# A Pagerduty alert channel
resource "checkly_alert_channel" "pagerduty_ac" {
- pagerduty {
+ pagerduty = {
account = "checkly"
service_key = "key1"
service_name = "pdalert"
@@ -64,7 +64,7 @@ resource "checkly_alert_channel" "pagerduty_ac" {
# A Webhook alert channel
resource "checkly_alert_channel" "webhook_ac" {
- webhook {
+ webhook = {
name = "foo"
method = "get"
template = "footemplate"
@@ -75,7 +75,7 @@ resource "checkly_alert_channel" "webhook_ac" {
# A Firehydran alert channel integration
resource "checkly_alert_channel" "firehydrant_ac" {
- webhook {
+ webhook = {
name = "firehydrant"
method = "post"
template = <
+
### Nested Schema for `call`
Required:
@@ -141,7 +142,7 @@ Required:
- `number` (String) The mobile number to receive the alerts
-
+
### Nested Schema for `email`
Required:
@@ -149,7 +150,7 @@ Required:
- `address` (String) The email address of this email alert channel.
-
+
### Nested Schema for `opsgenie`
Required:
@@ -160,7 +161,7 @@ Required:
- `region` (String)
-
+
### Nested Schema for `pagerduty`
Required:
@@ -173,7 +174,7 @@ Optional:
- `service_name` (String)
-
+
### Nested Schema for `slack`
Required:
@@ -182,7 +183,7 @@ Required:
- `url` (String) The Slack webhook URL
-
+
### Nested Schema for `sms`
Required:
@@ -191,7 +192,7 @@ Required:
- `number` (String) The mobile number to receive the alerts
-
+
### Nested Schema for `webhook`
Required:
diff --git a/docs/resources/check.md b/docs/resources/check.md
index bfbddbe..b8a3ea2 100644
--- a/docs/resources/check.md
+++ b/docs/resources/check.md
@@ -3,12 +3,12 @@
page_title: "checkly_check Resource - terraform-provider-checkly"
subcategory: ""
description: |-
- Checks allows you to monitor key webapp flows, backend API's and set up alerting, so you get a notification when things break or slow down.
+ Check groups allow you to group together a set of related checks, which can also share default settings for various attributes.
---
# checkly_check (Resource)
-Checks allows you to monitor key webapp flows, backend API's and set up alerting, so you get a notification when things break or slow down.
+Check groups allow you to group together a set of related checks, which can also share default settings for various attributes.
## Example Usage
@@ -26,15 +26,15 @@ resource "checkly_check" "example_check" {
"us-west-1"
]
- request {
+ request = {
url = "https://api.example.com/"
follow_redirects = true
skip_ssl = false
- assertion {
+ assertions = [{
source = "STATUS_CODE"
comparison = "EQUALS"
target = "200"
- }
+ }]
}
}
@@ -54,19 +54,19 @@ resource "checkly_check" "example_check_2" {
"ap-south-1",
]
- alert_settings {
+ alert_settings = {
escalation_type = "RUN_BASED"
- run_based_escalation {
+ run_based_escalation = {
failed_run_threshold = 1
}
- reminders {
+ reminders = {
amount = 1
}
}
- retry_strategy {
+ retry_strategy = {
type = "FIXED"
base_backoff_seconds = 60
max_duration_seconds = 600
@@ -74,7 +74,7 @@ resource "checkly_check" "example_check_2" {
same_region = false
}
- request {
+ request = {
follow_redirects = true
skip_ssl = false
url = "http://api.example.com/"
@@ -87,21 +87,21 @@ resource "checkly_check" "example_check_2" {
X-Bogus = "bogus"
}
- assertion {
+ assertion = {
source = "JSON_BODY"
property = "code"
comparison = "HAS_VALUE"
target = "authentication.failed"
}
- assertion {
+ assertion = {
source = "STATUS_CODE"
property = ""
comparison = "EQUALS"
target = "401"
}
- basic_auth {
+ basic_auth = {
username = ""
password = ""
}
@@ -137,13 +137,13 @@ EOT
# Connection checks with alert channels
resource "checkly_alert_channel" "email_ac1" {
- email {
+ email = {
address = "info1@example.com"
}
}
resource "checkly_alert_channel" "email_ac2" {
- email {
+ email = {
address = "info2@example.com"
}
}
@@ -152,15 +152,16 @@ resource "checkly_check" "example_check" {
name = "Example check"
# ...
- alert_channel_subscription {
- channel_id = checkly_alert_channel.email_ac1.id
- activated = true
- }
-
- alert_channel_subscription {
- channel_id = checkly_alert_channel.email_ac2.id
- activated = true
- }
+ alert_channel_subscriptions = [
+ {
+ channel_id = checkly_alert_channel.email_ac1.id
+ activated = true
+ },
+ {
+ channel_id = checkly_alert_channel.email_ac2.id
+ activated = true
+ }
+ ]
}
# An alternative syntax for add the script is by referencing an external file
@@ -196,23 +197,23 @@ resource "checkly_check" "example_check" {
### Optional
-- `alert_channel_subscription` (Block List) An array of channel IDs and whether they're activated or not. If you don't set at least one alert subscription for your check, we won't be able to alert you in case something goes wrong with it. (see [below for nested schema](#nestedblock--alert_channel_subscription))
-- `alert_settings` (Block List, Max: 1) (see [below for nested schema](#nestedblock--alert_settings))
+- `alert_channel_subscription` (Attributes List) An array of channel IDs and whether they're activated or not. If you don't set at least one alert subscription for your check, we won't be able to alert you in case something goes wrong with it. (see [below for nested schema](#nestedatt--alert_channel_subscription))
+- `alert_settings` (Attributes) (see [below for nested schema](#nestedatt--alert_settings))
- `degraded_response_time` (Number) The response time in milliseconds starting from which a check should be considered degraded. Possible values are between 0 and 30000. (Default `15000`).
- `double_check` (Boolean, Deprecated) Setting this to `true` will trigger a retry when a check fails from the failing region and another, randomly selected region before marking the check as failed.
-- `environment_variable` (Block List) Key/value pairs for setting environment variables during check execution, add locked = true to keep value hidden, add secret = true to create a secret variable. These are only relevant for browser checks. Use global environment variables whenever possible. (see [below for nested schema](#nestedblock--environment_variable))
+- `environment_variable` (Attributes List) Introduce additional environment variables to the check execution environment. Only relevant for browser checks. Prefer global environment variables when possible. (see [below for nested schema](#nestedatt--environment_variable))
- `environment_variables` (Map of String, Deprecated) Key/value pairs for setting environment variables during check execution. These are only relevant for browser checks. Use global environment variables whenever possible.
-- `frequency_offset` (Number) This property only valid for API high frequency checks. To create a hight frequency check, the property `frequency` must be `0` and `frequency_offset` could be `10`, `20` or `30`.
+- `frequency_offset` (Number) This property is only valid for high frequency API checks. To create a high frequency check, the property `frequency` must be `0` and `frequency_offset` could be `10`, `20` or `30`.
- `group_id` (Number) The id of the check group this check is part of.
- `group_order` (Number) The position of this check in a check group. It determines in what order checks are run when a group is triggered from the API or from CI/CD.
- `local_setup_script` (String) A valid piece of Node.js code to run in the setup phase.
- `local_teardown_script` (String) A valid piece of Node.js code to run in the teardown phase.
-- `locations` (Set of String) An array of one or more data center locations where to run the this check. (Default ["us-east-1"])
+- `locations` (Set of String) An array of one or more data center locations where to run the checks.
- `max_response_time` (Number) The response time in milliseconds starting from which a check should be considered failing. Possible values are between 0 and 30000. (Default `30000`).
- `muted` (Boolean) Determines if any notifications will be sent out when a check fails/degrades/recovers.
- `private_locations` (Set of String) An array of one or more private locations slugs.
-- `request` (Block Set, Max: 1) An API check might have one request config. (see [below for nested schema](#nestedblock--request))
-- `retry_strategy` (Block Set, Max: 1) A strategy for retrying failed check runs. (see [below for nested schema](#nestedblock--retry_strategy))
+- `request` (Attributes) (see [below for nested schema](#nestedatt--request))
+- `retry_strategy` (Attributes) A strategy for retrying failed check runs. (see [below for nested schema](#nestedatt--retry_strategy))
- `run_parallel` (Boolean) Determines if the check should run in all selected locations in parallel or round-robin.
- `runtime_id` (String) The id of the runtime to use for this check.
- `script` (String) A valid piece of Node.js JavaScript code describing a browser interaction with the Puppeteer/Playwright framework or a reference to an external JavaScript file.
@@ -220,7 +221,7 @@ resource "checkly_check" "example_check" {
- `should_fail` (Boolean) Allows to invert the behaviour of when a check is considered to fail. Allows for validating error status like 404.
- `ssl_check` (Boolean, Deprecated) Determines if the SSL certificate should be validated for expiry.
- `ssl_check_domain` (String) A valid fully qualified domain name (FQDN) to check its SSL certificate.
-- `tags` (Set of String) A list of tags for organizing and filtering checks.
+- `tags` (Set of String) Tags for organizing and filtering checks.
- `teardown_snippet_id` (Number) An ID reference to a snippet to use in the teardown phase of an API check.
- `use_global_alert_settings` (Boolean) When true, the account level alert settings will be used, not the alert setting defined on this check.
@@ -228,7 +229,7 @@ resource "checkly_check" "example_check" {
- `id` (String) The ID of this resource.
-
+
### Nested Schema for `alert_channel_subscription`
Required:
@@ -237,28 +238,28 @@ Required:
- `channel_id` (Number)
-
+
### Nested Schema for `alert_settings`
Optional:
- `escalation_type` (String) Determines what type of escalation to use. Possible values are `RUN_BASED` or `TIME_BASED`.
-- `parallel_run_failure_threshold` (Block List) (see [below for nested schema](#nestedblock--alert_settings--parallel_run_failure_threshold))
-- `reminders` (Block List) (see [below for nested schema](#nestedblock--alert_settings--reminders))
-- `run_based_escalation` (Block List) (see [below for nested schema](#nestedblock--alert_settings--run_based_escalation))
-- `ssl_certificates` (Block Set, Deprecated) (see [below for nested schema](#nestedblock--alert_settings--ssl_certificates))
-- `time_based_escalation` (Block List) (see [below for nested schema](#nestedblock--alert_settings--time_based_escalation))
+- `parallel_run_failure_threshold` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--parallel_run_failure_threshold))
+- `reminders` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--reminders))
+- `run_based_escalation` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--run_based_escalation))
+- `ssl_certificates` (Attributes, Deprecated) At what interval the reminders should be sent. (see [below for nested schema](#nestedatt--alert_settings--ssl_certificates))
+- `time_based_escalation` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--time_based_escalation))
-
+
### Nested Schema for `alert_settings.parallel_run_failure_threshold`
Optional:
- `enabled` (Boolean) Applicable only for checks scheduled in parallel in multiple locations.
-- `percentage` (Number) Possible values are `10`, `20`, `30`, `40`, `50`, `60`, `70`, `80`, `100`, and `100`. (Default `10`).
+- `percentage` (Number) Possible values are `10`, `20`, `30`, `40`, `50`, `60`, `70`, `80`, `90`, and `100`. (Default `10`).
-
+
### Nested Schema for `alert_settings.reminders`
Optional:
@@ -267,7 +268,7 @@ Optional:
- `interval` (Number) Possible values are `5`, `10`, `15`, and `30`. (Default `5`).
-
+
### Nested Schema for `alert_settings.run_based_escalation`
Optional:
@@ -275,7 +276,7 @@ Optional:
- `failed_run_threshold` (Number) After how many failed consecutive check runs an alert notification should be sent. Possible values are between 1 and 5. (Default `1`).
-
+
### Nested Schema for `alert_settings.ssl_certificates`
Optional:
@@ -284,7 +285,7 @@ Optional:
- `enabled` (Boolean) Determines if alert notifications should be sent for expiring SSL certificates. Possible values `true`, and `false`. (Default `false`).
-
+
### Nested Schema for `alert_settings.time_based_escalation`
Optional:
@@ -293,21 +294,21 @@ Optional:
-
+
### Nested Schema for `environment_variable`
Required:
-- `key` (String)
-- `value` (String)
+- `key` (String) The name of the environment variable.
+- `value` (String, Sensitive) The value of the environment variable. By default the value is plain text and can be seen by any team member. It will also be present in check results and logs.
Optional:
-- `locked` (Boolean)
-- `secret` (Boolean)
+- `locked` (Boolean) Locked environment variables are encrypted at rest and in flight on the Checkly backend and are only decrypted when needed. Their value is hidden by default, but can be accessed by team members with the appropriate permissions.
+- `secret` (Boolean) Secret environment variables are always encrypted and their value is never shown to any user. However, keep in mind that your Terraform state will still contain the value.
-
+
### Nested Schema for `request`
Required:
@@ -316,8 +317,8 @@ Required:
Optional:
-- `assertion` (Block Set) A request can have multiple assertions. (see [below for nested schema](#nestedblock--request--assertion))
-- `basic_auth` (Block Set, Max: 1) Set up HTTP basic authentication (username & password). (see [below for nested schema](#nestedblock--request--basic_auth))
+- `assertion` (Attributes List) (see [below for nested schema](#nestedatt--request--assertion))
+- `basic_auth` (Attributes) Credentials for Basic HTTP authentication. (see [below for nested schema](#nestedatt--request--basic_auth))
- `body` (String) The body of the request.
- `body_type` (String) The `Content-Type` header of the request. Possible values `NONE`, `JSON`, `FORM`, `RAW`, and `GRAPHQL`.
- `follow_redirects` (Boolean)
@@ -327,31 +328,31 @@ Optional:
- `query_parameters` (Map of String)
- `skip_ssl` (Boolean)
-
+
### Nested Schema for `request.assertion`
Required:
- `comparison` (String) The type of comparison to be executed between expected and actual value of the assertion. Possible values `EQUALS`, `NOT_EQUALS`, `HAS_KEY`, `NOT_HAS_KEY`, `HAS_VALUE`, `NOT_HAS_VALUE`, `IS_EMPTY`, `NOT_EMPTY`, `GREATER_THAN`, `LESS_THAN`, `CONTAINS`, `NOT_CONTAINS`, `IS_NULL`, and `NOT_NULL`.
- `source` (String) The source of the asserted value. Possible values `STATUS_CODE`, `JSON_BODY`, `HEADERS`, `TEXT_BODY`, and `RESPONSE_TIME`.
+- `target` (String)
Optional:
- `property` (String)
-- `target` (String)
-
+
### Nested Schema for `request.basic_auth`
Required:
-- `password` (String)
+- `password` (String, Sensitive)
- `username` (String)
-
+
### Nested Schema for `retry_strategy`
Required:
diff --git a/docs/resources/check_group.md b/docs/resources/check_group.md
index 9fc618b..5a1fa31 100644
--- a/docs/resources/check_group.md
+++ b/docs/resources/check_group.md
@@ -3,12 +3,12 @@
page_title: "checkly_check_group Resource - terraform-provider-checkly"
subcategory: ""
description: |-
- Check groups allow you to group together a set of related checks, which can also share default settings for various attributes.
+ Check groups allow you to group together a set of related checks, which can also share default settings for various attributes.
---
# checkly_check_group (Resource)
-Check groups allow you to group together a set of related checks, which can also share default settings for various attributes.
+Check groups allow you to group together a set of related checks, which can also share default settings for various attributes.
## Example Usage
@@ -25,7 +25,7 @@ resource "checkly_check_group" "test_group1" {
"eu-west-1",
]
concurrency = 3
- api_check_defaults {
+ api_check_defaults = {
url = "http://example.com/"
headers = {
X-Test = "foo"
@@ -35,48 +35,49 @@ resource "checkly_check_group" "test_group1" {
query = "foo"
}
- assertion {
+ assertion = {
source = "STATUS_CODE"
property = ""
comparison = "EQUALS"
target = "200"
}
- assertion {
+ assertion = {
source = "TEXT_BODY"
property = ""
comparison = "CONTAINS"
target = "welcome"
}
- basic_auth {
+ basic_auth = {
username = "user"
password = "pass"
}
}
- environment_variable {
- key = "TEST_ENV_VAR"
- value = "Hello world"
- locked = false
- }
-
- environment_variable {
- key = "ADDITIONAL_ENV_VAR"
- value = "test value"
- locked = true
- }
+ environment_variables = [
+ {
+ key = "TEST_ENV_VAR"
+ value = "Hello world"
+ locked = false
+ },
+ {
+ key = "ADDITIONAL_ENV_VAR"
+ value = "test value"
+ locked = true
+ }
+ ]
use_global_alert_settings = false
- alert_settings {
+ alert_settings = {
escalation_type = "RUN_BASED"
- run_based_escalation {
+ run_based_escalation = {
failed_run_threshold = 1
}
- reminders {
+ reminders = {
amount = 2
interval = 5
}
@@ -96,7 +97,7 @@ resource "checkly_check" "test_check1" {
"us-west-1"
]
- request {
+ request = {
url = "https://api.example.com/"
}
group_id = checkly_check_group.test_group1.id
@@ -106,13 +107,13 @@ resource "checkly_check" "test_check1" {
# Using with alert channels
resource "checkly_alert_channel" "email_ac1" {
- email {
+ email = {
address = "info@example.com"
}
}
resource "checkly_alert_channel" "email_ac2" {
- email {
+ email = {
address = "info2@example.com"
}
}
@@ -122,15 +123,16 @@ resource "checkly_alert_channel" "email_ac2" {
resource "checkly_check_group" "test_group1" {
name = "My test group 1"
- alert_channel_subscription {
- channel_id = checkly_alert_channel.email_ac1.id
- activated = true
- }
-
- alert_channel_subscription {
- channel_id = checkly_alert_channel.email_ac2.id
- activated = true
- }
+ alert_channel_subscriptions = [
+ {
+ channel_id = checkly_alert_channel.email_ac1.id
+ activated = true
+ },
+ {
+ channel_id = checkly_alert_channel.email_ac2.id
+ activated = true
+ }
+ ]
}
```
@@ -145,18 +147,18 @@ resource "checkly_check_group" "test_group1" {
### Optional
-- `alert_channel_subscription` (Block List) (see [below for nested schema](#nestedblock--alert_channel_subscription))
-- `alert_settings` (Block List, Max: 1) (see [below for nested schema](#nestedblock--alert_settings))
-- `api_check_defaults` (Block Set, Max: 1) (see [below for nested schema](#nestedblock--api_check_defaults))
+- `alert_channel_subscription` (Attributes List) An array of channel IDs and whether they're activated or not. If you don't set at least one alert subscription for your check, we won't be able to alert you in case something goes wrong with it. (see [below for nested schema](#nestedatt--alert_channel_subscription))
+- `alert_settings` (Attributes) (see [below for nested schema](#nestedatt--alert_settings))
+- `api_check_defaults` (Attributes) (see [below for nested schema](#nestedatt--api_check_defaults))
- `double_check` (Boolean, Deprecated) Setting this to `true` will trigger a retry when a check fails from the failing region and another, randomly selected region before marking the check as failed.
-- `environment_variable` (Block List) Key/value pairs for setting environment variables during check execution, add locked = true to keep value hidden, add secret = true to create a secret variable. These are only relevant for browser checks. Use global environment variables whenever possible. (see [below for nested schema](#nestedblock--environment_variable))
+- `environment_variable` (Attributes List) Introduce additional environment variables to the check execution environment. Only relevant for browser checks. Prefer global environment variables when possible. (see [below for nested schema](#nestedatt--environment_variable))
- `environment_variables` (Map of String, Deprecated) Key/value pairs for setting environment variables during check execution. These are only relevant for browser checks. Use global environment variables whenever possible.
- `local_setup_script` (String) A valid piece of Node.js code to run in the setup phase of an API check in this group.
- `local_teardown_script` (String) A valid piece of Node.js code to run in the teardown phase of an API check in this group.
- `locations` (Set of String) An array of one or more data center locations where to run the checks.
- `muted` (Boolean) Determines if any notifications will be sent out when a check in this group fails and/or recovers.
- `private_locations` (Set of String) An array of one or more private locations slugs.
-- `retry_strategy` (Block Set, Max: 1) A strategy for retrying failed check runs. (see [below for nested schema](#nestedblock--retry_strategy))
+- `retry_strategy` (Attributes) A strategy for retrying failed check runs. (see [below for nested schema](#nestedatt--retry_strategy))
- `run_parallel` (Boolean) Determines if the checks in the group should run in all selected locations in parallel or round-robin.
- `runtime_id` (String) The id of the runtime to use for this group.
- `setup_snippet_id` (Number) An ID reference to a snippet to use in the setup phase of an API check.
@@ -168,7 +170,7 @@ resource "checkly_check_group" "test_group1" {
- `id` (String) The ID of this resource.
-
+
### Nested Schema for `alert_channel_subscription`
Required:
@@ -177,28 +179,28 @@ Required:
- `channel_id` (Number)
-
+
### Nested Schema for `alert_settings`
Optional:
- `escalation_type` (String) Determines what type of escalation to use. Possible values are `RUN_BASED` or `TIME_BASED`.
-- `parallel_run_failure_threshold` (Block List) (see [below for nested schema](#nestedblock--alert_settings--parallel_run_failure_threshold))
-- `reminders` (Block List) (see [below for nested schema](#nestedblock--alert_settings--reminders))
-- `run_based_escalation` (Block List) (see [below for nested schema](#nestedblock--alert_settings--run_based_escalation))
-- `ssl_certificates` (Block Set, Deprecated) (see [below for nested schema](#nestedblock--alert_settings--ssl_certificates))
-- `time_based_escalation` (Block List) (see [below for nested schema](#nestedblock--alert_settings--time_based_escalation))
+- `parallel_run_failure_threshold` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--parallel_run_failure_threshold))
+- `reminders` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--reminders))
+- `run_based_escalation` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--run_based_escalation))
+- `ssl_certificates` (Attributes, Deprecated) At what interval the reminders should be sent. (see [below for nested schema](#nestedatt--alert_settings--ssl_certificates))
+- `time_based_escalation` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--time_based_escalation))
-
+
### Nested Schema for `alert_settings.parallel_run_failure_threshold`
Optional:
- `enabled` (Boolean) Applicable only for checks scheduled in parallel in multiple locations.
-- `percentage` (Number) Possible values are `10`, `20`, `30`, `40`, `50`, `60`, `70`, `80`, `100`, and `100`. (Default `10`).
+- `percentage` (Number) Possible values are `10`, `20`, `30`, `40`, `50`, `60`, `70`, `80`, `90`, and `100`. (Default `10`).
-
+
### Nested Schema for `alert_settings.reminders`
Optional:
@@ -207,7 +209,7 @@ Optional:
- `interval` (Number) Possible values are `5`, `10`, `15`, and `30`. (Default `5`).
-
+
### Nested Schema for `alert_settings.run_based_escalation`
Optional:
@@ -215,16 +217,16 @@ Optional:
- `failed_run_threshold` (Number) After how many failed consecutive check runs an alert notification should be sent. Possible values are between 1 and 5. (Default `1`).
-
+
### Nested Schema for `alert_settings.ssl_certificates`
Optional:
-- `alert_threshold` (Number) At what moment in time to start alerting on SSL certificates. Possible values `3`, `7`, `14`, `30`. (Default `3`).
-- `enabled` (Boolean) Determines if alert notifications should be sent for expiring SSL certificates.
+- `alert_threshold` (Number) How long before SSL certificate expiry to send alerts. Possible values `3`, `7`, `14`, `30`. (Default `3`).
+- `enabled` (Boolean) Determines if alert notifications should be sent for expiring SSL certificates. Possible values `true`, and `false`. (Default `false`).
-
+
### Nested Schema for `alert_settings.time_based_escalation`
Optional:
@@ -233,7 +235,7 @@ Optional:
-
+
### Nested Schema for `api_check_defaults`
Required:
@@ -242,12 +244,12 @@ Required:
Optional:
-- `assertion` (Block Set) (see [below for nested schema](#nestedblock--api_check_defaults--assertion))
-- `basic_auth` (Block Set, Max: 1) (see [below for nested schema](#nestedblock--api_check_defaults--basic_auth))
+- `assertion` (Attributes List) (see [below for nested schema](#nestedatt--api_check_defaults--assertion))
+- `basic_auth` (Attributes) Credentials for Basic HTTP authentication. (see [below for nested schema](#nestedatt--api_check_defaults--basic_auth))
- `headers` (Map of String)
- `query_parameters` (Map of String)
-
+
### Nested Schema for `api_check_defaults.assertion`
Required:
@@ -261,31 +263,31 @@ Optional:
- `property` (String)
-
+
### Nested Schema for `api_check_defaults.basic_auth`
Required:
-- `password` (String)
+- `password` (String, Sensitive)
- `username` (String)
-
+
### Nested Schema for `environment_variable`
Required:
-- `key` (String)
-- `value` (String)
+- `key` (String) The name of the environment variable.
+- `value` (String, Sensitive) The value of the environment variable. By default the value is plain text and can be seen by any team member. It will also be present in check results and logs.
Optional:
-- `locked` (Boolean)
-- `secret` (Boolean)
+- `locked` (Boolean) Locked environment variables are encrypted at rest and in flight on the Checkly backend and are only decrypted when needed. Their value is hidden by default, but can be accessed by team members with the appropriate permissions.
+- `secret` (Boolean) Secret environment variables are always encrypted and their value is never shown to any user. However, keep in mind that your Terraform state will still contain the value.
-
+
### Nested Schema for `retry_strategy`
Required:
diff --git a/docs/resources/environment_variable.md b/docs/resources/environment_variable.md
index 4308265..8cba751 100644
--- a/docs/resources/environment_variable.md
+++ b/docs/resources/environment_variable.md
@@ -32,7 +32,7 @@ resource "checkly_environment_variable" "variable_2" {
### Required
- `key` (String)
-- `value` (String)
+- `value` (String, Sensitive)
### Optional
diff --git a/docs/resources/heartbeat.md b/docs/resources/heartbeat.md
index 279c19c..b611007 100644
--- a/docs/resources/heartbeat.md
+++ b/docs/resources/heartbeat.md
@@ -16,7 +16,7 @@ Heartbeats allows you to monitor your cron jobs and set up alerting, so you get
resource "checkly_heartbeat" "example-heartbeat" {
name = "Example heartbeat"
activated = true
- heartbeat {
+ heartbeat = {
period = 7
period_unit = "days"
grace = 1
@@ -32,13 +32,13 @@ resource "checkly_heartbeat" "example-heartbeat" {
### Required
- `activated` (Boolean) Determines if the check is running or not. Possible values `true`, and `false`.
-- `heartbeat` (Block Set, Min: 1, Max: 1) (see [below for nested schema](#nestedblock--heartbeat))
+- `heartbeat` (Attributes) (see [below for nested schema](#nestedatt--heartbeat))
- `name` (String) The name of the check.
### Optional
-- `alert_channel_subscription` (Block List) (see [below for nested schema](#nestedblock--alert_channel_subscription))
-- `alert_settings` (Block List, Max: 1) (see [below for nested schema](#nestedblock--alert_settings))
+- `alert_channel_subscription` (Attributes List) An array of channel IDs and whether they're activated or not. If you don't set at least one alert subscription for your check, we won't be able to alert you in case something goes wrong with it. (see [below for nested schema](#nestedatt--alert_channel_subscription))
+- `alert_settings` (Attributes) (see [below for nested schema](#nestedatt--alert_settings))
- `muted` (Boolean) Determines if any notifications will be sent out when a check fails/degrades/recovers.
- `tags` (Set of String) A list of tags for organizing and filtering checks.
- `use_global_alert_settings` (Boolean) When true, the account level alert settings will be used, not the alert setting defined on this check.
@@ -47,7 +47,7 @@ resource "checkly_heartbeat" "example-heartbeat" {
- `id` (String) The ID of this resource.
-
+
### Nested Schema for `heartbeat`
Required:
@@ -57,12 +57,12 @@ Required:
- `period` (Number) How often you expect a ping to the ping URL.
- `period_unit` (String) Possible values `seconds`, `minutes`, `hours` and `days`.
-Optional:
+Read-Only:
- `ping_token` (String) Custom token to generate your ping URL. Checkly will expect a ping to `https://ping.checklyhq.com/[PING_TOKEN]`.
-
+
### Nested Schema for `alert_channel_subscription`
Required:
@@ -71,28 +71,28 @@ Required:
- `channel_id` (Number)
-
+
### Nested Schema for `alert_settings`
Optional:
- `escalation_type` (String) Determines what type of escalation to use. Possible values are `RUN_BASED` or `TIME_BASED`.
-- `parallel_run_failure_threshold` (Block List) (see [below for nested schema](#nestedblock--alert_settings--parallel_run_failure_threshold))
-- `reminders` (Block List) (see [below for nested schema](#nestedblock--alert_settings--reminders))
-- `run_based_escalation` (Block List) (see [below for nested schema](#nestedblock--alert_settings--run_based_escalation))
-- `ssl_certificates` (Block Set, Deprecated) (see [below for nested schema](#nestedblock--alert_settings--ssl_certificates))
-- `time_based_escalation` (Block List) (see [below for nested schema](#nestedblock--alert_settings--time_based_escalation))
+- `parallel_run_failure_threshold` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--parallel_run_failure_threshold))
+- `reminders` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--reminders))
+- `run_based_escalation` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--run_based_escalation))
+- `ssl_certificates` (Attributes, Deprecated) At what interval the reminders should be sent. (see [below for nested schema](#nestedatt--alert_settings--ssl_certificates))
+- `time_based_escalation` (Attributes) (see [below for nested schema](#nestedatt--alert_settings--time_based_escalation))
-
+
### Nested Schema for `alert_settings.parallel_run_failure_threshold`
Optional:
- `enabled` (Boolean) Applicable only for checks scheduled in parallel in multiple locations.
-- `percentage` (Number) Possible values are `10`, `20`, `30`, `40`, `50`, `60`, `70`, `80`, `100`, and `100`. (Default `10`).
+- `percentage` (Number) Possible values are `10`, `20`, `30`, `40`, `50`, `60`, `70`, `80`, `90`, and `100`. (Default `10`).
-
+
### Nested Schema for `alert_settings.reminders`
Optional:
@@ -101,7 +101,7 @@ Optional:
- `interval` (Number) Possible values are `5`, `10`, `15`, and `30`. (Default `5`).
-
+
### Nested Schema for `alert_settings.run_based_escalation`
Optional:
@@ -109,7 +109,7 @@ Optional:
- `failed_run_threshold` (Number) After how many failed consecutive check runs an alert notification should be sent. Possible values are between 1 and 5. (Default `1`).
-
+
### Nested Schema for `alert_settings.ssl_certificates`
Optional:
@@ -118,7 +118,7 @@ Optional:
- `enabled` (Boolean) Determines if alert notifications should be sent for expiring SSL certificates. Possible values `true`, and `false`. (Default `false`).
-
+
### Nested Schema for `alert_settings.time_based_escalation`
Optional:
diff --git a/docs/resources/snippet.md b/docs/resources/snippet.md
index d9dbced..2dd64ba 100644
--- a/docs/resources/snippet.md
+++ b/docs/resources/snippet.md
@@ -43,7 +43,7 @@ EOT
### Required
-- `name` (String) The name of the snippet
+- `name` (String) The name of the snippet.
- `script` (String) Your Node.js code that interacts with the API check lifecycle, or functions as a partial for browser checks.
### Read-Only
diff --git a/docs/resources/trigger_check.md b/docs/resources/trigger_check.md
index 06134eb..0fbf5eb 100644
--- a/docs/resources/trigger_check.md
+++ b/docs/resources/trigger_check.md
@@ -27,11 +27,11 @@ output "test_trigger_check_url" {
### Required
-- `check_id` (String) The id of the check that you want to attach the trigger to.
+- `check_id` (String) The ID of the check that you want to attach the trigger to.
### Optional
-- `token` (String) The token value created to trigger the check
+- `token` (String) The token value created to trigger the check.
- `url` (String) The request URL to trigger the check run.
### Read-Only
diff --git a/docs/resources/trigger_group.md b/docs/resources/trigger_group.md
index 51e0dd0..b51da1a 100644
--- a/docs/resources/trigger_group.md
+++ b/docs/resources/trigger_group.md
@@ -27,11 +27,11 @@ output "test_trigger_group_url" {
### Required
-- `group_id` (Number) The id of the group that you want to attach the trigger to.
+- `group_id` (Number) The ID of the group that you want to attach the trigger to.
### Optional
-- `token` (String) The token value created to trigger the group
+- `token` (String) The token value created to trigger the group.
- `url` (String) The request URL to trigger the group run.
### Read-Only
diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf
index 16499e0..05482bc 100644
--- a/examples/provider/provider.tf
+++ b/examples/provider/provider.tf
@@ -31,13 +31,13 @@ resource "checkly_check" "example_check" {
"us-west-1"
]
- request {
+ request = {
url = "https://api.example.com/"
follow_redirects = true
- assertion {
+ assertions = [{
source = "STATUS_CODE"
comparison = "EQUALS"
target = "200"
- }
+ }]
}
-}
\ No newline at end of file
+}
diff --git a/examples/resources/checkly_alert_channel/resource.tf b/examples/resources/checkly_alert_channel/resource.tf
index b581b60..05e63d5 100644
--- a/examples/resources/checkly_alert_channel/resource.tf
+++ b/examples/resources/checkly_alert_channel/resource.tf
@@ -1,6 +1,6 @@
# An Email alert channel
resource "checkly_alert_channel" "email_ac" {
- email {
+ email = {
address = "john@example.com"
}
send_recovery = true
@@ -12,7 +12,7 @@ resource "checkly_alert_channel" "email_ac" {
# A SMS alert channel
resource "checkly_alert_channel" "sms_ac" {
- sms {
+ sms = {
name = "john"
number = "+5491100001111"
}
@@ -22,7 +22,7 @@ resource "checkly_alert_channel" "sms_ac" {
# A Slack alert channel
resource "checkly_alert_channel" "slack_ac" {
- slack {
+ slack = {
channel = "#checkly-notifications"
url = "https://hooks.slack.com/services/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"
}
@@ -30,7 +30,7 @@ resource "checkly_alert_channel" "slack_ac" {
# An Opsgenie alert channel
resource "checkly_alert_channel" "opsgenie_ac" {
- opsgenie {
+ opsgenie = {
name = "opsalerts"
api_key = "fookey"
region = "fooregion"
@@ -40,7 +40,7 @@ resource "checkly_alert_channel" "opsgenie_ac" {
# A Pagerduty alert channel
resource "checkly_alert_channel" "pagerduty_ac" {
- pagerduty {
+ pagerduty = {
account = "checkly"
service_key = "key1"
service_name = "pdalert"
@@ -49,7 +49,7 @@ resource "checkly_alert_channel" "pagerduty_ac" {
# A Webhook alert channel
resource "checkly_alert_channel" "webhook_ac" {
- webhook {
+ webhook = {
name = "foo"
method = "get"
template = "footemplate"
@@ -60,7 +60,7 @@ resource "checkly_alert_channel" "webhook_ac" {
# A Firehydran alert channel integration
resource "checkly_alert_channel" "firehydrant_ac" {
- webhook {
+ webhook = {
name = "firehydrant"
method = "post"
template = < description for the dashboard.",
+ },
+ "header": schema.StringAttribute{
+ Optional: true,
+ Description: "A piece of text displayed at the top of your dashboard.",
+ },
+ "width": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ Default: stringdefault.StaticString("FULL"),
+ Validators: []validator.String{
+ stringvalidator.OneOf("FULL", "960PX"),
+ },
+ Description: "Determines whether to use the full screen or focus in the center. Possible values `FULL` and `960PX`.",
+ },
+ "refresh_rate": schema.Int32Attribute{
+ Optional: true,
+ Computed: true,
+ Default: int32default.StaticInt32(60),
+ Validators: []validator.Int32{
+ int32validator.OneOf(60, 300, 600),
+ },
+ Description: "How often to refresh the dashboard in seconds. Possible values `60`, '300' and `600`.",
+ },
+ "paginate": schema.BoolAttribute{
+ Optional: true,
+ Computed: true,
+ Default: booldefault.StaticBool(true),
+ Description: "Determines if pagination is on or off.",
+ },
+ "checks_per_page": schema.Int32Attribute{
+ Optional: true,
+ Computed: true,
+ Default: int32default.StaticInt32(15),
+ Description: "Determines how many checks to show per page.",
+ },
+ "pagination_rate": schema.Int32Attribute{
+ Optional: true,
+ Computed: true,
+ Default: int32default.StaticInt32(60),
+ Validators: []validator.Int32{
+ int32validator.OneOf(30, 60, 300),
+ },
+ Description: "How often to trigger pagination in seconds. Possible values `30`, `60` and `300`.",
+ },
+ "tags": schema.SetAttribute{
+ ElementType: types.StringType,
+ Optional: true,
+ Description: "A list of one or more tags that filter which checks to display on the dashboard.",
+ },
+ "hide_tags": schema.BoolAttribute{
+ Optional: true,
+ Computed: true,
+ Default: booldefault.StaticBool(false),
+ Description: "Show or hide the tags on the dashboard.",
+ },
+ "use_tags_and_operator": schema.BoolAttribute{
+ Optional: true,
+ Computed: true,
+ Default: booldefault.StaticBool(false),
+ Description: "Set when to use AND operator for fetching dashboard tags.",
+ },
+ "is_private": schema.BoolAttribute{
+ Optional: true,
+ Computed: true,
+ Default: booldefault.StaticBool(false),
+ Description: "Set your dashboard as private and generate key.",
+ },
+ "key": schema.StringAttribute{
+ Computed: true,
+ Sensitive: true,
+ Description: "The access key when the dashboard is private.",
+ },
+ },
+ }
+}
+
+func (r *DashboardResource) Configure(
+ ctx context.Context,
+ req resource.ConfigureRequest,
+ resp *resource.ConfigureResponse,
+) {
+ client, diags := interop.ClientFromProviderData(req.ProviderData)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ r.client = client
+}
+
+func (r *DashboardResource) ImportState(
+ ctx context.Context,
+ req resource.ImportStateRequest,
+ resp *resource.ImportStateResponse,
+) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *DashboardResource) Create(
+ ctx context.Context,
+ req resource.CreateRequest,
+ resp *resource.CreateResponse,
+) {
+ var plan DashboardResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var desiredModel checkly.Dashboard
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.CreateDashboard(ctx, desiredModel)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Creating Checkly Dashboard",
+ fmt.Sprintf("Could not create dashboard, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Created)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *DashboardResource) Delete(
+ ctx context.Context,
+ req resource.DeleteRequest,
+ resp *resource.DeleteResponse,
+) {
+ var state DashboardResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ err := r.client.DeleteDashboard(ctx, state.ID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Deleting Checkly Dashboard",
+ fmt.Sprintf("Could not delete dashboard, unexpected error: %s", err),
+ )
+
+ return
+ }
+}
+
+func (r *DashboardResource) Read(
+ ctx context.Context,
+ req resource.ReadRequest,
+ resp *resource.ReadResponse,
+) {
+ var state DashboardResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.GetDashboard(ctx, state.ID.ValueString())
+ if err != nil {
+ if sdkutil.IsHTTPNotFoundError(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+
+ resp.Diagnostics.AddError(
+ "Error Reading Checkly Dashboard",
+ fmt.Sprintf("Could not retrieve dashboard, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(state.Refresh(ctx, realizedModel, interop.Loaded)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *DashboardResource) Update(
+ ctx context.Context,
+ req resource.UpdateRequest,
+ resp *resource.UpdateResponse,
+) {
+ var plan DashboardResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var desiredModel checkly.Dashboard
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.UpdateDashboard(
+ ctx,
+ plan.ID.ValueString(),
+ desiredModel,
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Updating Checkly Dashboard",
+ fmt.Sprintf("Could not update dashboard, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Updated)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+var (
+ _ interop.Model[checkly.Dashboard] = (*DashboardResourceModel)(nil)
+)
+
+type DashboardResourceModel struct {
+ ID types.String `tfsdk:"id"`
+ CustomURL types.String `tfsdk:"custom_url"`
+ CustomDomain types.String `tfsdk:"custom_domain"`
+ Logo types.String `tfsdk:"logo"`
+ Favicon types.String `tfsdk:"favicon"`
+ Link types.String `tfsdk:"link"`
+ Description types.String `tfsdk:"description"`
+ Header types.String `tfsdk:"header"`
+ Width types.String `tfsdk:"width"`
+ RefreshRate types.Int32 `tfsdk:"refresh_rate"`
+ Paginate types.Bool `tfsdk:"paginate"`
+ ChecksPerPage types.Int32 `tfsdk:"checks_per_page"`
+ PaginationRate types.Int32 `tfsdk:"pagination_rate"`
+ Tags types.Set `tfsdk:"tags"`
+ HideTags types.Bool `tfsdk:"hide_tags"`
+ UseTagsAndOperator types.Bool `tfsdk:"use_tags_and_operator"`
+ IsPrivate types.Bool `tfsdk:"is_private"`
+ Key types.String `tfsdk:"key"`
+}
+
+func (m *DashboardResourceModel) Refresh(ctx context.Context, from *checkly.Dashboard, flags interop.RefreshFlags) diag.Diagnostics {
+ if flags.Created() {
+ m.ID = types.StringValue(from.DashboardID)
+ }
+
+ m.CustomURL = types.StringValue(from.CustomUrl)
+ m.CustomDomain = types.StringValue(from.CustomDomain)
+ m.Logo = types.StringValue(from.Logo)
+ m.Favicon = types.StringValue(from.Favicon)
+ m.Link = types.StringValue(from.Link)
+ m.Description = types.StringValue(from.Description)
+ m.Header = types.StringValue(from.Header)
+ m.Width = types.StringValue(from.Width)
+ m.RefreshRate = types.Int32Value(int32(from.RefreshRate))
+ m.Paginate = types.BoolValue(from.Paginate)
+ m.ChecksPerPage = types.Int32Value(int32(from.ChecksPerPage))
+ m.PaginationRate = types.Int32Value(int32(from.PaginationRate))
+ m.HideTags = types.BoolValue(from.HideTags)
+ m.UseTagsAndOperator = types.BoolValue(from.UseTagsAndOperator)
+ m.IsPrivate = types.BoolValue(from.IsPrivate)
+ m.Tags = interop.IntoUntypedStringSet(&from.Tags)
+
+ if from.IsPrivate {
+ if len(from.Keys) > 0 {
+ m.Key = types.StringValue(from.Keys[0].RawKey)
+ }
+ } else {
+ m.Key = types.StringNull()
+ }
+
+ return nil
+}
+
+func (m *DashboardResourceModel) Render(ctx context.Context, into *checkly.Dashboard) diag.Diagnostics {
+ into.CustomUrl = m.CustomURL.ValueString()
+ into.CustomDomain = m.CustomDomain.ValueString()
+ into.IsPrivate = m.IsPrivate.ValueBool()
+ into.Logo = m.Logo.ValueString()
+ into.Link = m.Link.ValueString()
+ into.Description = m.Description.ValueString()
+ into.Favicon = m.Favicon.ValueString()
+ into.Header = m.Header.ValueString()
+ into.Width = m.Width.ValueString()
+ into.RefreshRate = int(m.RefreshRate.ValueInt32())
+ into.ChecksPerPage = int(m.ChecksPerPage.ValueInt32())
+ into.PaginationRate = int(m.PaginationRate.ValueInt32())
+ into.Paginate = m.Paginate.ValueBool()
+ into.Tags = interop.FromUntypedStringSet(m.Tags)
+ into.HideTags = m.HideTags.ValueBool()
+ into.UseTagsAndOperator = m.UseTagsAndOperator.ValueBool()
+
+ return nil
+}
diff --git a/internal/provider/resources/environment_variable_resource.go b/internal/provider/resources/environment_variable_resource.go
new file mode 100644
index 0000000..54a149b
--- /dev/null
+++ b/internal/provider/resources/environment_variable_resource.go
@@ -0,0 +1,284 @@
+package resources
+
+import (
+ "context"
+ "fmt"
+
+ "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-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+
+ checkly "github.com/checkly/checkly-go-sdk"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/interop"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/resources/attributes"
+ "github.com/checkly/terraform-provider-checkly/internal/sdkutil"
+)
+
+var (
+ _ resource.Resource = (*EnvironmentVariableResource)(nil)
+ _ resource.ResourceWithConfigure = (*EnvironmentVariableResource)(nil)
+ _ resource.ResourceWithImportState = (*EnvironmentVariableResource)(nil)
+)
+
+type EnvironmentVariableResource struct {
+ client checkly.Client
+}
+
+func NewEnvironmentVariableResource() resource.Resource {
+ return &EnvironmentVariableResource{}
+}
+
+func (r *EnvironmentVariableResource) Metadata(
+ ctx context.Context,
+ req resource.MetadataRequest,
+ resp *resource.MetadataResponse,
+) {
+ resp.TypeName = req.ProviderTypeName + "_environment_variable"
+}
+
+func (r *EnvironmentVariableResource) Schema(
+ ctx context.Context,
+ req resource.SchemaRequest,
+ resp *resource.SchemaResponse,
+) {
+ resp.Schema = schema.Schema{
+ Attributes: map[string]schema.Attribute{
+ "id": attributes.IDAttributeSchema,
+ "key": schema.StringAttribute{
+ Required: true,
+ Description: "", // TODO
+ },
+ "value": schema.StringAttribute{
+ Required: true,
+ Sensitive: true, // FIXME: Keep sensitive? Old code did not set it.
+ Description: "", // TODO
+ },
+ "locked": schema.BoolAttribute{
+ Optional: true,
+ Computed: true,
+ Default: booldefault.StaticBool(false),
+ Description: "", // TODO
+ },
+ "secret": schema.BoolAttribute{
+ Optional: true,
+ Computed: true,
+ Default: booldefault.StaticBool(false),
+ Description: "", // TODO
+ },
+ },
+ }
+}
+
+func (r *EnvironmentVariableResource) Configure(
+ ctx context.Context,
+ req resource.ConfigureRequest,
+ resp *resource.ConfigureResponse,
+) {
+ client, diags := interop.ClientFromProviderData(req.ProviderData)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ r.client = client
+}
+
+func (r *EnvironmentVariableResource) ImportState(
+ ctx context.Context,
+ req resource.ImportStateRequest,
+ resp *resource.ImportStateResponse,
+) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *EnvironmentVariableResource) Create(
+ ctx context.Context,
+ req resource.CreateRequest,
+ resp *resource.CreateResponse,
+) {
+ var plan EnvironmentVariableResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var desiredModel checkly.EnvironmentVariable
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.CreateEnvironmentVariable(
+ ctx,
+ desiredModel,
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Creating Checkly Environment Variable",
+ fmt.Sprintf("Could not create environment variable, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Created)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *EnvironmentVariableResource) Delete(
+ ctx context.Context,
+ req resource.DeleteRequest,
+ resp *resource.DeleteResponse,
+) {
+ var state EnvironmentVariableResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ err := r.client.DeleteEnvironmentVariable(ctx, state.ID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Deleting Checkly Environment Variable",
+ fmt.Sprintf("Could not delete environment variable, unexpected error: %s", err),
+ )
+
+ return
+ }
+}
+
+func (r *EnvironmentVariableResource) Read(
+ ctx context.Context,
+ req resource.ReadRequest,
+ resp *resource.ReadResponse,
+) {
+ var state EnvironmentVariableResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.GetEnvironmentVariable(
+ ctx,
+ state.ID.ValueString(),
+ )
+ if err != nil {
+ if sdkutil.IsHTTPNotFoundError(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+
+ resp.Diagnostics.AddError(
+ "Error Reading Checkly Environment Variable",
+ fmt.Sprintf("Could not retrieve environment variable, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(state.Refresh(ctx, realizedModel, interop.Loaded)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *EnvironmentVariableResource) Update(
+ ctx context.Context,
+ req resource.UpdateRequest,
+ resp *resource.UpdateResponse,
+) {
+ var plan EnvironmentVariableResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var desiredModel checkly.EnvironmentVariable
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.UpdateEnvironmentVariable(
+ ctx,
+ plan.ID.ValueString(),
+ desiredModel,
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Updating Checkly Environment Variable",
+ fmt.Sprintf("Could not update environment variable, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Updated)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+var (
+ _ interop.Model[checkly.EnvironmentVariable] = (*EnvironmentVariableResourceModel)(nil)
+)
+
+type EnvironmentVariableResourceModel struct {
+ ID types.String `tfsdk:"id"`
+ Key types.String `tfsdk:"key"`
+ Value types.String `tfsdk:"value"`
+ Locked types.Bool `tfsdk:"locked"`
+ Secret types.Bool `tfsdk:"secret"`
+}
+
+func (m *EnvironmentVariableResourceModel) Refresh(ctx context.Context, from *checkly.EnvironmentVariable, flags interop.RefreshFlags) diag.Diagnostics {
+ if flags.Created() {
+ m.ID = types.StringValue(from.Key)
+ }
+
+ m.Key = types.StringValue(from.Key)
+ m.Locked = types.BoolValue(from.Locked)
+ m.Secret = types.BoolValue(from.Secret)
+
+ // We can never receive a secret value back from the server. Just assume
+ // the value is still unchanged and only update state if we're not dealing
+ // with a secret.
+ if !from.Secret {
+ m.Value = types.StringValue(from.Value)
+ }
+
+ return nil
+}
+
+func (m *EnvironmentVariableResourceModel) Render(ctx context.Context, into *checkly.EnvironmentVariable) diag.Diagnostics {
+ into.Key = m.Key.ValueString()
+ into.Value = m.Value.ValueString()
+ into.Locked = m.Locked.ValueBool()
+ into.Secret = m.Secret.ValueBool()
+
+ return nil
+}
diff --git a/internal/provider/resources/environment_variable_resource_test.go b/internal/provider/resources/environment_variable_resource_test.go
new file mode 100644
index 0000000..99bd50a
--- /dev/null
+++ b/internal/provider/resources/environment_variable_resource_test.go
@@ -0,0 +1,70 @@
+package resources_test
+
+import (
+ "regexp"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccEnvVarCheckRequiredFields(t *testing.T) {
+ config := `resource "checkly_environment_variable" "test" {}`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "key" is required`),
+ },
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "value" is required`),
+ },
+ },
+ })
+}
+
+func TestAccEnvVarSuccess(t *testing.T) {
+ config := `resource "checkly_environment_variable" "test" {
+ key = "API_URL"
+ value = "https://api.checklyhq.com"
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(
+ "checkly_environment_variable.test",
+ "key",
+ "API_URL",
+ ),
+ resource.TestCheckResourceAttr(
+ "checkly_environment_variable.test",
+ "value",
+ "https://api.checklyhq.com",
+ ),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccSecretEnvVarSuccess(t *testing.T) {
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: `resource "checkly_environment_variable" "test" {
+ key = "SECRET"
+ value = "https://api.checklyhq.com"
+ secret = true
+ }`,
+ },
+ },
+ })
+}
diff --git a/internal/provider/resources/heartbeat_resource.go b/internal/provider/resources/heartbeat_resource.go
new file mode 100644
index 0000000..d9ec591
--- /dev/null
+++ b/internal/provider/resources/heartbeat_resource.go
@@ -0,0 +1,489 @@
+package resources
+
+import (
+ "context"
+ "fmt"
+ "slices"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "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-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
+ "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"
+
+ checkly "github.com/checkly/checkly-go-sdk"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/interop"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/resources/attributes"
+ "github.com/checkly/terraform-provider-checkly/internal/sdkutil"
+)
+
+var (
+ _ resource.Resource = (*HeartbeatResource)(nil)
+ _ resource.ResourceWithConfigure = (*HeartbeatResource)(nil)
+ _ resource.ResourceWithImportState = (*HeartbeatResource)(nil)
+ _ resource.ResourceWithValidateConfig = (*HeartbeatResource)(nil)
+)
+
+type HeartbeatResource struct {
+ client checkly.Client
+}
+
+func NewHeartbeatResource() resource.Resource {
+ return &HeartbeatResource{}
+}
+
+func (r *HeartbeatResource) Metadata(
+ ctx context.Context,
+ req resource.MetadataRequest,
+ resp *resource.MetadataResponse,
+) {
+ resp.TypeName = req.ProviderTypeName + "_heartbeat"
+}
+
+func (r *HeartbeatResource) Schema(
+ ctx context.Context,
+ req resource.SchemaRequest,
+ resp *resource.SchemaResponse,
+) {
+ resp.Schema = schema.Schema{
+ Description: "Heartbeats allows you to monitor your cron jobs and set up alerting, so you get a notification when things break or slow down.",
+ Attributes: map[string]schema.Attribute{
+ "id": attributes.IDAttributeSchema,
+ "name": schema.StringAttribute{
+ Description: "The name of the check.",
+ Required: true,
+ },
+ "activated": schema.BoolAttribute{
+ Description: "Determines if the check is running or not. Possible values `true`, and `false`.",
+ Required: true,
+ },
+ "muted": schema.BoolAttribute{
+ Description: "Determines if any notifications will be sent out when a check fails/degrades/recovers.",
+ Optional: true,
+ Computed: true,
+ Default: booldefault.StaticBool(false),
+ },
+ "tags": schema.SetAttribute{
+ Description: "A list of tags for organizing and filtering checks.",
+ ElementType: types.StringType,
+ Optional: true,
+ Computed: true,
+ },
+ "alert_settings": attributes.AlertSettingsAttributeSchema,
+ "use_global_alert_settings": schema.BoolAttribute{
+ Description: "When true, the account level alert settings will be used, not the alert setting defined on this check.",
+ Optional: true,
+ Computed: true,
+ Default: booldefault.StaticBool(false),
+ },
+ "heartbeat": HeartbeatAttributeSchema,
+ "alert_channel_subscription": attributes.AlertChannelSubscriptionAttributeSchema,
+ },
+ }
+}
+
+func valueWithUnitToSeconds(value int32, unit string) int32 {
+ switch unit {
+ case "seconds":
+ return value * 1
+ case "minutes":
+ return value * 60
+ case "hours":
+ return value * 3600
+ case "days":
+ return value * 3600 * 24
+ default:
+ return 0
+ }
+}
+
+func (r *HeartbeatResource) ValidateConfig(
+ ctx context.Context,
+ req resource.ValidateConfigRequest,
+ resp *resource.ValidateConfigResponse,
+) {
+ var config HeartbeatResourceModel
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ _, heartbeatAttributeModel, diags := HeartbeatAttributeGluer.RenderFromObject(ctx, config.Heartbeat)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ if !heartbeatAttributeModel.Period.IsUnknown() && !heartbeatAttributeModel.PeriodUnit.IsUnknown() {
+ value := heartbeatAttributeModel.Period.ValueInt32()
+ valuePath := path.Root("heartbeat").AtName("period")
+
+ unit := heartbeatAttributeModel.PeriodUnit.ValueString()
+ unitPath := path.Root("heartbeat").AtName("period_unit")
+
+ seconds := valueWithUnitToSeconds(value, unit)
+
+ if seconds < 30 {
+ resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
+ valuePath,
+ fmt.Sprintf(`value in combination with %s must be greater than or equal to 30s`, unitPath.String()),
+ fmt.Sprintf("%d %s", value, unit),
+ ))
+ }
+
+ if seconds > 3600*24*365 {
+ resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
+ valuePath,
+ fmt.Sprintf(`value in combination with %s must be less than or equal to 365 days`, unitPath.String()),
+ fmt.Sprintf("%d %s", value, unit),
+ ))
+ }
+ }
+
+ if !heartbeatAttributeModel.Grace.IsUnknown() && !heartbeatAttributeModel.GraceUnit.IsUnknown() {
+ value := heartbeatAttributeModel.Grace.ValueInt32()
+ valuePath := path.Root("heartbeat").AtName("grace")
+
+ unit := heartbeatAttributeModel.GraceUnit.ValueString()
+ unitPath := path.Root("heartbeat").AtName("grace_unit")
+
+ seconds := valueWithUnitToSeconds(value, unit)
+
+ if seconds > 3600*24*365 {
+ resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
+ valuePath,
+ fmt.Sprintf(`value in combination with %s must be less than or equal to 365 days`, unitPath.String()),
+ fmt.Sprintf("%d %s", value, unit),
+ ))
+ }
+ }
+}
+
+func (r *HeartbeatResource) Configure(
+ ctx context.Context,
+ req resource.ConfigureRequest,
+ resp *resource.ConfigureResponse,
+) {
+ client, diags := interop.ClientFromProviderData(req.ProviderData)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ r.client = client
+}
+
+func (r *HeartbeatResource) ImportState(
+ ctx context.Context,
+ req resource.ImportStateRequest,
+ resp *resource.ImportStateResponse,
+) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *HeartbeatResource) Create(
+ ctx context.Context,
+ req resource.CreateRequest,
+ resp *resource.CreateResponse,
+) {
+ var plan HeartbeatResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var desiredModel checkly.HeartbeatCheck
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.CreateHeartbeat(ctx, desiredModel)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Creating Checkly Heartbeat",
+ fmt.Sprintf("Could not create heartbeat, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Created)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *HeartbeatResource) Delete(
+ ctx context.Context,
+ req resource.DeleteRequest,
+ resp *resource.DeleteResponse,
+) {
+ var state HeartbeatResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ err := r.client.DeleteCheck(ctx, state.ID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Deleting Checkly Heartbeat",
+ fmt.Sprintf("Could not delete heartbeat, unexpected error: %s", err),
+ )
+
+ return
+ }
+}
+
+func (r *HeartbeatResource) Read(
+ ctx context.Context,
+ req resource.ReadRequest,
+ resp *resource.ReadResponse,
+) {
+ var state HeartbeatResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.GetHeartbeatCheck(ctx, state.ID.ValueString())
+ if err != nil {
+ if sdkutil.IsHTTPNotFoundError(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+
+ resp.Diagnostics.AddError(
+ "Error Reading Checkly Heartbeat",
+ fmt.Sprintf("Could not retrieve heartbeat, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(state.Refresh(ctx, realizedModel, interop.Loaded)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *HeartbeatResource) Update(
+ ctx context.Context,
+ req resource.UpdateRequest,
+ resp *resource.UpdateResponse,
+) {
+ var plan HeartbeatResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var desiredModel checkly.HeartbeatCheck
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.UpdateHeartbeat(
+ ctx,
+ plan.ID.ValueString(),
+ desiredModel,
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Updating Checkly Heartbeat",
+ fmt.Sprintf("Could not update heartbeat, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Updated)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+var (
+ _ interop.Model[checkly.HeartbeatCheck] = (*HeartbeatResourceModel)(nil)
+ _ interop.Model[checkly.Heartbeat] = (*HeartbeatAttributeModel)(nil)
+)
+
+type HeartbeatResourceModel struct {
+ ID types.String `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ Activated types.Bool `tfsdk:"activated"`
+ Muted types.Bool `tfsdk:"muted"`
+ Tags types.Set `tfsdk:"tags"`
+ AlertSettings types.Object `tfsdk:"alert_settings"`
+ UseGlobalAlertSettings types.Bool `tfsdk:"use_global_alert_settings"`
+ Heartbeat types.Object `tfsdk:"heartbeat"`
+ AlertChannelSubscriptions types.List `tfsdk:"alert_channel_subscription"`
+}
+
+func (m *HeartbeatResourceModel) Refresh(ctx context.Context, from *checkly.HeartbeatCheck, flags interop.RefreshFlags) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ if flags.Created() {
+ m.ID = types.StringValue(from.ID)
+ }
+
+ m.Name = types.StringValue(from.Name)
+ m.Activated = types.BoolValue(from.Activated)
+ m.Muted = types.BoolValue(from.Muted)
+
+ slices.Sort(from.Tags)
+ m.Tags = interop.IntoUntypedStringSet(&from.Tags)
+
+ m.AlertSettings, _, diags = attributes.AlertSettingsAttributeGluer.RefreshToObject(ctx, &from.AlertSettings, flags)
+ if diags.HasError() {
+ return diags
+ }
+
+ m.UseGlobalAlertSettings = types.BoolValue(from.UseGlobalAlertSettings)
+
+ m.Heartbeat, _, diags = HeartbeatAttributeGluer.RefreshToObject(ctx, &from.Heartbeat, flags)
+ if diags.HasError() {
+ return diags
+ }
+
+ m.AlertChannelSubscriptions, _, diags = attributes.AlertChannelSubscriptionAttributeGluer.RefreshToList(ctx, &from.AlertChannelSubscriptions, flags)
+ if diags.HasError() {
+ return diags
+ }
+
+ return diags
+}
+
+func (m *HeartbeatResourceModel) Render(ctx context.Context, into *checkly.HeartbeatCheck) diag.Diagnostics {
+ var diags diag.Diagnostics
+
+ into.Name = m.Name.ValueString()
+ into.Activated = m.Activated.ValueBool()
+ into.Muted = m.Muted.ValueBool()
+ into.Tags = interop.FromUntypedStringSet(m.Tags)
+
+ into.AlertSettings, _, diags = attributes.AlertSettingsAttributeGluer.RenderFromObject(ctx, m.AlertSettings)
+ if diags.HasError() {
+ return diags
+ }
+
+ into.UseGlobalAlertSettings = m.UseGlobalAlertSettings.ValueBool()
+
+ into.Heartbeat, _, diags = HeartbeatAttributeGluer.RenderFromObject(ctx, m.Heartbeat)
+ if diags.HasError() {
+ return diags
+ }
+
+ into.AlertChannelSubscriptions, _, diags = attributes.AlertChannelSubscriptionAttributeGluer.RenderFromList(ctx, m.AlertChannelSubscriptions)
+ if diags.HasError() {
+ return diags
+ }
+
+ return diags
+}
+
+var HeartbeatAttributeSchema = schema.SingleNestedAttribute{
+ Required: true,
+ Attributes: map[string]schema.Attribute{
+ "period": schema.Int32Attribute{
+ Description: "How often you expect a ping to the ping URL.",
+ Required: true,
+ },
+ "period_unit": schema.StringAttribute{
+ Description: "Possible values `seconds`, `minutes`, `hours` and `days`.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.OneOf(
+ "seconds",
+ "minutes",
+ "hours",
+ "days",
+ ),
+ },
+ },
+ "grace": schema.Int32Attribute{
+ Description: "How long Checkly should wait before triggering any alerts when a ping does not arrive within the set period.",
+ Required: true,
+ },
+ "grace_unit": schema.StringAttribute{
+ Description: "Possible values `seconds`, `minutes`, `hours` and `days`.",
+ Required: true,
+ Validators: []validator.String{
+ stringvalidator.OneOf(
+ "seconds",
+ "minutes",
+ "hours",
+ "days",
+ ),
+ },
+ },
+ "ping_token": schema.StringAttribute{
+ Description: "Custom token to generate your ping URL. Checkly will expect a ping to `https://ping.checklyhq.com/[PING_TOKEN]`.",
+ Computed: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.UseStateForUnknown(),
+ },
+ },
+ },
+}
+
+type HeartbeatAttributeModel struct {
+ Period types.Int32 `tfsdk:"period"`
+ PeriodUnit types.String `tfsdk:"period_unit"`
+ Grace types.Int32 `tfsdk:"grace"`
+ GraceUnit types.String `tfsdk:"grace_unit"`
+ PingToken types.String `tfsdk:"ping_token"`
+}
+
+var HeartbeatAttributeGluer = interop.GluerForSingleNestedAttribute[
+ checkly.Heartbeat,
+ HeartbeatAttributeModel,
+](HeartbeatAttributeSchema)
+
+func (m *HeartbeatAttributeModel) Refresh(ctx context.Context, from *checkly.Heartbeat, flags interop.RefreshFlags) diag.Diagnostics {
+ m.Period = types.Int32Value(int32(from.Period))
+ m.PeriodUnit = types.StringValue(from.PeriodUnit)
+ m.Grace = types.Int32Value(int32(from.Grace))
+ m.GraceUnit = types.StringValue(from.GraceUnit)
+ m.PingToken = types.StringValue(from.PingToken)
+
+ return nil
+}
+
+func (m *HeartbeatAttributeModel) Render(ctx context.Context, into *checkly.Heartbeat) diag.Diagnostics {
+ into.Period = int(m.Period.ValueInt32())
+ into.PeriodUnit = m.PeriodUnit.ValueString()
+ into.Grace = int(m.Grace.ValueInt32())
+ into.GraceUnit = m.GraceUnit.ValueString()
+ into.PingToken = m.PingToken.ValueString()
+
+ return nil
+}
diff --git a/internal/provider/resources/heartbeat_resource_test.go b/internal/provider/resources/heartbeat_resource_test.go
new file mode 100644
index 0000000..6496ce0
--- /dev/null
+++ b/internal/provider/resources/heartbeat_resource_test.go
@@ -0,0 +1,215 @@
+package resources_test
+
+import (
+ "regexp"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccHeartbeatRequiredFields(t *testing.T) {
+ config := `resource "checkly_heartbeat" "test" {}`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "name" is required, but no definition was found.`),
+ },
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "activated" is required, but no definition was found.`),
+ },
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "heartbeat" is required, but no definition was found.`),
+ },
+ },
+ })
+}
+
+func TestAccHeartbeatCheckInvalidInputs(t *testing.T) {
+ config := `resource "checkly_check" "test" {
+ name = 1
+ activated = "invalid"
+ use_global_alert_settings = "invalid"
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`Inappropriate value for attribute "activated"`),
+ },
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`Inappropriate value for attribute "use_global_alert_settings"`),
+ },
+ },
+ })
+}
+
+func TestAccHeartbeatCheckMissingHeartbeatFields(t *testing.T) {
+ config := `resource "checkly_heartbeat" "test" {
+ activated = true
+ name = "heartbeat check"
+ heartbeat = {
+
+ }
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "grace" is required, but no definition was found.`),
+ },
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "grace_unit" is required, but no definition was found.`),
+ },
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "period" is required, but no definition was found.`),
+ },
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "period_unit" is required, but no definition was found.`),
+ },
+ },
+ })
+}
+
+func TestAccHeartbeatCheckPeriodTooBig(t *testing.T) {
+ config := `resource "checkly_heartbeat" "test" {
+ activated = true
+ name = "heartbeat check"
+ heartbeat = {
+ period = 366
+ period_unit = "days"
+ grace = 0
+ grace_unit = "seconds"
+ }
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`Attribute heartbeat\.period value [\s\S]*?must be less than or equal to 365 days`),
+ },
+ },
+ })
+}
+
+func TestAccHeartbeatCheckPeriodTooSmall(t *testing.T) {
+ config := `resource "checkly_heartbeat" "test" {
+ activated = true
+ name = "heartbeat check"
+ heartbeat = {
+ period = 5
+ period_unit = "seconds"
+ grace = 0
+ grace_unit = "seconds"
+ }
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`Attribute heartbeat.period value[\s\S]*?must be greater than or equal to 30s`),
+ },
+ },
+ })
+}
+
+func TestAccHeartbeatCheckInvalidPeriodUnit(t *testing.T) {
+ config := `resource "checkly_heartbeat" "test" {
+ activated = true
+ name = "heartbeat check"
+ heartbeat = {
+ period = 5
+ period_unit = "lightyear"
+ grace = 0
+ grace_unit = "seconds"
+ }
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`Attribute heartbeat\.period_unit value must be one of`),
+ },
+ },
+ })
+}
+
+func TestAccHeartbeatCheckInvalidGraceUnit(t *testing.T) {
+ config := `resource "checkly_heartbeat" "test" {
+ activated = true
+ name = "heartbeat check"
+ heartbeat = {
+ period = 5
+ period_unit = "days"
+ grace = 0
+ grace_unit = "lightyear"
+ }
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`Attribute heartbeat\.grace_unit value must be one of`),
+ },
+ },
+ })
+}
+
+func TestAccHeartbeatCheckCreate(t *testing.T) {
+ config := `resource "checkly_heartbeat" "test" {
+ activated = true
+ name = "heartbeat check"
+ heartbeat = {
+ period = 5
+ period_unit = "days"
+ grace = 0
+ grace_unit = "seconds"
+ }
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(
+ "checkly_heartbeat.test",
+ "name",
+ "heartbeat check",
+ ),
+ resource.TestCheckResourceAttr(
+ "checkly_heartbeat.test",
+ "heartbeat.period",
+ "5",
+ ),
+ resource.TestCheckResourceAttr(
+ "checkly_heartbeat.test",
+ "heartbeat.period_unit",
+ "days",
+ ),
+ ),
+ },
+ },
+ })
+}
diff --git a/internal/provider/resources/maintenance_windows_resource.go b/internal/provider/resources/maintenance_windows_resource.go
new file mode 100644
index 0000000..c43cb4d
--- /dev/null
+++ b/internal/provider/resources/maintenance_windows_resource.go
@@ -0,0 +1,316 @@
+package resources
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
+ "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-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+
+ checkly "github.com/checkly/checkly-go-sdk"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/interop"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/resources/attributes"
+ "github.com/checkly/terraform-provider-checkly/internal/sdkutil"
+)
+
+var (
+ _ resource.Resource = (*MaintenanceWindowsResource)(nil)
+ _ resource.ResourceWithConfigure = (*MaintenanceWindowsResource)(nil)
+ _ resource.ResourceWithImportState = (*MaintenanceWindowsResource)(nil)
+)
+
+type MaintenanceWindowsResource struct {
+ client checkly.Client
+}
+
+func NewMaintenanceWindowsResource() resource.Resource {
+ return &MaintenanceWindowsResource{}
+}
+
+func (r *MaintenanceWindowsResource) Metadata(
+ ctx context.Context,
+ req resource.MetadataRequest,
+ resp *resource.MetadataResponse,
+) {
+ resp.TypeName = req.ProviderTypeName + "_maintenance_windows"
+}
+
+func (r *MaintenanceWindowsResource) Schema(
+ ctx context.Context,
+ req resource.SchemaRequest,
+ resp *resource.SchemaResponse,
+) {
+ resp.Schema = schema.Schema{
+ Attributes: map[string]schema.Attribute{
+ "id": attributes.IDAttributeSchema,
+ "name": schema.StringAttribute{
+ Required: true,
+ Description: "The maintenance window name.",
+ },
+ "starts_at": schema.StringAttribute{
+ Required: true,
+ Description: "The start date of the maintenance window.",
+ },
+ "ends_at": schema.StringAttribute{
+ Required: true,
+ Description: "The end date of the maintenance window.",
+ },
+ "repeat_unit": schema.StringAttribute{
+ Optional: true,
+ Validators: []validator.String{
+ stringvalidator.OneOf("DAY", "WEEK", "MONTH"),
+ },
+ Description: "The repeat cadence for the maintenance window. Possible values `DAY`, `WEEK` and `MONTH`.",
+ },
+ "repeat_interval": schema.Int32Attribute{
+ Optional: true,
+ Description: "The repeat interval of the maintenance window from the first occurrence.",
+ },
+ "repeat_ends_at": schema.StringAttribute{
+ Optional: true,
+ Description: "The date on which the maintenance window should stop repeating.",
+ },
+ "tags": schema.SetAttribute{
+ ElementType: types.StringType,
+ Optional: true,
+ Description: "The names of the checks and groups maintenance window should apply to.",
+ },
+ },
+ }
+}
+
+func (r *MaintenanceWindowsResource) Configure(
+ ctx context.Context,
+ req resource.ConfigureRequest,
+ resp *resource.ConfigureResponse,
+) {
+ client, diags := interop.ClientFromProviderData(req.ProviderData)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ r.client = client
+}
+
+func (r *MaintenanceWindowsResource) ImportState(
+ ctx context.Context,
+ req resource.ImportStateRequest,
+ resp *resource.ImportStateResponse,
+) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *MaintenanceWindowsResource) Create(
+ ctx context.Context,
+ req resource.CreateRequest,
+ resp *resource.CreateResponse,
+) {
+ var plan MaintenanceWindowsResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var desiredModel checkly.MaintenanceWindow
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.CreateMaintenanceWindow(ctx, desiredModel)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Creating Checkly Maintenance Window",
+ fmt.Sprintf("Could not create maintenance window, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Created)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *MaintenanceWindowsResource) Delete(
+ ctx context.Context,
+ req resource.DeleteRequest,
+ resp *resource.DeleteResponse,
+) {
+ var state MaintenanceWindowsResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ id, diags := MaintenanceWindowID.FromString(state.ID)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ err := r.client.DeleteMaintenanceWindow(ctx, id)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Deleting Checkly Maintenance Window",
+ fmt.Sprintf("Could not delete maintenance window, unexpected error: %s", err),
+ )
+
+ return
+ }
+}
+
+func (r *MaintenanceWindowsResource) Read(
+ ctx context.Context,
+ req resource.ReadRequest,
+ resp *resource.ReadResponse,
+) {
+ var state MaintenanceWindowsResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ id, diags := MaintenanceWindowID.FromString(state.ID)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ realizedModel, err := r.client.GetMaintenanceWindow(ctx, id)
+ if err != nil {
+ if sdkutil.IsHTTPNotFoundError(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+
+ resp.Diagnostics.AddError(
+ "Error Reading Checkly Maintenance Window",
+ fmt.Sprintf("Could not retrieve maintenance window, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(state.Refresh(ctx, realizedModel, interop.Loaded)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *MaintenanceWindowsResource) Update(
+ ctx context.Context,
+ req resource.UpdateRequest,
+ resp *resource.UpdateResponse,
+) {
+ var plan MaintenanceWindowsResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ id, diags := MaintenanceWindowID.FromString(plan.ID)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ var desiredModel checkly.MaintenanceWindow
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.UpdateMaintenanceWindow(
+ ctx,
+ id,
+ desiredModel,
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Updating Checkly Maintenance Window",
+ fmt.Sprintf("Could not update maintenance window, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Updated)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+var MaintenanceWindowID = sdkutil.Identifier{
+ Path: path.Root("id"),
+ Title: "Checkly Maintenance Window ID",
+}
+
+var (
+ _ interop.Model[checkly.MaintenanceWindow] = (*MaintenanceWindowsResourceModel)(nil)
+)
+
+type MaintenanceWindowsResourceModel struct {
+ ID types.String `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ StartsAt types.String `tfsdk:"starts_at"`
+ EndsAt types.String `tfsdk:"ends_at"`
+ RepeatUnit types.String `tfsdk:"repeat_unit"`
+ RepeatInterval types.Int32 `tfsdk:"repeat_interval"`
+ RepeatEndsAt types.String `tfsdk:"repeat_ends_at"`
+ Tags types.Set `tfsdk:"tags"`
+}
+
+func (m *MaintenanceWindowsResourceModel) Refresh(ctx context.Context, from *checkly.MaintenanceWindow, flags interop.RefreshFlags) diag.Diagnostics {
+ if flags.Created() {
+ m.ID = MaintenanceWindowID.IntoString(from.ID)
+ }
+
+ m.Name = types.StringValue(from.Name)
+ m.StartsAt = types.StringValue(from.StartsAt)
+ m.EndsAt = types.StringValue(from.EndsAt)
+ m.RepeatUnit = types.StringValue(from.RepeatUnit)
+ m.RepeatEndsAt = types.StringValue(from.RepeatEndsAt)
+ m.RepeatInterval = types.Int32Value(int32(from.RepeatInterval))
+ m.Tags = interop.IntoUntypedStringSet(&from.Tags)
+
+ return nil
+}
+
+func (m *MaintenanceWindowsResourceModel) Render(ctx context.Context, into *checkly.MaintenanceWindow) diag.Diagnostics {
+ into.Name = m.Name.ValueString()
+ into.StartsAt = m.StartsAt.ValueString()
+ into.EndsAt = m.EndsAt.ValueString()
+ into.RepeatUnit = m.RepeatUnit.ValueString()
+ into.RepeatEndsAt = m.RepeatEndsAt.ValueString()
+ into.RepeatInterval = int(m.RepeatInterval.ValueInt32())
+ into.Tags = interop.FromUntypedStringSet(m.Tags)
+
+ return nil
+}
diff --git a/internal/provider/resources/private_location_resource.go b/internal/provider/resources/private_location_resource.go
new file mode 100644
index 0000000..951c026
--- /dev/null
+++ b/internal/provider/resources/private_location_resource.go
@@ -0,0 +1,284 @@
+package resources
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "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-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+
+ checkly "github.com/checkly/checkly-go-sdk"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/interop"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/resources/attributes"
+ "github.com/checkly/terraform-provider-checkly/internal/sdkutil"
+)
+
+var (
+ _ resource.Resource = (*PrivateLocationResource)(nil)
+ _ resource.ResourceWithConfigure = (*PrivateLocationResource)(nil)
+ _ resource.ResourceWithImportState = (*PrivateLocationResource)(nil)
+)
+
+type PrivateLocationResource struct {
+ client checkly.Client
+}
+
+func NewPrivateLocationResource() resource.Resource {
+ return &PrivateLocationResource{}
+}
+
+func (r *PrivateLocationResource) Metadata(
+ ctx context.Context,
+ req resource.MetadataRequest,
+ resp *resource.MetadataResponse,
+) {
+ resp.TypeName = req.ProviderTypeName + "_private_location"
+}
+
+func (r *PrivateLocationResource) Schema(
+ ctx context.Context,
+ req resource.SchemaRequest,
+ resp *resource.SchemaResponse,
+) {
+ resp.Schema = schema.Schema{
+ Attributes: map[string]schema.Attribute{
+ "id": attributes.IDAttributeSchema,
+ "name": schema.StringAttribute{
+ Required: true,
+ Description: "The private location name.",
+ },
+ "slug_name": schema.StringAttribute{
+ Required: true,
+ Description: "Valid slug name.",
+ },
+ "icon": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ Default: stringdefault.StaticString("location"),
+ Description: "Icon assigned to the private location.",
+ },
+ "keys": schema.SetAttribute{
+ ElementType: types.StringType,
+ Computed: true,
+ Sensitive: true,
+ Description: "Private location API keys.",
+ },
+ },
+ }
+}
+
+func (r *PrivateLocationResource) Configure(
+ ctx context.Context,
+ req resource.ConfigureRequest,
+ resp *resource.ConfigureResponse,
+) {
+ client, diags := interop.ClientFromProviderData(req.ProviderData)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ r.client = client
+}
+
+func (r *PrivateLocationResource) ImportState(
+ ctx context.Context,
+ req resource.ImportStateRequest,
+ resp *resource.ImportStateResponse,
+) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *PrivateLocationResource) Create(
+ ctx context.Context,
+ req resource.CreateRequest,
+ resp *resource.CreateResponse,
+) {
+ var plan PrivateLocationResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var desiredModel checkly.PrivateLocation
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.CreatePrivateLocation(ctx, desiredModel)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Creating Checkly Private Location",
+ fmt.Sprintf("Could not create private location, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Created)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *PrivateLocationResource) Delete(
+ ctx context.Context,
+ req resource.DeleteRequest,
+ resp *resource.DeleteResponse,
+) {
+ var state PrivateLocationResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ err := r.client.DeletePrivateLocation(ctx, state.ID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Deleting Checkly Private Location",
+ fmt.Sprintf("Could not delete private location, unexpected error: %s", err),
+ )
+
+ return
+ }
+}
+
+func (r *PrivateLocationResource) Read(
+ ctx context.Context,
+ req resource.ReadRequest,
+ resp *resource.ReadResponse,
+) {
+ var state PrivateLocationResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.GetPrivateLocation(ctx, state.ID.ValueString())
+ if err != nil {
+ if sdkutil.IsHTTPNotFoundError(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+
+ resp.Diagnostics.AddError(
+ "Error Reading Checkly Private Location",
+ fmt.Sprintf("Could not retrieve private location, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(state.Refresh(ctx, realizedModel, interop.Loaded)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *PrivateLocationResource) Update(
+ ctx context.Context,
+ req resource.UpdateRequest,
+ resp *resource.UpdateResponse,
+) {
+ var plan PrivateLocationResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var desiredModel checkly.PrivateLocation
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.UpdatePrivateLocation(
+ ctx,
+ plan.ID.ValueString(),
+ desiredModel,
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Updating Checkly Private Location",
+ fmt.Sprintf("Could not update private location, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Updated)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+var (
+ _ interop.Model[checkly.PrivateLocation] = (*PrivateLocationResourceModel)(nil)
+)
+
+type PrivateLocationResourceModel struct {
+ ID types.String `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ SlugName types.String `tfsdk:"slug_name"`
+ Icon types.String `tfsdk:"icon"`
+ Keys types.Set `tfsdk:"keys"`
+}
+
+func (m *PrivateLocationResourceModel) Refresh(ctx context.Context, from *checkly.PrivateLocation, flags interop.RefreshFlags) diag.Diagnostics {
+ if flags.Created() {
+ m.ID = types.StringValue(from.ID)
+ }
+
+ m.Name = types.StringValue(from.Name)
+ m.SlugName = types.StringValue(from.SlugName)
+ m.Icon = types.StringValue(from.Icon)
+
+ var keyValues []attr.Value
+ for _, key := range from.Keys {
+ keyValues = append(keyValues, types.StringValue(key.RawKey))
+ }
+
+ keys, diags := types.SetValue(types.StringType, keyValues)
+ if diags.HasError() {
+ return diags
+ }
+
+ m.Keys = keys
+
+ return nil
+}
+
+func (m *PrivateLocationResourceModel) Render(ctx context.Context, into *checkly.PrivateLocation) diag.Diagnostics {
+ into.Name = m.Name.ValueString()
+ into.SlugName = m.SlugName.ValueString()
+ into.Icon = m.Icon.ValueString()
+
+ // Keys are intentionally not included.
+
+ return nil
+}
diff --git a/internal/provider/resources/private_location_resource_test.go b/internal/provider/resources/private_location_resource_test.go
new file mode 100644
index 0000000..37a2fb3
--- /dev/null
+++ b/internal/provider/resources/private_location_resource_test.go
@@ -0,0 +1,93 @@
+package resources_test
+
+import (
+ "regexp"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccPrivateLocationCheckRequiredFields(t *testing.T) {
+ config := `resource "checkly_private_location" "test" {}`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "name" is required`),
+ },
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "slug_name" is required`),
+ },
+ },
+ })
+}
+
+func TestAccPrivateLocationSuccess(t *testing.T) {
+ config := `resource "checkly_private_location" "test" {
+ name = "New Private Location"
+ slug_name = "new-private-location"
+ icon = "bell-fill"
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(
+ "checkly_private_location.test",
+ "name",
+ "New Private Location",
+ ),
+ resource.TestCheckResourceAttr(
+ "checkly_private_location.test",
+ "slug_name",
+ "new-private-location",
+ ),
+ resource.TestCheckResourceAttr(
+ "checkly_private_location.test",
+ "icon",
+ "bell-fill",
+ ),
+ ),
+ },
+ },
+ })
+}
+
+func TestAccPrivateLocationDefaultIcon(t *testing.T) {
+ config := `resource "checkly_private_location" "without_icon" {
+ name = "New Private Location"
+ slug_name = "new-private-location"
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(
+ "checkly_private_location.without_icon",
+ "name",
+ "New Private Location",
+ ),
+ resource.TestCheckResourceAttr(
+ "checkly_private_location.without_icon",
+ "slug_name",
+ "new-private-location",
+ ),
+ resource.TestCheckResourceAttr(
+ "checkly_private_location.without_icon",
+ "icon",
+ "location",
+ ),
+ ),
+ },
+ },
+ })
+}
diff --git a/checkly/test_util.go b/internal/provider/resources/shared_test.go
similarity index 52%
rename from checkly/test_util.go
rename to internal/provider/resources/shared_test.go
index d95147e..38f31e0 100644
--- a/checkly/test_util.go
+++ b/internal/provider/resources/shared_test.go
@@ -1,46 +1,24 @@
-package checkly
+package resources_test
import (
"encoding/json"
"fmt"
- "os"
"regexp"
- "testing"
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
- "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
- "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
-)
-
-var testAccProviders map[string]*schema.Provider
-
-func init() {
- testAccProviders = map[string]*schema.Provider{
- "checkly": Provider(),
- }
-}
+ "github.com/hashicorp/terraform-plugin-framework/providerserver"
+ "github.com/hashicorp/terraform-plugin-go/tfprotov6"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
-func testAccPreCheck(t *testing.T) {
- if os.Getenv("CHECKLY_API_KEY") == "" {
- t.Fatal("CHECKLY_API_KEY must be set for acceptance tests")
- }
+ "github.com/checkly/terraform-provider-checkly/internal/provider"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/globalregistry"
+)
- if os.Getenv("CHECKLY_ACCOUNT_ID") == "" {
- t.Fatal("CHECKLY_ACCOUNT_ID must be set for acceptance tests")
+func protoV6ProviderFactories() map[string]func() (tfprotov6.ProviderServer, error) {
+ return map[string]func() (tfprotov6.ProviderServer, error){
+ "checkly": providerserver.NewProtocol6WithError(provider.New("test", globalregistry.Registry)()),
}
}
-func accTestCase(t *testing.T, steps []resource.TestStep) {
- resource.Test(t, resource.TestCase{
- PreCheck: func() {
- testAccPreCheck(t)
- },
- Providers: testAccProviders,
- CheckDestroy: nil,
- Steps: steps,
- })
-}
-
// test resource using regular expressions
// this helps testing arrays which have irregular indices;
// needed because we get things like "alert_settings.2888461220.escalation_type": "RUN_BASED"
diff --git a/internal/provider/resources/snippet_resource.go b/internal/provider/resources/snippet_resource.go
new file mode 100644
index 0000000..706ae09
--- /dev/null
+++ b/internal/provider/resources/snippet_resource.go
@@ -0,0 +1,277 @@
+package resources
+
+import (
+ "context"
+ "fmt"
+
+ "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-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+
+ checkly "github.com/checkly/checkly-go-sdk"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/interop"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/resources/attributes"
+ "github.com/checkly/terraform-provider-checkly/internal/sdkutil"
+)
+
+var (
+ _ resource.Resource = (*SnippetResource)(nil)
+ _ resource.ResourceWithConfigure = (*SnippetResource)(nil)
+ _ resource.ResourceWithImportState = (*SnippetResource)(nil)
+)
+
+type SnippetResource struct {
+ client checkly.Client
+}
+
+func NewSnippetResource() resource.Resource {
+ return &SnippetResource{}
+}
+
+func (r *SnippetResource) Metadata(
+ ctx context.Context,
+ req resource.MetadataRequest,
+ resp *resource.MetadataResponse,
+) {
+ resp.TypeName = req.ProviderTypeName + "_snippet"
+}
+
+func (r *SnippetResource) Schema(
+ ctx context.Context,
+ req resource.SchemaRequest,
+ resp *resource.SchemaResponse,
+) {
+ resp.Schema = schema.Schema{
+ Attributes: map[string]schema.Attribute{
+ "id": attributes.IDAttributeSchema,
+ "name": schema.StringAttribute{
+ Required: true,
+ Description: "The name of the snippet.",
+ },
+ "script": schema.StringAttribute{
+ Required: true,
+ Description: "Your Node.js code that interacts with the API " +
+ "check lifecycle, or functions as a partial for browser " +
+ "checks.",
+ },
+ },
+ }
+}
+
+func (r *SnippetResource) Configure(
+ ctx context.Context,
+ req resource.ConfigureRequest,
+ resp *resource.ConfigureResponse,
+) {
+ client, diags := interop.ClientFromProviderData(req.ProviderData)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ r.client = client
+}
+
+func (r *SnippetResource) ImportState(
+ ctx context.Context,
+ req resource.ImportStateRequest,
+ resp *resource.ImportStateResponse,
+) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *SnippetResource) Create(
+ ctx context.Context,
+ req resource.CreateRequest,
+ resp *resource.CreateResponse,
+) {
+ var plan SnippetResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var desiredModel checkly.Snippet
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.CreateSnippet(ctx, desiredModel)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Creating Checkly Snippet",
+ fmt.Sprintf("Could not create snippet, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Created)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *SnippetResource) Delete(
+ ctx context.Context,
+ req resource.DeleteRequest,
+ resp *resource.DeleteResponse,
+) {
+ var state SnippetResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ id, diags := SnippetID.FromString(state.ID)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ err := r.client.DeleteSnippet(ctx, id)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Deleting Checkly Snippet",
+ fmt.Sprintf("Could not delete snippet, unexpected error: %s", err),
+ )
+
+ return
+ }
+}
+
+func (r *SnippetResource) Read(
+ ctx context.Context,
+ req resource.ReadRequest,
+ resp *resource.ReadResponse,
+) {
+ var state SnippetResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ id, diags := SnippetID.FromString(state.ID)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ realizedModel, err := r.client.GetSnippet(ctx, id)
+ if err != nil {
+ if sdkutil.IsHTTPNotFoundError(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+
+ resp.Diagnostics.AddError(
+ "Error Reading Checkly Snippet",
+ fmt.Sprintf("Could not retrieve snippet, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(state.Refresh(ctx, realizedModel, interop.Loaded)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *SnippetResource) Update(
+ ctx context.Context,
+ req resource.UpdateRequest,
+ resp *resource.UpdateResponse,
+) {
+ var plan SnippetResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ id, diags := SnippetID.FromString(plan.ID)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ var desiredModel checkly.Snippet
+ resp.Diagnostics.Append(plan.Render(ctx, &desiredModel)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.UpdateSnippet(
+ ctx,
+ id,
+ desiredModel,
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Updating Checkly Snippet",
+ fmt.Sprintf("Could not update snippet, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Updated)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+var SnippetID = sdkutil.Identifier{
+ Path: path.Root("id"),
+ Title: "Checkly Snippet ID",
+}
+
+var (
+ _ interop.Model[checkly.Snippet] = (*SnippetResourceModel)(nil)
+)
+
+type SnippetResourceModel struct {
+ ID types.String `tfsdk:"id"`
+ Name types.String `tfsdk:"name"`
+ Script types.String `tfsdk:"script"`
+}
+
+func (m *SnippetResourceModel) Refresh(ctx context.Context, from *checkly.Snippet, flags interop.RefreshFlags) diag.Diagnostics {
+ if flags.Created() {
+ m.ID = SnippetID.IntoString(from.ID)
+ }
+
+ m.Name = types.StringValue(from.Name)
+ m.Script = types.StringValue(from.Script)
+
+ return nil
+}
+
+func (m *SnippetResourceModel) Render(ctx context.Context, into *checkly.Snippet) diag.Diagnostics {
+ into.Name = m.Name.ValueString()
+ into.Script = m.Script.ValueString()
+
+ return nil
+}
diff --git a/internal/provider/resources/snippet_resource_test.go b/internal/provider/resources/snippet_resource_test.go
new file mode 100644
index 0000000..9daab74
--- /dev/null
+++ b/internal/provider/resources/snippet_resource_test.go
@@ -0,0 +1,54 @@
+package resources_test
+
+import (
+ "regexp"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAccSnippetCheckRequiredFields(t *testing.T) {
+ config := `resource "checkly_snippet" "test" {}`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "name" is required`),
+ },
+ {
+ Config: config,
+ ExpectError: regexp.MustCompile(`The argument "script" is required`),
+ },
+ },
+ })
+}
+
+func TestAccSnippetSuccess(t *testing.T) {
+ config := `resource "checkly_snippet" "test" {
+ name = "foo"
+ script = "console.log('bar')"
+ }`
+ resource.UnitTest(t, resource.TestCase{
+ ProtoV6ProviderFactories: protoV6ProviderFactories(),
+
+ Steps: []resource.TestStep{
+ {
+ Config: config,
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr(
+ "checkly_snippet.test",
+ "name",
+ "foo",
+ ),
+ resource.TestCheckResourceAttr(
+ "checkly_snippet.test",
+ "script",
+ "console.log('bar')",
+ ),
+ ),
+ },
+ },
+ })
+}
diff --git a/internal/provider/resources/trigger_check_resource.go b/internal/provider/resources/trigger_check_resource.go
new file mode 100644
index 0000000..032da93
--- /dev/null
+++ b/internal/provider/resources/trigger_check_resource.go
@@ -0,0 +1,260 @@
+package resources
+
+import (
+ "context"
+ "fmt"
+
+ "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-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+
+ checkly "github.com/checkly/checkly-go-sdk"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/interop"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/resources/attributes"
+ "github.com/checkly/terraform-provider-checkly/internal/sdkutil"
+)
+
+var (
+ _ resource.Resource = (*TriggerCheckResource)(nil)
+ _ resource.ResourceWithConfigure = (*TriggerCheckResource)(nil)
+ _ resource.ResourceWithImportState = (*TriggerCheckResource)(nil)
+)
+
+type TriggerCheckResource struct {
+ client checkly.Client
+}
+
+func NewTriggerCheckResource() resource.Resource {
+ return &TriggerCheckResource{}
+}
+
+func (r *TriggerCheckResource) Metadata(
+ ctx context.Context,
+ req resource.MetadataRequest,
+ resp *resource.MetadataResponse,
+) {
+ resp.TypeName = req.ProviderTypeName + "_trigger_check"
+}
+
+func (r *TriggerCheckResource) Schema(
+ ctx context.Context,
+ req resource.SchemaRequest,
+ resp *resource.SchemaResponse,
+) {
+ resp.Schema = schema.Schema{
+ Attributes: map[string]schema.Attribute{
+ "id": attributes.IDAttributeSchema,
+ "check_id": schema.StringAttribute{
+ Required: true,
+ Description: "The ID of the check that you want to attach the trigger to.",
+ },
+ "token": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ Description: "The token value created to trigger the check.",
+ },
+ "url": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ Description: "The request URL to trigger the check run.",
+ },
+ },
+ }
+}
+
+func (r *TriggerCheckResource) Configure(
+ ctx context.Context,
+ req resource.ConfigureRequest,
+ resp *resource.ConfigureResponse,
+) {
+ client, diags := interop.ClientFromProviderData(req.ProviderData)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ r.client = client
+}
+
+func (r *TriggerCheckResource) ImportState(
+ ctx context.Context,
+ req resource.ImportStateRequest,
+ resp *resource.ImportStateResponse,
+) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *TriggerCheckResource) Create(
+ ctx context.Context,
+ req resource.CreateRequest,
+ resp *resource.CreateResponse,
+) {
+ var plan TriggerCheckResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.CreateTriggerCheck(
+ ctx,
+ plan.CheckID.ValueString(),
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Creating Checkly Trigger Check",
+ fmt.Sprintf("Could not create trigger check, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Created)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *TriggerCheckResource) Delete(
+ ctx context.Context,
+ req resource.DeleteRequest,
+ resp *resource.DeleteResponse,
+) {
+ var state TriggerCheckResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ err := r.client.DeleteTriggerCheck(ctx, state.CheckID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Deleting Checkly Trigger Check",
+ fmt.Sprintf("Could not delete trigger check, unexpected error: %s", err),
+ )
+
+ return
+ }
+}
+
+func (r *TriggerCheckResource) Read(
+ ctx context.Context,
+ req resource.ReadRequest,
+ resp *resource.ReadResponse,
+) {
+ var state TriggerCheckResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.GetTriggerCheck(
+ ctx,
+ state.CheckID.ValueString(),
+ )
+ if err != nil {
+ if sdkutil.IsHTTPNotFoundError(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+
+ resp.Diagnostics.AddError(
+ "Error Reading Checkly Trigger Check",
+ fmt.Sprintf("Could not retrieve trigger check, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(state.Refresh(ctx, realizedModel, interop.Loaded)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *TriggerCheckResource) Update(
+ ctx context.Context,
+ req resource.UpdateRequest,
+ resp *resource.UpdateResponse,
+) {
+ var plan TriggerCheckResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.GetTriggerCheck(
+ ctx,
+ plan.CheckID.ValueString(),
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Updating Checkly Trigger Check",
+ fmt.Sprintf("Could not update trigger check, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Updated)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+var TriggerCheckID = sdkutil.Identifier{
+ Path: path.Root("id"),
+ Title: "Checkly Trigger Check ID",
+}
+
+var (
+ _ interop.Model[checkly.TriggerCheck] = (*TriggerCheckResourceModel)(nil)
+)
+
+type TriggerCheckResourceModel struct {
+ ID types.String `tfsdk:"id"`
+ CheckID types.String `tfsdk:"check_id"`
+ Token types.String `tfsdk:"token"`
+ URL types.String `tfsdk:"url"`
+}
+
+func (m *TriggerCheckResourceModel) Refresh(ctx context.Context, from *checkly.TriggerCheck, flags interop.RefreshFlags) diag.Diagnostics {
+ // TODO: Always update ID? CheckID, which is used for lookup, is user-modifiable,
+ // and we could receive back a complete different ID.
+ if flags.Created() {
+ m.ID = TriggerCheckID.IntoString(from.ID)
+ }
+
+ m.CheckID = types.StringValue(from.CheckId)
+ m.Token = types.StringValue(from.Token)
+ m.URL = types.StringValue(from.URL)
+
+ return nil
+}
+
+func (m *TriggerCheckResourceModel) Render(ctx context.Context, into *checkly.TriggerCheck) diag.Diagnostics {
+ into.Token = m.Token.ValueString()
+ into.URL = m.URL.ValueString()
+
+ return nil
+}
diff --git a/internal/provider/resources/trigger_group_resource.go b/internal/provider/resources/trigger_group_resource.go
new file mode 100644
index 0000000..b31ff56
--- /dev/null
+++ b/internal/provider/resources/trigger_group_resource.go
@@ -0,0 +1,258 @@
+package resources
+
+import (
+ "context"
+ "fmt"
+
+ "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-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+
+ checkly "github.com/checkly/checkly-go-sdk"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/interop"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/resources/attributes"
+ "github.com/checkly/terraform-provider-checkly/internal/sdkutil"
+)
+
+var (
+ _ resource.Resource = (*TriggerGroupResource)(nil)
+ _ resource.ResourceWithConfigure = (*TriggerGroupResource)(nil)
+ _ resource.ResourceWithImportState = (*TriggerGroupResource)(nil)
+)
+
+type TriggerGroupResource struct {
+ client checkly.Client
+}
+
+func NewTriggerGroupResource() resource.Resource {
+ return &TriggerGroupResource{}
+}
+
+func (r *TriggerGroupResource) Metadata(
+ ctx context.Context,
+ req resource.MetadataRequest,
+ resp *resource.MetadataResponse,
+) {
+ resp.TypeName = req.ProviderTypeName + "_trigger_group"
+}
+
+func (r *TriggerGroupResource) Schema(
+ ctx context.Context,
+ req resource.SchemaRequest,
+ resp *resource.SchemaResponse,
+) {
+ resp.Schema = schema.Schema{
+ Attributes: map[string]schema.Attribute{
+ "id": attributes.IDAttributeSchema,
+ "group_id": schema.Int64Attribute{
+ Required: true,
+ Description: "The ID of the group that you want to attach the trigger to.",
+ },
+ "token": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ Description: "The token value created to trigger the group.",
+ },
+ "url": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ Description: "The request URL to trigger the group run.",
+ },
+ },
+ }
+}
+
+func (r *TriggerGroupResource) Configure(
+ ctx context.Context,
+ req resource.ConfigureRequest,
+ resp *resource.ConfigureResponse,
+) {
+ client, diags := interop.ClientFromProviderData(req.ProviderData)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ r.client = client
+}
+
+func (r *TriggerGroupResource) ImportState(
+ ctx context.Context,
+ req resource.ImportStateRequest,
+ resp *resource.ImportStateResponse,
+) {
+ resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
+}
+
+func (r *TriggerGroupResource) Create(
+ ctx context.Context,
+ req resource.CreateRequest,
+ resp *resource.CreateResponse,
+) {
+ var plan TriggerGroupResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.CreateTriggerGroup(
+ ctx,
+ plan.GroupID.ValueInt64(),
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Creating Checkly Trigger Group",
+ fmt.Sprintf("Could not create trigger group, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Created)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *TriggerGroupResource) Delete(
+ ctx context.Context,
+ req resource.DeleteRequest,
+ resp *resource.DeleteResponse,
+) {
+ var state TriggerGroupResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ err := r.client.DeleteTriggerGroup(ctx, state.GroupID.ValueInt64())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Deleting Checkly Trigger Group",
+ fmt.Sprintf("Could not delete trigger group, unexpected error: %s", err),
+ )
+
+ return
+ }
+}
+
+func (r *TriggerGroupResource) Read(
+ ctx context.Context,
+ req resource.ReadRequest,
+ resp *resource.ReadResponse,
+) {
+ var state TriggerGroupResourceModel
+
+ resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.GetTriggerGroup(
+ ctx,
+ state.GroupID.ValueInt64(),
+ )
+ if err != nil {
+ if sdkutil.IsHTTPNotFoundError(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+
+ resp.Diagnostics.AddError(
+ "Error Reading Checkly Trigger Group",
+ fmt.Sprintf("Could not retrieve trigger group, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(state.Refresh(ctx, realizedModel, interop.Loaded)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &state)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *TriggerGroupResource) Update(
+ ctx context.Context,
+ req resource.UpdateRequest,
+ resp *resource.UpdateResponse,
+) {
+ var plan TriggerGroupResourceModel
+
+ resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ realizedModel, err := r.client.GetTriggerGroup(
+ ctx,
+ plan.GroupID.ValueInt64(),
+ )
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error Updating Checkly Trigger Group",
+ fmt.Sprintf("Could not update trigger group, unexpected error: %s", err),
+ )
+
+ return
+ }
+
+ resp.Diagnostics.Append(plan.Refresh(ctx, realizedModel, interop.Updated)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+var TriggerGroupID = sdkutil.Identifier{
+ Path: path.Root("id"),
+ Title: "Checkly Trigger Group ID",
+}
+
+var (
+ _ interop.Model[checkly.TriggerGroup] = (*TriggerGroupResourceModel)(nil)
+)
+
+type TriggerGroupResourceModel struct {
+ ID types.String `tfsdk:"id"`
+ GroupID types.Int64 `tfsdk:"group_id"`
+ Token types.String `tfsdk:"token"`
+ URL types.String `tfsdk:"url"`
+}
+
+func (m *TriggerGroupResourceModel) Refresh(ctx context.Context, from *checkly.TriggerGroup, flags interop.RefreshFlags) diag.Diagnostics {
+ if flags.Created() {
+ m.ID = TriggerGroupID.IntoString(from.ID)
+ }
+
+ m.GroupID = types.Int64Value(from.GroupId)
+ m.Token = types.StringValue(from.Token)
+ m.URL = types.StringValue(from.URL)
+
+ return nil
+}
+
+func (m *TriggerGroupResourceModel) Render(ctx context.Context, into *checkly.TriggerGroup) diag.Diagnostics {
+ into.Token = m.Token.ValueString()
+ into.URL = m.URL.ValueString()
+
+ return nil
+}
diff --git a/internal/sdkutil/sdkutil.go b/internal/sdkutil/sdkutil.go
new file mode 100644
index 0000000..14aa748
--- /dev/null
+++ b/internal/sdkutil/sdkutil.go
@@ -0,0 +1,103 @@
+package sdkutil
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/path"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+
+ checkly "github.com/checkly/checkly-go-sdk"
+)
+
+type Identifier struct {
+ Path path.Path
+ Title string
+}
+
+func (i *Identifier) FromString(id types.String) (int64, diag.Diagnostics) {
+ if id.IsUnknown() {
+ return 0, diag.Diagnostics{
+ diag.NewAttributeErrorDiagnostic(
+ i.Path,
+ "Unknown "+i.Title,
+ "", // TODO
+ ),
+ }
+ }
+
+ if id.IsNull() {
+ return 0, diag.Diagnostics{
+ diag.NewAttributeErrorDiagnostic(
+ i.Path,
+ "Missing "+i.Title,
+ "", // TODO
+ ),
+ }
+ }
+
+ val, err := strconv.ParseInt(id.ValueString(), 10, 64)
+ if err != nil {
+ return 0, diag.Diagnostics{
+ diag.NewAttributeErrorDiagnostic(
+ i.Path,
+ "Invalid "+i.Title,
+ "Value must be numeric, but was not: "+err.Error(),
+ ),
+ }
+ }
+
+ return val, nil
+}
+
+func (i *Identifier) IntoString(id int64) types.String {
+ return types.StringValue(fmt.Sprintf("%d", id))
+}
+
+func KeyValuesFromMap(m types.Map) []checkly.KeyValue {
+ if m.IsNull() {
+ return nil
+ }
+
+ var values []checkly.KeyValue
+ for key, val := range m.Elements() {
+ values = append(values, checkly.KeyValue{
+ Key: key,
+ Value: val.(types.String).ValueString(),
+ })
+ }
+
+ return values
+}
+
+func KeyValuesIntoMap(values *[]checkly.KeyValue) types.Map {
+ if values == nil {
+ return types.MapNull(types.StringType)
+ }
+
+ mapValues := make(map[string]attr.Value, len(*values))
+ for _, kv := range *values {
+ mapValues[kv.Key] = types.StringValue(kv.Value)
+ }
+
+ return types.MapValueMust(types.StringType, mapValues)
+}
+
+func IsHTTPNotFoundError(err error) bool {
+ // Unfortunately the SDK presents HTTP errors in a completely unusable way,
+ // forcing us to match against string values.
+ msg := err.Error()
+
+ switch {
+ case strings.Contains(msg, "unexpected response status: 404"):
+ return true
+ // Unfortunate inconsistency.
+ case strings.Contains(msg, "unexpected response status 404"):
+ return true
+ }
+
+ return false
+}
diff --git a/main.go b/main.go
index fb402ba..69ce3c8 100644
--- a/main.go
+++ b/main.go
@@ -1,25 +1,38 @@
package main
import (
+ "context"
"flag"
+ "log"
- "github.com/checkly/terraform-provider-checkly/checkly"
- "github.com/hashicorp/terraform-plugin-sdk/v2/plugin"
+ "github.com/hashicorp/terraform-plugin-framework/providerserver"
+
+ "github.com/checkly/terraform-provider-checkly/internal/provider"
+ "github.com/checkly/terraform-provider-checkly/internal/provider/globalregistry"
)
-//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs
+var (
+ version = "dev"
+)
func main() {
- var debugMode bool
+ var debug bool
- flag.BoolVar(&debugMode, "debug", false, "set to true to run the provider with support for debuggers like delve")
+ flag.BoolVar(&debug, "debug", false, "set to true to run the provider with support for debuggers like delve")
flag.Parse()
- opts := &plugin.ServeOpts{
- Debug: debugMode,
- ProviderAddr: "registry.terraform.io/checkly/checkly",
- ProviderFunc: checkly.Provider,
+ opts := providerserver.ServeOpts{
+ Address: "registry.terraform.io/checkly/checkly",
+ Debug: debug,
}
- plugin.Serve(opts)
+ err := providerserver.Serve(
+ context.Background(),
+ provider.New(version, globalregistry.Registry),
+ opts,
+ )
+
+ if err != nil {
+ log.Fatal(err.Error())
+ }
}
diff --git a/terraform-registry-manifest.json b/terraform-registry-manifest.json
index 5a294ba..8f42373 100644
--- a/terraform-registry-manifest.json
+++ b/terraform-registry-manifest.json
@@ -1,7 +1,8 @@
{
"version": 1,
"metadata": {
- "protocol_versions": ["5.0"]
+ "protocol_versions": [
+ "6.0"
+ ]
}
-}
-
+}
\ No newline at end of file
diff --git a/tools/go.mod b/tools/go.mod
new file mode 100644
index 0000000..19925b2
--- /dev/null
+++ b/tools/go.mod
@@ -0,0 +1,57 @@
+module tools
+
+go 1.23.3
+
+require github.com/hashicorp/terraform-plugin-docs v0.20.0
+
+require (
+ github.com/BurntSushi/toml v1.2.1 // indirect
+ github.com/Kunde21/markdownfmt/v3 v3.1.0 // indirect
+ github.com/Masterminds/goutils v1.1.1 // indirect
+ github.com/Masterminds/semver/v3 v3.2.0 // indirect
+ github.com/Masterminds/sprig/v3 v3.2.3 // indirect
+ github.com/ProtonMail/go-crypto v1.1.3 // indirect
+ github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
+ github.com/armon/go-radix v1.0.0 // indirect
+ github.com/bgentry/speakeasy v0.1.0 // indirect
+ github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
+ github.com/cloudflare/circl v1.5.0 // indirect
+ github.com/fatih/color v1.18.0 // indirect
+ github.com/google/uuid v1.6.0 // indirect
+ github.com/hashicorp/cli v1.1.6 // indirect
+ 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-multierror v1.1.1 // indirect
+ github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
+ github.com/hashicorp/go-uuid v1.0.3 // indirect
+ github.com/hashicorp/go-version v1.7.0 // indirect
+ github.com/hashicorp/hc-install v0.9.0 // indirect
+ github.com/hashicorp/terraform-exec v0.21.0 // indirect
+ github.com/hashicorp/terraform-json v0.23.0 // indirect
+ github.com/huandu/xstrings v1.3.3 // indirect
+ github.com/imdario/mergo v0.3.15 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mattn/go-runewidth v0.0.16 // indirect
+ github.com/mitchellh/copystructure v1.2.0 // indirect
+ github.com/mitchellh/reflectwalk v1.0.2 // indirect
+ github.com/posener/complete v1.2.3 // indirect
+ github.com/rivo/uniseg v0.4.7 // indirect
+ github.com/shopspring/decimal v1.3.1 // indirect
+ github.com/spf13/cast v1.5.0 // indirect
+ github.com/stretchr/testify v1.9.0 // indirect
+ github.com/yuin/goldmark v1.7.7 // indirect
+ github.com/yuin/goldmark-meta v1.1.0 // indirect
+ github.com/zclconf/go-cty v1.15.0 // indirect
+ go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect
+ golang.org/x/crypto v0.29.0 // indirect
+ golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/net v0.31.0 // indirect
+ golang.org/x/sys v0.27.0 // indirect
+ golang.org/x/text v0.20.0 // indirect
+ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
+ gopkg.in/yaml.v2 v2.4.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/tools/go.sum b/tools/go.sum
new file mode 100644
index 0000000..adff740
--- /dev/null
+++ b/tools/go.sum
@@ -0,0 +1,203 @@
+dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
+dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
+github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/Kunde21/markdownfmt/v3 v3.1.0 h1:KiZu9LKs+wFFBQKhrZJrFZwtLnCCWJahL+S+E/3VnM0=
+github.com/Kunde21/markdownfmt/v3 v3.1.0/go.mod h1:tPXN1RTyOzJwhfHoon9wUr4HGYmWgVxSQN6VBJDkrVc=
+github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
+github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
+github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
+github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
+github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
+github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
+github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
+github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
+github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
+github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
+github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
+github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
+github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
+github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
+github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
+github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
+github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
+github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
+github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
+github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
+github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
+github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
+github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
+github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
+github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
+github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/hashicorp/cli v1.1.6 h1:CMOV+/LJfL1tXCOKrgAX0uRKnzjj/mpmqNXloRSy2K8=
+github.com/hashicorp/cli v1.1.6/go.mod h1:MPon5QYlgjjo0BSoAiN0ESeT5fRzDjVRp+uioJ0piz4=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
+github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=
+github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
+github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
+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.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+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-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
+github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
+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.9.0 h1:2dIk8LcvANwtv3QZLckxcjyF5w8KVtiMxu6G6eLhghE=
+github.com/hashicorp/hc-install v0.9.0/go.mod h1:+6vOP+mf3tuGgMApVYtmsnDoKWMDcFXeTxCACYZ8SFg=
+github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ=
+github.com/hashicorp/terraform-exec v0.21.0/go.mod h1:1PPeMYou+KDUSSeRE9szMZ/oHf4fYUmB923Wzbq1ICg=
+github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2xoR+lppBkI=
+github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c=
+github.com/hashicorp/terraform-plugin-docs v0.20.0 h1:ox7rm1FN0dVZaJBUzkVVh10R1r3+FeMQWL0QopQ9d7o=
+github.com/hashicorp/terraform-plugin-docs v0.20.0/go.mod h1:A/+4SVMdAkQYtIBtaxV0H7AU862TxVZk/hhKaMDQB6Y=
+github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4=
+github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
+github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
+github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
+github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
+github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
+github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
+github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
+github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.2.3 h1:NP0eAhjcjImqslEwo/1hq7gpajME0fTLTezBKDqfXqo=
+github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
+github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
+github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
+github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
+github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
+github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
+github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
+github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yuin/goldmark v1.7.7 h1:5m9rrB1sW3JUMToKFQfb+FGt1U7r57IHu5GrYrG2nqU=
+github.com/yuin/goldmark v1.7.7/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
+github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
+github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
+github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ=
+github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
+go.abhg.dev/goldmark/frontmatter v0.2.0 h1:P8kPG0YkL12+aYk2yU3xHv4tcXzeVnN+gU0tJ5JnxRw=
+go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px76YjkOzhB4YlU=
+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.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
+golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
+golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
+golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
+golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
+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-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
+golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
+golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
+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.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
+golang.org/x/sync v0.9.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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+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.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
+golang.org/x/sys v0.27.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.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
+golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
+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.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
+golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
+gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/tools/tools.go b/tools/tools.go
index 7d8afd6..efef263 100644
--- a/tools/tools.go
+++ b/tools/tools.go
@@ -1,8 +1,15 @@
-//go:build tools
+//go:build generate
package tools
import (
- // [tfplugindocs](https://github.com/hashicorp/terraform-plugin-docs).
_ "github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs"
)
+
+// Format Terraform code for use in documentation.
+// If you do not have Terraform installed, you can remove the formatting command, but it is suggested
+// to ensure the documentation is formatted properly.
+//go:generate terraform fmt -recursive ../examples/
+
+// Generate documentation.
+//go:generate go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate --provider-dir ..