Skip to content

Commit 0f8362c

Browse files
authored
feat(observability): add google chat to webhooks (#961)
1 parent 971cd27 commit 0f8362c

9 files changed

Lines changed: 112 additions & 23 deletions

File tree

docs/data-sources/observability_instance.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ Read-Only:
9696
Read-Only:
9797

9898
- `auth_identity` (String) SMTP authentication information. Must be a valid email address
99-
- `auth_password` (String) SMTP authentication password.
99+
- `auth_password` (String, Sensitive) SMTP authentication password.
100100
- `auth_username` (String) SMTP authentication username.
101101
- `from` (String) The sender email address. Must be a valid email address
102102
- `smart_host` (String) The SMTP host through which emails are sent.
@@ -119,8 +119,9 @@ Read-Only:
119119

120120
Read-Only:
121121

122+
- `google_chat` (Boolean) Google Chat webhooks require special handling, set this to true if the webhook is for Google Chat.
122123
- `ms_teams` (Boolean) Microsoft Teams webhooks require special handling, set this to true if the webhook is for Microsoft Teams.
123-
- `url` (String) The endpoint to send HTTP POST requests to. Must be a valid URL
124+
- `url` (String, Sensitive) The endpoint to send HTTP POST requests to. Must be a valid URL
124125

125126

126127

docs/resources/observability_instance.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ Optional:
101101
Optional:
102102

103103
- `auth_identity` (String) SMTP authentication information. Must be a valid email address
104-
- `auth_password` (String) SMTP authentication password.
104+
- `auth_password` (String, Sensitive) SMTP authentication password.
105105
- `auth_username` (String) SMTP authentication username.
106106
- `from` (String) The sender email address. Must be a valid email address
107107
- `smart_host` (String) The SMTP host through which emails are sent.
@@ -124,8 +124,9 @@ Optional:
124124

125125
Optional:
126126

127+
- `google_chat` (Boolean) Google Chat webhooks require special handling, set this to true if the webhook is for Google Chat.
127128
- `ms_teams` (Boolean) Microsoft Teams webhooks require special handling, set this to true if the webhook is for Microsoft Teams.
128-
- `url` (String) The endpoint to send HTTP POST requests to. Must be a valid URL
129+
- `url` (String, Sensitive) The endpoint to send HTTP POST requests to. Must be a valid URL
129130

130131

131132

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ require (
2323
github.com/stackitcloud/stackit-sdk-go/services/modelserving v0.5.1
2424
github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v1.5.2
2525
github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.3.1
26-
github.com/stackitcloud/stackit-sdk-go/services/observability v0.10.0
26+
github.com/stackitcloud/stackit-sdk-go/services/observability v0.11.0
2727
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.24.1
2828
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v1.2.1
2929
github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.25.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,8 +176,8 @@ github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v1.5.2 h1:BQ+qAkVS/a
176176
github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v1.5.2/go.mod h1:oc8Mpwl7O6EZwG0YxfhOzNCJwNQBWK5rFh764OtxoMY=
177177
github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.3.1 h1:4jsFLbDVEosYTgQz6lPds1E9KDOiHwjuhWqcG+lo5B4=
178178
github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.3.1/go.mod h1:j1SHAS5lN8F9b/iPUOfjAl9QAA9tOT7NKOiDEzcM2zc=
179-
github.com/stackitcloud/stackit-sdk-go/services/observability v0.10.0 h1:SIctDqGprEoZArWaTds7hzQyh8Pqaz95Nmuj/1QuDEQ=
180-
github.com/stackitcloud/stackit-sdk-go/services/observability v0.10.0/go.mod h1:tJEOi6L0le4yQZPGwalup/PZ13gqs1aCQDqlUs2cYW0=
179+
github.com/stackitcloud/stackit-sdk-go/services/observability v0.11.0 h1:5BhZ/Ry3KWmhjBTVFBdLfVjPxxUOvVJuhGY0bnGRS7I=
180+
github.com/stackitcloud/stackit-sdk-go/services/observability v0.11.0/go.mod h1:tJEOi6L0le4yQZPGwalup/PZ13gqs1aCQDqlUs2cYW0=
181181
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.24.1 h1:50n87uZn0EvSP9hJGLqd3Wm2hfqbyh7BMGGCk7axgqA=
182182
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.24.1/go.mod h1:jfguuSPa56Z5Bzs/Xg/CI37XzPo5Zn5lzC5LhfuT8Qc=
183183
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v1.2.1 h1:K8vXele3U6b5urcSIpq21EkVblWfPDY3eMPSuQ48TkI=

stackit/internal/services/observability/instance/datasource.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ func (d *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques
210210
"auth_password": schema.StringAttribute{
211211
Description: "SMTP authentication password.",
212212
Computed: true,
213+
Sensitive: true,
213214
},
214215
"auth_username": schema.StringAttribute{
215216
Description: "SMTP authentication username.",
@@ -262,11 +263,16 @@ func (d *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques
262263
"url": schema.StringAttribute{
263264
Description: "The endpoint to send HTTP POST requests to. Must be a valid URL",
264265
Computed: true,
266+
Sensitive: true,
265267
},
266268
"ms_teams": schema.BoolAttribute{
267269
Description: "Microsoft Teams webhooks require special handling, set this to true if the webhook is for Microsoft Teams.",
268270
Computed: true,
269271
},
272+
"google_chat": schema.BoolAttribute{
273+
Description: "Google Chat webhooks require special handling, set this to true if the webhook is for Google Chat.",
274+
Computed: true,
275+
},
270276
},
271277
},
272278
},

stackit/internal/services/observability/instance/resource.go

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/hashicorp/terraform-plugin-framework/path"
2121
"github.com/hashicorp/terraform-plugin-framework/resource"
2222
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
23+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
2324
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
2425
"github.com/hashicorp/terraform-plugin-framework/resource/schema/mapplanmodifier"
2526
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
@@ -202,13 +203,15 @@ var opsgenieConfigsTypes = map[string]attr.Type{
202203

203204
// Struct corresponding to Model.AlertConfig.receivers.webHooksConfigs
204205
type webHooksConfigsModel struct {
205-
Url types.String `tfsdk:"url"`
206-
MsTeams types.Bool `tfsdk:"ms_teams"`
206+
Url types.String `tfsdk:"url"`
207+
MsTeams types.Bool `tfsdk:"ms_teams"`
208+
GoogleChat types.Bool `tfsdk:"google_chat"`
207209
}
208210

209211
var webHooksConfigsTypes = map[string]attr.Type{
210-
"url": types.StringType,
211-
"ms_teams": types.BoolType,
212+
"url": types.StringType,
213+
"ms_teams": types.BoolType,
214+
"google_chat": types.BoolType,
212215
}
213216

214217
var routeDescriptions = map[string]string{
@@ -591,6 +594,7 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
591594
"auth_password": schema.StringAttribute{
592595
Description: "SMTP authentication password.",
593596
Optional: true,
597+
Sensitive: true,
594598
},
595599
"auth_username": schema.StringAttribute{
596600
Description: "SMTP authentication username.",
@@ -645,14 +649,26 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
645649
listvalidator.SizeAtLeast(1),
646650
},
647651
NestedObject: schema.NestedAttributeObject{
652+
Validators: []validator.Object{
653+
WebhookConfigMutuallyExclusive(),
654+
},
648655
Attributes: map[string]schema.Attribute{
649656
"url": schema.StringAttribute{
650657
Description: "The endpoint to send HTTP POST requests to. Must be a valid URL",
651658
Optional: true,
659+
Sensitive: true,
652660
},
653661
"ms_teams": schema.BoolAttribute{
654662
Description: "Microsoft Teams webhooks require special handling, set this to true if the webhook is for Microsoft Teams.",
655663
Optional: true,
664+
Computed: true,
665+
Default: booldefault.StaticBool(false),
666+
},
667+
"google_chat": schema.BoolAttribute{
668+
Description: "Google Chat webhooks require special handling, set this to true if the webhook is for Google Chat.",
669+
Optional: true,
670+
Computed: true,
671+
Default: booldefault.StaticBool(false),
656672
},
657673
},
658674
},
@@ -1687,9 +1703,13 @@ func mapReceiversToAttributes(ctx context.Context, respReceivers *[]observabilit
16871703
webhooksConfigList := []attr.Value{}
16881704
if receiver.WebHookConfigs != nil {
16891705
for _, webhookConfig := range *receiver.WebHookConfigs {
1706+
msTeamsValue := types.BoolPointerValue(webhookConfig.MsTeams)
1707+
googleChatValue := types.BoolPointerValue(webhookConfig.GoogleChat)
1708+
16901709
webHookConfigsMap := map[string]attr.Value{
1691-
"url": types.StringPointerValue(webhookConfig.Url),
1692-
"ms_teams": types.BoolPointerValue(webhookConfig.MsTeams),
1710+
"url": types.StringPointerValue(webhookConfig.Url),
1711+
"ms_teams": msTeamsValue,
1712+
"google_chat": googleChatValue,
16931713
}
16941714
webHookConfigsModel, diags := types.ObjectValue(webHooksConfigsTypes, webHookConfigsMap)
16951715
if diags.HasError() {
@@ -2040,8 +2060,9 @@ func toReceiverPayload(ctx context.Context, model *alertConfigModel) (*[]observa
20402060
for i := range receiverWebHooksConfigs {
20412061
webHooksConfig := receiverWebHooksConfigs[i]
20422062
payloadWebHooksConfigs = append(payloadWebHooksConfigs, observability.CreateAlertConfigReceiverPayloadWebHookConfigsInner{
2043-
Url: conversion.StringValueToPointer(webHooksConfig.Url),
2044-
MsTeams: conversion.BoolValueToPointer(webHooksConfig.MsTeams),
2063+
Url: conversion.StringValueToPointer(webHooksConfig.Url),
2064+
MsTeams: conversion.BoolValueToPointer(webHooksConfig.MsTeams),
2065+
GoogleChat: conversion.BoolValueToPointer(webHooksConfig.GoogleChat),
20452066
})
20462067
}
20472068
receiverPayload.WebHookConfigs = &payloadWebHooksConfigs
@@ -2214,3 +2235,51 @@ func setMetricsRetentions(ctx context.Context, state *tfsdk.State, model *Model)
22142235
func setAlertConfig(ctx context.Context, state *tfsdk.State, model *Model) diag.Diagnostics {
22152236
return state.SetAttribute(ctx, path.Root("alert_config"), model.AlertConfig)
22162237
}
2238+
2239+
type webhookConfigMutuallyExclusive struct{}
2240+
2241+
func (v webhookConfigMutuallyExclusive) Description(_ context.Context) string {
2242+
return "ms_teams and google_chat cannot both be true"
2243+
}
2244+
2245+
func (v webhookConfigMutuallyExclusive) MarkdownDescription(ctx context.Context) string {
2246+
return v.Description(ctx)
2247+
}
2248+
2249+
func (v webhookConfigMutuallyExclusive) ValidateObject(_ context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) { //nolint:gocritic // req parameter signature required by validator.Object interface
2250+
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
2251+
return
2252+
}
2253+
2254+
attributes := req.ConfigValue.Attributes()
2255+
2256+
msTeamsAttr, msTeamsExists := attributes["ms_teams"]
2257+
googleChatAttr, googleChatExists := attributes["google_chat"]
2258+
2259+
if !msTeamsExists || !googleChatExists {
2260+
return
2261+
}
2262+
2263+
if msTeamsAttr.IsNull() || msTeamsAttr.IsUnknown() || googleChatAttr.IsNull() || googleChatAttr.IsUnknown() {
2264+
return
2265+
}
2266+
2267+
msTeamsValue, ok1 := msTeamsAttr.(types.Bool)
2268+
googleChatValue, ok2 := googleChatAttr.(types.Bool)
2269+
2270+
if !ok1 || !ok2 {
2271+
return
2272+
}
2273+
2274+
if msTeamsValue.ValueBool() && googleChatValue.ValueBool() {
2275+
resp.Diagnostics.AddAttributeError(
2276+
req.Path,
2277+
"Invalid Webhook Configuration",
2278+
"Both ms_teams and google_chat cannot be set to true at the same time. Only one can be true.",
2279+
)
2280+
}
2281+
}
2282+
2283+
func WebhookConfigMutuallyExclusive() validator.Object {
2284+
return webhookConfigMutuallyExclusive{}
2285+
}

stackit/internal/services/observability/instance/resource_test.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ func fixtureOpsGenieConfigsModel() basetypes.ListValue {
4444
func fixtureWebHooksConfigsModel() basetypes.ListValue {
4545
return types.ListValueMust(types.ObjectType{AttrTypes: webHooksConfigsTypes}, []attr.Value{
4646
types.ObjectValueMust(webHooksConfigsTypes, map[string]attr.Value{
47-
"url": types.StringValue("http://example.com"),
48-
"ms_teams": types.BoolValue(true),
47+
"url": types.StringValue("http://example.com"),
48+
"ms_teams": types.BoolValue(true),
49+
"google_chat": types.BoolValue(true),
4950
}),
5051
})
5152
}
@@ -150,8 +151,9 @@ func fixtureOpsGenieConfigsPayload() observability.CreateAlertConfigReceiverPayl
150151

151152
func fixtureWebHooksConfigsPayload() observability.CreateAlertConfigReceiverPayloadWebHookConfigsInner {
152153
return observability.CreateAlertConfigReceiverPayloadWebHookConfigsInner{
153-
Url: utils.Ptr("http://example.com"),
154-
MsTeams: utils.Ptr(true),
154+
Url: utils.Ptr("http://example.com"),
155+
MsTeams: utils.Ptr(true),
156+
GoogleChat: utils.Ptr(true),
155157
}
156158
}
157159

@@ -231,8 +233,9 @@ func fixtureOpsGenieConfigsResponse() observability.OpsgenieConfig {
231233

232234
func fixtureWebHooksConfigsResponse() observability.WebHook {
233235
return observability.WebHook{
234-
Url: utils.Ptr("http://example.com"),
235-
MsTeams: utils.Ptr(true),
236+
Url: utils.Ptr("http://example.com"),
237+
MsTeams: utils.Ptr(true),
238+
GoogleChat: utils.Ptr(true),
236239
}
237240
}
238241

stackit/internal/services/observability/observability_acc_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ var testConfigVarsMax = config.Variables{
8787
"opsgenie_priority": config.StringVariable("P3"),
8888
"webhook_configs_url": config.StringVariable("https://example.com"),
8989
"ms_teams": config.StringVariable("true"),
90+
"google_chat": config.StringVariable("false"),
9091
"group_by": config.StringVariable("alertname"),
9192
"group_interval": config.StringVariable("10m"),
9293
"group_wait": config.StringVariable("1m"),
@@ -130,6 +131,9 @@ func configVarsMaxUpdated() config.Variables {
130131
tempConfig["alert_rule_expression"] = config.StringVariable(alert_rule_expression_updated)
131132
tempConfig["logalertgroup_interval"] = config.StringVariable("1h")
132133
tempConfig["logalertgroup_expression"] = config.StringVariable(logalertgroup_expression_updated)
134+
tempConfig["webhook_configs_url"] = config.StringVariable("https://chat.googleapis.com/api")
135+
tempConfig["ms_teams"] = config.StringVariable("false")
136+
tempConfig["google_chat"] = config.StringVariable("true")
133137
return tempConfig
134138
}
135139

@@ -510,6 +514,7 @@ func TestAccResourceMax(t *testing.T) {
510514

511515
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.url", testutil.ConvertConfigVariable(testConfigVarsMax["webhook_configs_url"])),
512516
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.ms_teams", testutil.ConvertConfigVariable(testConfigVarsMax["ms_teams"])),
517+
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.google_chat", testutil.ConvertConfigVariable(testConfigVarsMax["google_chat"])),
513518

514519
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.0", testutil.ConvertConfigVariable(testConfigVarsMax["group_by"])),
515520
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_interval", testutil.ConvertConfigVariable(testConfigVarsMax["group_interval"])),
@@ -672,6 +677,7 @@ func TestAccResourceMax(t *testing.T) {
672677

673678
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.url", testutil.ConvertConfigVariable(testConfigVarsMax["webhook_configs_url"])),
674679
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.ms_teams", testutil.ConvertConfigVariable(testConfigVarsMax["ms_teams"])),
680+
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.google_chat", testutil.ConvertConfigVariable(testConfigVarsMax["google_chat"])),
675681

676682
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.route.group_by.0", testutil.ConvertConfigVariable(testConfigVarsMax["group_by"])),
677683
resource.TestCheckResourceAttr("data.stackit_observability_instance.instance", "alert_config.route.group_interval", testutil.ConvertConfigVariable(testConfigVarsMax["group_interval"])),
@@ -893,8 +899,9 @@ func TestAccResourceMax(t *testing.T) {
893899
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.api_url", testutil.ConvertConfigVariable(testConfigVarsMax["opsgenie_api_url"])),
894900
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.opsgenie_configs.0.priority", testutil.ConvertConfigVariable(testConfigVarsMax["opsgenie_priority"])),
895901

896-
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.url", testutil.ConvertConfigVariable(testConfigVarsMax["webhook_configs_url"])),
897-
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.ms_teams", testutil.ConvertConfigVariable(testConfigVarsMax["ms_teams"])),
902+
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.url", testutil.ConvertConfigVariable(configVarsMaxUpdated()["webhook_configs_url"])),
903+
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.ms_teams", testutil.ConvertConfigVariable(configVarsMaxUpdated()["ms_teams"])),
904+
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.receivers.0.webhooks_configs.0.google_chat", testutil.ConvertConfigVariable(configVarsMaxUpdated()["google_chat"])),
898905

899906
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_by.0", testutil.ConvertConfigVariable(testConfigVarsMax["group_by"])),
900907
resource.TestCheckResourceAttr("stackit_observability_instance.instance", "alert_config.route.group_interval", testutil.ConvertConfigVariable(testConfigVarsMax["group_interval"])),

stackit/internal/services/observability/testdata/resource-max.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ variable "opsgenie_api_url" {}
2929
variable "opsgenie_priority" {}
3030
variable "webhook_configs_url" {}
3131
variable "ms_teams" {}
32+
variable "google_chat" {}
3233
variable "group_by" {}
3334
variable "group_interval" {}
3435
variable "group_wait" {}
@@ -125,6 +126,7 @@ resource "stackit_observability_instance" "instance" {
125126
{
126127
url = var.webhook_configs_url
127128
ms_teams = var.ms_teams
129+
google_chat = var.google_chat
128130
}
129131
]
130132
},

0 commit comments

Comments
 (0)