Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4755eb5

Browse files
yuri-tceretianJacobsonMT
andauthored
Alerting: Support template UID in template service (grafana#92164)
* add uid to template and populate it * update delete method to support both uid and name * update UpdateTemplate to support search by UID and fallback to name + support renaming of the template * update upsert to exit if template not found and uid is specified * update Get method to address by name or uid --------- Co-authored-by: Matthew Jacobson <matthew.jacobson@grafana.com>
1 parent 354aee9 commit 4755eb5

File tree

4 files changed

+364
-128
lines changed

4 files changed

+364
-128
lines changed

‎pkg/services/ngalert/api/api_provisioning.go‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ type ContactPointService interface {
4545

4646
type TemplateService interface {
4747
GetTemplates(ctx context.Context, orgID int64) ([]definitions.NotificationTemplate, error)
48-
GetTemplate(ctx context.Context, orgID int64, name string) (definitions.NotificationTemplate, error)
48+
GetTemplate(ctx context.Context, orgID int64, nameOrUid string) (definitions.NotificationTemplate, error)
4949
UpsertTemplate(ctx context.Context, orgID int64, tmpl definitions.NotificationTemplate) (definitions.NotificationTemplate, error)
50-
DeleteTemplate(ctx context.Context, orgID int64, name string, provenance definitions.Provenance, version string) error
50+
DeleteTemplate(ctx context.Context, orgID int64, nameOrUid string, provenance definitions.Provenance, version string) error
5151
}
5252

5353
type NotificationPolicyService interface {
@@ -229,9 +229,9 @@ func (srv *ProvisioningSrv) RoutePutTemplate(c *contextmodel.ReqContext, body de
229229
return response.JSON(http.StatusAccepted, modified)
230230
}
231231

232-
func (srv *ProvisioningSrv) RouteDeleteTemplate(c *contextmodel.ReqContext, name string) response.Response {
232+
func (srv *ProvisioningSrv) RouteDeleteTemplate(c *contextmodel.ReqContext, nameOrUid string) response.Response {
233233
version := c.Query("version")
234-
err := srv.templates.DeleteTemplate(c.Req.Context(), c.SignedInUser.GetOrgID(), name, determineProvenance(c), version)
234+
err := srv.templates.DeleteTemplate(c.Req.Context(), c.SignedInUser.GetOrgID(), nameOrUid, determineProvenance(c), version)
235235
if err != nil {
236236
return response.ErrOrFallback(http.StatusInternalServerError, "", err)
237237
}

‎pkg/services/ngalert/api/tooling/definitions/provisioning_templates.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type RouteDeleteTemplateParam struct {
5555

5656
// swagger:model
5757
type NotificationTemplate struct {
58+
UID string `json:"-" yaml:"-"`
5859
Name string `json:"name"`
5960
Template string `json:"template"`
6061
Provenance Provenance `json:"provenance,omitempty"`

‎pkg/services/ngalert/provisioning/templates.go‎

Lines changed: 75 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ func (t *TemplateService) GetTemplates(ctx context.Context, orgID int64) ([]defi
5050
templates := make([]definitions.NotificationTemplate, 0, len(revision.Config.TemplateFiles))
5151
for name, tmpl := range revision.Config.TemplateFiles {
5252
tmpl := definitions.NotificationTemplate{
53+
UID: legacy_storage.NameToUid(name),
5354
Name: name,
5455
Template: tmpl,
5556
ResourceVersion: calculateTemplateFingerprint(tmpl),
@@ -65,30 +66,34 @@ func (t *TemplateService) GetTemplates(ctx context.Context, orgID int64) ([]defi
6566
return templates, nil
6667
}
6768

68-
func (t *TemplateService) GetTemplate(ctx context.Context, orgID int64, name string) (definitions.NotificationTemplate, error) {
69+
func (t *TemplateService) GetTemplate(ctx context.Context, orgID int64, nameOrUid string) (definitions.NotificationTemplate, error) {
6970
revision, err := t.configStore.Get(ctx, orgID)
7071
if err != nil {
7172
return definitions.NotificationTemplate{}, err
7273
}
7374

74-
for tmplName, tmpl := range revision.Config.TemplateFiles {
75-
if tmplName != name {
76-
continue
77-
}
78-
tmpl := definitions.NotificationTemplate{
79-
Name: name,
80-
Template: tmpl,
81-
ResourceVersion: calculateTemplateFingerprint(tmpl),
82-
}
75+
existingName := nameOrUid
76+
existingContent, ok := revision.Config.TemplateFiles[nameOrUid]
77+
if !ok {
78+
existingName, existingContent, ok = getTemplateByUid(revision.Config.TemplateFiles, nameOrUid)
79+
}
80+
if !ok {
81+
return definitions.NotificationTemplate{}, ErrTemplateNotFound.Errorf("")
82+
}
8383

84-
provenance, err := t.provenanceStore.GetProvenance(ctx, &tmpl, orgID)
85-
if err != nil {
86-
return definitions.NotificationTemplate{}, err
87-
}
88-
tmpl.Provenance = definitions.Provenance(provenance)
89-
return tmpl, nil
84+
tmpl := definitions.NotificationTemplate{
85+
UID: legacy_storage.NameToUid(existingName),
86+
Name: existingName,
87+
Template: existingContent,
88+
ResourceVersion: calculateTemplateFingerprint(existingContent),
9089
}
91-
return definitions.NotificationTemplate{}, ErrTemplateNotFound.Errorf("")
90+
91+
provenance, err := t.provenanceStore.GetProvenance(ctx, &tmpl, orgID)
92+
if err != nil {
93+
return definitions.NotificationTemplate{}, err
94+
}
95+
tmpl.Provenance = definitions.Provenance(provenance)
96+
return tmpl, nil
9297
}
9398

9499
func (t *TemplateService) UpsertTemplate(ctx context.Context, orgID int64, tmpl definitions.NotificationTemplate) (definitions.NotificationTemplate, error) {
@@ -107,7 +112,11 @@ func (t *TemplateService) UpsertTemplate(ctx context.Context, orgID int64, tmpl
107112
if !errors.Is(err, ErrTemplateNotFound) {
108113
return d, err
109114
}
110-
if tmpl.ResourceVersion != "" { // if version is set then it's an update operation. Fail because resource does not exist anymore
115+
// If template was not found, this is assumed to be a create operation except for two cases:
116+
// - If a ResourceVersion is provided: we should assume that this was meant to be a conditional update operation.
117+
// - If UID is provided: custom UID for templates is not currently supported, so this was meant to be an update
118+
// operation without a ResourceVersion.
119+
if tmpl.ResourceVersion != "" || tmpl.UID != "" {
111120
return definitions.NotificationTemplate{}, ErrTemplateNotFound.Errorf("")
112121
}
113122
return t.createTemplate(ctx, revision, orgID, tmpl)
@@ -150,6 +159,7 @@ func (t *TemplateService) createTemplate(ctx context.Context, revision *legacy_s
150159
}
151160

152161
return definitions.NotificationTemplate{
162+
UID: legacy_storage.NameToUid(tmpl.Name),
153163
Name: tmpl.Name,
154164
Template: tmpl.Template,
155165
Provenance: tmpl.Provenance,
@@ -175,12 +185,28 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
175185
revision.Config.TemplateFiles = map[string]string{}
176186
}
177187

178-
existingName := tmpl.Name
179-
exisitingContent, found := revision.Config.TemplateFiles[existingName]
188+
var found bool
189+
var existingName, existingContent string
190+
// if UID is specified, look by UID.
191+
if tmpl.UID != "" {
192+
existingName, existingContent, found = getTemplateByUid(revision.Config.TemplateFiles, tmpl.UID)
193+
// do not fall back to name because we address by UID, and resource can be deleted\renamed
194+
} else {
195+
existingName = tmpl.Name
196+
existingContent, found = revision.Config.TemplateFiles[existingName]
197+
}
180198
if !found {
181199
return definitions.NotificationTemplate{}, ErrTemplateNotFound.Errorf("")
182200
}
183201

202+
if existingName != tmpl.Name { // if template is renamed, check if this name is already taken
203+
_, ok := revision.Config.TemplateFiles[tmpl.Name]
204+
if ok {
205+
// return error if template is being renamed to one that already exists
206+
return definitions.NotificationTemplate{}, ErrTemplateExists.Errorf("")
207+
}
208+
}
209+
184210
// check that provenance is not changed in an invalid way
185211
storedProvenance, err := t.provenanceStore.GetProvenance(ctx, &tmpl, orgID)
186212
if err != nil {
@@ -190,14 +216,22 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
190216
return definitions.NotificationTemplate{}, err
191217
}
192218

193-
err = t.checkOptimisticConcurrency(tmpl.Name, exisitingContent, models.Provenance(tmpl.Provenance), tmpl.ResourceVersion, "update")
219+
err = t.checkOptimisticConcurrency(tmpl.Name, existingContent, models.Provenance(tmpl.Provenance), tmpl.ResourceVersion, "update")
194220
if err != nil {
195221
return definitions.NotificationTemplate{}, err
196222
}
197223

198224
revision.Config.TemplateFiles[tmpl.Name] = tmpl.Template
199225

200226
err = t.xact.InTransaction(ctx, func(ctx context.Context) error {
227+
if existingName != tmpl.Name { // if template by was found by UID and it's name is different, then this is the rename operation. Delete old resources.
228+
delete(revision.Config.TemplateFiles, existingName)
229+
err := t.provenanceStore.DeleteProvenance(ctx, &definitions.NotificationTemplate{Name: existingName}, orgID)
230+
if err != nil {
231+
return err
232+
}
233+
}
234+
201235
if err := t.configStore.Save(ctx, revision, orgID); err != nil {
202236
return err
203237
}
@@ -208,14 +242,15 @@ func (t *TemplateService) updateTemplate(ctx context.Context, revision *legacy_s
208242
}
209243

210244
return definitions.NotificationTemplate{
245+
UID: legacy_storage.NameToUid(tmpl.Name), // if name was changed, this UID will not match the incoming one
211246
Name: tmpl.Name,
212247
Template: tmpl.Template,
213248
Provenance: tmpl.Provenance,
214249
ResourceVersion: calculateTemplateFingerprint(tmpl.Template),
215250
}, nil
216251
}
217252

218-
func (t *TemplateService) DeleteTemplate(ctx context.Context, orgID int64, name string, provenance definitions.Provenance, version string) error {
253+
func (t *TemplateService) DeleteTemplate(ctx context.Context, orgID int64, nameOrUid string, provenance definitions.Provenance, version string) error {
219254
revision, err := t.configStore.Get(ctx, orgID)
220255
if err != nil {
221256
return err
@@ -225,33 +260,37 @@ func (t *TemplateService) DeleteTemplate(ctx context.Context, orgID int64, name
225260
return nil
226261
}
227262

228-
existing, ok := revision.Config.TemplateFiles[name]
263+
existingName := nameOrUid
264+
existing, ok := revision.Config.TemplateFiles[nameOrUid]
265+
if !ok {
266+
existingName, existing, ok = getTemplateByUid(revision.Config.TemplateFiles, nameOrUid)
267+
}
229268
if !ok {
230269
return nil
231270
}
232271

233-
err = t.checkOptimisticConcurrency(name, existing, models.Provenance(provenance), version, "delete")
272+
err = t.checkOptimisticConcurrency(existingName, existing, models.Provenance(provenance), version, "delete")
234273
if err != nil {
235274
return err
236275
}
237276

238277
// check that provenance is not changed in an invalid way
239-
storedProvenance, err := t.provenanceStore.GetProvenance(ctx, &definitions.NotificationTemplate{Name: name}, orgID)
278+
storedProvenance, err := t.provenanceStore.GetProvenance(ctx, &definitions.NotificationTemplate{Name: existingName}, orgID)
240279
if err != nil {
241280
return err
242281
}
243282
if err = t.validator(storedProvenance, models.Provenance(provenance)); err != nil {
244283
return err
245284
}
246285

247-
delete(revision.Config.TemplateFiles, name)
286+
delete(revision.Config.TemplateFiles, existingName)
248287

249288
return t.xact.InTransaction(ctx, func(ctx context.Context) error {
250289
if err := t.configStore.Save(ctx, revision, orgID); err != nil {
251290
return err
252291
}
253292
tgt := definitions.NotificationTemplate{
254-
Name: name,
293+
Name: existingName,
255294
}
256295
return t.provenanceStore.DeleteProvenance(ctx, &tgt, orgID)
257296
})
@@ -277,3 +316,12 @@ func calculateTemplateFingerprint(t string) string {
277316
_, _ = sum.Write(unsafe.Slice(unsafe.StringData(t), len(t))) //nolint:gosec
278317
return fmt.Sprintf("%016x", sum.Sum64())
279318
}
319+
320+
func getTemplateByUid(templates map[string]string, uid string) (string, string, bool) {
321+
for n, tmpl := range templates {
322+
if legacy_storage.NameToUid(n) == uid {
323+
return n, tmpl, true
324+
}
325+
}
326+
return "", "", false
327+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /