Skip to content

Commit 58cfdc8

Browse files
Alerting Contact Points: Migrate to OpenAPI client (#1226)
The resource behaves exactly as before Some additional improvements may be made afterwards: - Supporting org-specific provisioning - Supporting UI editing (disabling provenance like in #1214) - Setting the name as the main ID. Right now, we're relying with the UIDs as the ID, this is a bit unwieldy. I think the ordering of contacts was probably the initial reason why it was done this way, but I think that can be fixed All of these will come in other PRs because this one is big enough as it is
1 parent 8b88ec0 commit 58cfdc8

File tree

5 files changed

+381
-379
lines changed

5 files changed

+381
-379
lines changed

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ require (
2020
github.com/hashicorp/hcl/v2 v2.19.1
2121
github.com/hashicorp/terraform-plugin-docs v0.16.0
2222
github.com/hashicorp/terraform-plugin-framework v1.4.2
23-
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
2423
github.com/hashicorp/terraform-plugin-go v0.19.0
2524
github.com/hashicorp/terraform-plugin-mux v0.12.0
2625
github.com/hashicorp/terraform-plugin-sdk/v2 v2.30.0

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,6 @@ github.com/hashicorp/terraform-plugin-docs v0.16.0 h1:UmxFr3AScl6Wged84jndJIfFcc
165165
github.com/hashicorp/terraform-plugin-docs v0.16.0/go.mod h1:M3ZrlKBJAbPMtNOPwHicGi1c+hZUh7/g0ifT/z7TVfA=
166166
github.com/hashicorp/terraform-plugin-framework v1.4.2 h1:P7a7VP1GZbjc4rv921Xy5OckzhoiO3ig6SGxwelD2sI=
167167
github.com/hashicorp/terraform-plugin-framework v1.4.2/go.mod h1:GWl3InPFZi2wVQmdVnINPKys09s9mLmTZr95/ngLnbY=
168-
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc=
169-
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg=
170168
github.com/hashicorp/terraform-plugin-go v0.19.0 h1:BuZx/6Cp+lkmiG0cOBk6Zps0Cb2tmqQpDM3iAtnhDQU=
171169
github.com/hashicorp/terraform-plugin-go v0.19.0/go.mod h1:EhRSkEPNoylLQntYsk5KrDHTZJh9HQoumZXbOGOXmec=
172170
github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0=

internal/resources/grafana/resource_alerting_contact_point.go

Lines changed: 49 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import (
66
"log"
77
"strings"
88

9-
gapi "github.com/grafana/grafana-api-golang-client"
9+
"github.com/grafana/grafana-openapi-client-go/client/provisioning"
10+
"github.com/grafana/grafana-openapi-client-go/models"
1011
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1112
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
1213

@@ -46,7 +47,7 @@ Manages Grafana Alerting contact points.
4647
4748
This resource requires Grafana 9.1.0 or later.
4849
`,
49-
CreateContext: createContactPoint,
50+
CreateContext: updateContactPoint,
5051
ReadContext: readContactPoint,
5152
UpdateContext: updateContactPoint,
5253
DeleteContext: deleteContactPoint,
@@ -86,12 +87,14 @@ This resource requires Grafana 9.1.0 or later.
8687

8788
func importContactPoint(ctx context.Context, data *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
8889
name := data.Id()
89-
client := meta.(*common.Client).DeprecatedGrafanaAPI
90+
client := OAPIGlobalClient(meta) // TODO: Support org-scoped contact points
9091

91-
ps, err := client.ContactPointsByName(name)
92+
params := provisioning.NewGetContactpointsParams().WithName(&name)
93+
resp, err := client.Provisioning.GetContactpoints(params)
9294
if err != nil {
9395
return nil, err
9496
}
97+
ps := resp.Payload
9598

9699
if len(ps) == 0 {
97100
return nil, fmt.Errorf("no contact points with the given name were found to import")
@@ -107,25 +110,30 @@ func importContactPoint(ctx context.Context, data *schema.ResourceData, meta int
107110
}
108111

109112
func readContactPoint(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
110-
client := meta.(*common.Client).DeprecatedGrafanaAPI
113+
client := OAPIGlobalClient(meta) // TODO: Support org-scoped contact points
111114

112115
uidsToFetch := unpackUIDs(data.Id())
113116

114-
points := []gapi.ContactPoint{}
117+
resp, err := client.Provisioning.GetContactpoints(nil)
118+
if err != nil {
119+
return diag.FromErr(err)
120+
}
121+
contactPointByUID := map[string]*models.EmbeddedContactPoint{}
122+
for _, p := range resp.Payload {
123+
contactPointByUID[p.UID] = p
124+
}
125+
126+
points := []*models.EmbeddedContactPoint{}
115127
for _, uid := range uidsToFetch {
116-
p, err := client.ContactPoint(uid)
117-
if err != nil {
118-
if strings.HasPrefix(err.Error(), "status: 404") || strings.Contains(err.Error(), "not found") {
119-
log.Printf("[WARN] removing contact point %s from state because it no longer exists in grafana", uid)
120-
continue
121-
}
122-
return diag.FromErr(err)
128+
p, ok := contactPointByUID[uid]
129+
if !ok {
130+
log.Printf("[WARN] removing contact point %s from state because it no longer exists in grafana", uid)
131+
continue
123132
}
124133
points = append(points, p)
125134
}
126135

127-
err := packContactPoints(points, data)
128-
if err != nil {
136+
if err := packContactPoints(points, data); err != nil {
129137
return diag.FromErr(err)
130138
}
131139
uids := make([]string, 0, len(points))
@@ -137,51 +145,28 @@ func readContactPoint(ctx context.Context, data *schema.ResourceData, meta inter
137145
return nil
138146
}
139147

140-
func createContactPoint(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
148+
func updateContactPoint(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
141149
lock := &meta.(*common.Client).AlertingMutex
142-
client := meta.(*common.Client).DeprecatedGrafanaAPI
143-
144-
ps := unpackContactPoints(data)
145-
uids := make([]string, 0, len(ps))
146-
147150
lock.Lock()
148151
defer lock.Unlock()
149-
for i := range ps {
150-
p := ps[i]
151-
uid, err := client.NewContactPoint(&p.gfState)
152-
if err != nil {
153-
return diag.FromErr(err)
154-
}
155-
uids = append(uids, uid)
156-
157-
// Since this is a new resource, the proposed state won't have a UID.
158-
// We need the UID so that we can later associate it with the config returned in the api response.
159-
p.tfState["uid"] = uid
160-
}
161-
162-
data.SetId(packUIDs(uids))
163-
return readContactPoint(ctx, data, meta)
164-
}
165-
166-
func updateContactPoint(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
167-
lock := &meta.(*common.Client).AlertingMutex
168-
client := meta.(*common.Client).DeprecatedGrafanaAPI
152+
client := OAPIGlobalClient(meta) // TODO: Support org-scoped contact points
169153

170154
existingUIDs := unpackUIDs(data.Id())
171155
ps := unpackContactPoints(data)
172156

173157
unprocessedUIDs := toUIDSet(existingUIDs)
174158
newUIDs := make([]string, 0, len(ps))
175-
lock.Lock()
176-
defer lock.Unlock()
177159
for i := range ps {
178160
p := ps[i].gfState
179161
delete(unprocessedUIDs, p.UID)
180-
err := client.UpdateContactPoint(&p)
162+
params := provisioning.NewPutContactpointParams().WithUID(p.UID).WithBody(p)
163+
_, err := client.Provisioning.PutContactpoint(params)
181164
if err != nil {
182-
if strings.HasPrefix(err.Error(), "status: 404") {
183-
uid, err := client.NewContactPoint(&p)
184-
newUIDs = append(newUIDs, uid)
165+
if common.IsNotFoundError(err) {
166+
params := provisioning.NewPostContactpointsParams().WithBody(p)
167+
resp, err := client.Provisioning.PostContactpoints(params)
168+
ps[i].tfState["uid"] = resp.Payload.UID
169+
newUIDs = append(newUIDs, resp.Payload.UID)
185170
if err != nil {
186171
return diag.FromErr(err)
187172
}
@@ -195,7 +180,7 @@ func updateContactPoint(ctx context.Context, data *schema.ResourceData, meta int
195180
// Any UIDs still left in the state that we haven't seen must map to deleted receivers.
196181
// Delete them on the server and drop them from state.
197182
for u := range unprocessedUIDs {
198-
if err := client.DeleteContactPoint(u); err != nil {
183+
if _, err := client.Provisioning.DeleteContactpoints(u); err != nil {
199184
return diag.FromErr(err)
200185
}
201186
}
@@ -207,14 +192,14 @@ func updateContactPoint(ctx context.Context, data *schema.ResourceData, meta int
207192

208193
func deleteContactPoint(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
209194
lock := &meta.(*common.Client).AlertingMutex
210-
client := meta.(*common.Client).DeprecatedGrafanaAPI
195+
lock.Lock()
196+
defer lock.Unlock()
197+
client := OAPIGlobalClient(meta) // TODO: Support org-scoped contact points
211198

212199
uids := unpackUIDs(data.Id())
213200

214-
lock.Lock()
215-
defer lock.Unlock()
216201
for _, uid := range uids {
217-
if err := client.DeleteContactPoint(uid); err != nil {
202+
if _, err := client.Provisioning.DeleteContactpoints(uid); err != nil {
218203
return diag.FromErr(err)
219204
}
220205
}
@@ -239,24 +224,25 @@ func unpackContactPoints(data *schema.ResourceData) []statePair {
239224
return result
240225
}
241226

242-
func unpackPointConfig(n notifier, data interface{}, name string) gapi.ContactPoint {
227+
func unpackPointConfig(n notifier, data interface{}, name string) *models.EmbeddedContactPoint {
243228
pt := n.unpack(data, name)
229+
settings := pt.Settings.(map[string]interface{})
244230
// Treat settings like `omitempty`. Workaround for versions affected by https://github.com/grafana/grafana/issues/55139
245-
for k, v := range pt.Settings {
231+
for k, v := range settings {
246232
if v == "" {
247-
delete(pt.Settings, k)
233+
delete(settings, k)
248234
}
249235
}
250236
return pt
251237
}
252238

253-
func packContactPoints(ps []gapi.ContactPoint, data *schema.ResourceData) error {
239+
func packContactPoints(ps []*models.EmbeddedContactPoint, data *schema.ResourceData) error {
254240
pointsPerNotifier := map[notifier][]interface{}{}
255241
for _, p := range ps {
256242
data.Set("name", p.Name)
257243

258244
for _, n := range notifiers {
259-
if p.Type == n.meta().typeStr {
245+
if *p.Type == n.meta().typeStr {
260246
packed, err := n.pack(p, data)
261247
if err != nil {
262248
return err
@@ -278,16 +264,16 @@ func unpackCommonNotifierFields(raw map[string]interface{}) (string, bool, map[s
278264
return raw["uid"].(string), raw["disable_resolve_message"].(bool), raw["settings"].(map[string]interface{})
279265
}
280266

281-
func packCommonNotifierFields(p *gapi.ContactPoint) map[string]interface{} {
267+
func packCommonNotifierFields(p *models.EmbeddedContactPoint) map[string]interface{} {
282268
return map[string]interface{}{
283269
"uid": p.UID,
284270
"disable_resolve_message": p.DisableResolveMessage,
285271
}
286272
}
287273

288-
func packSettings(p *gapi.ContactPoint) map[string]interface{} {
274+
func packSettings(p *models.EmbeddedContactPoint) map[string]interface{} {
289275
settings := map[string]interface{}{}
290-
for k, v := range p.Settings {
276+
for k, v := range p.Settings.(map[string]interface{}) {
291277
settings[k] = fmt.Sprintf("%#v", v)
292278
}
293279
return settings
@@ -342,8 +328,8 @@ func toUIDSet(uids []string) map[string]bool {
342328
type notifier interface {
343329
meta() notifierMeta
344330
schema() *schema.Resource
345-
pack(p gapi.ContactPoint, data *schema.ResourceData) (interface{}, error)
346-
unpack(raw interface{}, name string) gapi.ContactPoint
331+
pack(p *models.EmbeddedContactPoint, data *schema.ResourceData) (interface{}, error)
332+
unpack(raw interface{}, name string) *models.EmbeddedContactPoint
347333
}
348334

349335
type notifierMeta struct {
@@ -355,7 +341,7 @@ type notifierMeta struct {
355341

356342
type statePair struct {
357343
tfState map[string]interface{}
358-
gfState gapi.ContactPoint
344+
gfState *models.EmbeddedContactPoint
359345
}
360346

361347
func packNotifierStringField(gfSettings, tfSettings *map[string]interface{}, gfKey, tfKey string) {

0 commit comments

Comments
 (0)