Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 29 additions & 26 deletions checkly/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,35 @@ func Provider() *schema.Provider {
},
},
ResourcesMap: map[string]*schema.Resource{
"checkly_check": resourceCheck(),
"checkly_heartbeat": resourceHeartbeat(), // Renamed
"checkly_heartbeat_monitor": resourceHeartbeatMonitor(),
"checkly_tcp_check": resourceTCPCheck(), // Renamed
"checkly_tcp_monitor": resourceTCPMonitor(),
"checkly_check_group": resourceCheckGroup(),
"checkly_check_group_v2": resourceCheckGroupV2(),
"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(),
"checkly_client_certificate": resourceClientCertificate(),
"checkly_status_page": resourceStatusPage(),
"checkly_status_page_service": resourceStatusPageService(),
"checkly_url_monitor": resourceURLMonitor(),
"checkly_dns_monitor": resourceDNSMonitor(),
"checkly_icmp_monitor": resourceICMPMonitor(),
"checkly_grpc_monitor": resourceGRPCMonitor(),
"checkly_traceroute_monitor": resourceTracerouteMonitor(),
"checkly_ssl_monitor": resourceSSLMonitor(),
"checkly_playwright_check_suite": resourcePlaywrightCheckSuite(),
"checkly_playwright_code_bundle": resourcePlaywrightCodeBundle(),
"checkly_check": resourceCheck(),
"checkly_heartbeat": resourceHeartbeat(), // Renamed
"checkly_heartbeat_monitor": resourceHeartbeatMonitor(),
"checkly_tcp_check": resourceTCPCheck(), // Renamed
"checkly_tcp_monitor": resourceTCPMonitor(),
"checkly_check_group": resourceCheckGroup(),
"checkly_check_group_v2": resourceCheckGroupV2(),
"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(),
"checkly_client_certificate": resourceClientCertificate(),
"checkly_status_page": resourceStatusPage(),
"checkly_status_page_service": resourceStatusPageService(),
"checkly_status_page_v3": resourceStatusPageV3(),
"checkly_status_page_v3_component": resourceStatusPageV3Component(),
"checkly_status_page_v3_automation_rule": resourceStatusPageV3AutomationRule(),
"checkly_url_monitor": resourceURLMonitor(),
"checkly_dns_monitor": resourceDNSMonitor(),
"checkly_icmp_monitor": resourceICMPMonitor(),
"checkly_grpc_monitor": resourceGRPCMonitor(),
"checkly_traceroute_monitor": resourceTracerouteMonitor(),
"checkly_ssl_monitor": resourceSSLMonitor(),
"checkly_playwright_check_suite": resourcePlaywrightCheckSuite(),
"checkly_playwright_code_bundle": resourcePlaywrightCodeBundle(),
},
DataSourcesMap: map[string]*schema.Resource{
"checkly_static_ips": dataSourceStaticIPs(),
Expand Down
4 changes: 3 additions & 1 deletion checkly/resource_status_page.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ func resourceStatusPage() *schema.Resource {
StateContext: schema.ImportStatePassthroughContext,
},
Description: "Checkly status pages allow you to easily communicate " +
"the uptime and health of your applications and services to your customers.",
"the uptime and health of your applications and services to your customers. " +
"This resource creates a legacy v2 status page: the new " +
"`checkly_status_page_v3` resource should always be preferred for new pages.",
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Expand Down
4 changes: 3 additions & 1 deletion checkly/resource_status_page_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ func resourceStatusPageService() *schema.Resource {
StateContext: schema.ImportStatePassthroughContext,
},
Description: "Status page services represent functional pieces of " +
"your application or website, such as landing page, API, support portal etc.",
"your application or website, such as landing page, API, support portal etc. " +
"Services belong to legacy v2 status pages: on new v3 pages, use " +
"`checkly_status_page_v3_component` instead.",
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Expand Down
221 changes: 221 additions & 0 deletions checkly/resource_status_page_v3.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package checkly

import (
"context"
"fmt"
"regexp"
"strings"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

checkly "github.com/checkly/checkly-go-sdk"
)

var statusPageV3ThemeValues = allowedValues[string]{
{Value: "AUTO"},
{Value: "DARK"},
{Value: "LIGHT"},
}

// The server lowercases the URL before storing it, so accepting uppercase
// here would create a permanent diff between config and state.
var statusPageV3URLRegex = regexp.MustCompile(`^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$`)

func resourceStatusPageV3() *schema.Resource {
return &schema.Resource{
Create: resourceStatusPageV3Create,
Read: resourceStatusPageV3Read,
Update: resourceStatusPageV3Update,
Delete: resourceStatusPageV3Delete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Description: "Checkly status pages allow you to easily communicate " +
"the uptime and health of your applications and services to your " +
"customers. A v3 status page has no cards or services: its " +
"structure is managed with `checkly_status_page_v3_component` " +
"resources, and incidents can be automated with " +
"`checkly_status_page_v3_automation_rule` resources. The " +
"`checkly_status_page_v3` resource should always be preferred " +
"over the old `checkly_status_page` resource.",
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
Description: "The name of the status page.",
},
"url": {
Type: schema.TypeString,
Required: true,
Description: "The unique subdomain of the status page: lowercase alphanumeric characters and dashes, no leading or trailing dash, at most 63 characters.",
ValidateFunc: func(value interface{}, key string) (warns []string, errs []error) {
v := value.(string)
if !statusPageV3URLRegex.MatchString(v) {
errs = append(errs, fmt.Errorf("%q can only contain lowercase alphanumeric characters and dashes, with no leading or trailing dash, got: %s", key, v))
}
return warns, errs
},
},
"custom_domain": {
Type: schema.TypeString,
Optional: true,
Description: "A custom user domain, e.g. \"status.example.com\". See the docs on updating your DNS and SSL usage.",
},
"description": {
Type: schema.TypeString,
Optional: true,
Description: "A short description shown on the status page.",
},
"logo": {
Type: schema.TypeString,
Optional: true,
Description: "A URL to an image file to use as the logo for the status page.",
},
"logo_dark": {
Type: schema.TypeString,
Optional: true,
Description: "A URL to an image file to use as the logo in dark mode.",
},
"redirect_to": {
Type: schema.TypeString,
Optional: true,
Description: "The URL the user should be redirected to when clicking the logo.",
},
"favicon": {
Type: schema.TypeString,
Optional: true,
Description: "A URL to an image file to use as the favicon of the status page.",
},
"default_theme": {
Type: schema.TypeString,
Optional: true,
Default: "AUTO",
Description: "The default theme of the status page. " + statusPageV3ThemeValues.String(),
ValidateFunc: validateOneOf(statusPageV3ThemeValues.Values()),
},
"privacy_policy_link": {
Type: schema.TypeString,
Optional: true,
Description: "A link to your privacy policy, shown in the page footer.",
},
"terms_of_service_link": {
Type: schema.TypeString,
Optional: true,
Description: "A link to your terms of service, shown in the page footer.",
},
"footer_text": {
Type: schema.TypeString,
Optional: true,
Description: "Free-form footer text.",
},
"google_analytics_tag": {
Type: schema.TypeString,
Optional: true,
Description: "A Google Analytics tag ID (e.g. \"G-XXXXXXXXXX\") to embed on the public page.",
},
"allow_indexing": {
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "Whether search engines may index the public page. (Default `true`).",
},
},
}
}

func statusPageV3FromResourceData(d *schema.ResourceData) checkly.StatusPageV3 {
return checkly.StatusPageV3{
ID: d.Id(),
Name: d.Get("name").(string),
URL: d.Get("url").(string),
CustomDomain: d.Get("custom_domain").(string),
Description: d.Get("description").(string),
Logo: d.Get("logo").(string),
LogoDark: d.Get("logo_dark").(string),
RedirectTo: d.Get("redirect_to").(string),
Favicon: d.Get("favicon").(string),
DefaultTheme: checkly.StatusPageTheme(d.Get("default_theme").(string)),
PrivacyPolicyLink: d.Get("privacy_policy_link").(string),
TermsOfServiceLink: d.Get("terms_of_service_link").(string),
FooterText: d.Get("footer_text").(string),
GoogleAnalyticsTag: d.Get("google_analytics_tag").(string),
AllowIndexing: d.Get("allow_indexing").(bool),
}
}

func resourceDataFromStatusPageV3(p *checkly.StatusPageV3, d *schema.ResourceData) error {
pairs := []struct {
key string
value interface{}
}{
{"name", p.Name},
{"url", p.URL},
{"custom_domain", p.CustomDomain},
{"description", p.Description},
{"logo", p.Logo},
{"logo_dark", p.LogoDark},
{"redirect_to", p.RedirectTo},
{"favicon", p.Favicon},
{"default_theme", string(p.DefaultTheme)},
{"privacy_policy_link", p.PrivacyPolicyLink},
{"terms_of_service_link", p.TermsOfServiceLink},
{"footer_text", p.FooterText},
{"google_analytics_tag", p.GoogleAnalyticsTag},
{"allow_indexing", p.AllowIndexing},
}
for _, pair := range pairs {
if err := d.Set(pair.key, pair.value); err != nil {
return fmt.Errorf("failed to set %q: %w", pair.key, err)
}
}
return nil
}

func resourceStatusPageV3Create(d *schema.ResourceData, client interface{}) error {
statusPage := statusPageV3FromResourceData(d)
ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
defer cancel()
result, err := client.(checkly.Client).CreateStatusPageV3(ctx, statusPage)
if err != nil {
return fmt.Errorf("CreateStatusPageV3: API error: %w", err)
}
d.SetId(result.ID)
return resourceStatusPageV3Read(d, client)
}

func resourceStatusPageV3Read(d *schema.ResourceData, client interface{}) error {
ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
defer cancel()
statusPage, err := client.(checkly.Client).GetStatusPageV3(ctx, d.Id())
if err != nil {
if strings.Contains(err.Error(), "404") {
// If the resource was deleted remotely, mark it as successfully
// gone by unsetting its ID.
d.SetId("")
return nil
}
return fmt.Errorf("resourceStatusPageV3Read: API error: %w", err)
}
return resourceDataFromStatusPageV3(statusPage, d)
}

func resourceStatusPageV3Update(d *schema.ResourceData, client interface{}) error {
statusPage := statusPageV3FromResourceData(d)
ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
defer cancel()
_, err := client.(checkly.Client).UpdateStatusPageV3(ctx, statusPage.ID, statusPage)
if err != nil {
return fmt.Errorf("resourceStatusPageV3Update: API error: %w", err)
}
return resourceStatusPageV3Read(d, client)
}

func resourceStatusPageV3Delete(d *schema.ResourceData, client interface{}) error {
ctx, cancel := context.WithTimeout(context.Background(), apiCallTimeout())
defer cancel()
err := client.(checkly.Client).DeleteStatusPageV3(ctx, d.Id())
if err != nil {
return fmt.Errorf("resourceStatusPageV3Delete: API error: %w", err)
}
return nil
}
Loading
Loading