From 25da759bf22b005dc716927f8bb52f7f07e85d35 Mon Sep 17 00:00:00 2001
From: Alexander Weaver <weaver.alex.d@gmail.com>
Date: Wed, 18 May 2022 13:52:30 -0500
Subject: [PATCH] Indicate whether templates are provisioned (#49025)

---
 .../ngalert/api/api_alertmanager_test.go      | 52 ++++++++++++++++++-
 .../api/tooling/definitions/alertmanager.go   |  5 +-
 .../ngalert/notifier/alertmanager_config.go   | 25 ++++++---
 3 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/pkg/services/ngalert/api/api_alertmanager_test.go b/pkg/services/ngalert/api/api_alertmanager_test.go
index 51f36db2e40..304ce1e0ffd 100644
--- a/pkg/services/ngalert/api/api_alertmanager_test.go
+++ b/pkg/services/ngalert/api/api_alertmanager_test.go
@@ -232,6 +232,15 @@ func TestAlertmanagerConfig(t *testing.T) {
 			body := asGettableUserConfig(t, response)
 			require.Equal(t, ngmodels.ProvenanceNone, body.AlertmanagerConfig.Receivers[0].GrafanaManagedReceivers[0].Provenance)
 		})
+		t.Run("templates from GET config have no provenance", func(t *testing.T) {
+			sut := createSut(t, nil)
+			rc := createRequestCtxInOrg(1)
+
+			response := sut.RouteGetAlertingConfig(rc)
+
+			body := asGettableUserConfig(t, response)
+			require.Nil(t, body.TemplateFileProvenances)
+		})
 	})
 
 	t.Run("when objects are provisioned", func(t *testing.T) {
@@ -265,6 +274,18 @@ func TestAlertmanagerConfig(t *testing.T) {
 
 			require.Equal(t, ngmodels.ProvenanceAPI, body.AlertmanagerConfig.Receivers[0].GrafanaManagedReceivers[0].Provenance)
 		})
+		t.Run("templates from GET config have expected provenance", func(t *testing.T) {
+			sut := createSut(t, nil)
+			rc := createRequestCtxInOrg(1)
+			setTemplateProvenance(t, 1, "a", sut.mam.ProvStore)
+
+			response := sut.RouteGetAlertingConfig(rc)
+
+			body := asGettableUserConfig(t, response)
+			require.NotNil(t, body.TemplateFileProvenances)
+			require.Len(t, body.TemplateFileProvenances, 1)
+			require.Equal(t, ngmodels.ProvenanceAPI, body.TemplateFileProvenances["a"])
+		})
 	})
 }
 
@@ -522,7 +543,29 @@ func createMultiOrgAlertmanager(t *testing.T) *notifier.MultiOrgAlertmanager {
 	return mam
 }
 
-var validConfig = setting.GetAlertmanagerDefaultConfiguration()
+var validConfig = `{
+	"template_files": {
+		"a": "template"
+	},
+	"alertmanager_config": {
+		"route": {
+			"receiver": "grafana-default-email"
+		},
+		"receivers": [{
+			"name": "grafana-default-email",
+			"grafana_managed_receiver_configs": [{
+				"uid": "",
+				"name": "email receiver",
+				"type": "email",
+				"isDefault": true,
+				"settings": {
+					"addresses": "<example@email.com>"
+				}
+			}]
+		}]
+	}
+}
+`
 
 var brokenConfig = `
 	"alertmanager_config": {
@@ -605,6 +648,13 @@ func setContactPointProvenance(t *testing.T, orgID int64, UID string, ps provisi
 	require.NoError(t, err)
 }
 
+// setTemplateProvenance marks a template as provisioned.
+func setTemplateProvenance(t *testing.T, orgID int64, name string, ps provisioning.ProvisioningStore) {
+	t.Helper()
+	err := ps.SetProvenance(context.Background(), &apimodels.MessageTemplate{Name: name}, orgID, ngmodels.ProvenanceAPI)
+	require.NoError(t, err)
+}
+
 func asGettableUserConfig(t *testing.T, r response.Response) *apimodels.GettableUserConfig {
 	t.Helper()
 	body := &apimodels.GettableUserConfig{}
diff --git a/pkg/services/ngalert/api/tooling/definitions/alertmanager.go b/pkg/services/ngalert/api/tooling/definitions/alertmanager.go
index bdf18d2f7d3..7b525a07478 100644
--- a/pkg/services/ngalert/api/tooling/definitions/alertmanager.go
+++ b/pkg/services/ngalert/api/tooling/definitions/alertmanager.go
@@ -561,8 +561,9 @@ func (c *PostableUserConfig) UnmarshalYAML(value *yaml.Node) error {
 
 // swagger:model
 type GettableUserConfig struct {
-	TemplateFiles      map[string]string         `yaml:"template_files" json:"template_files"`
-	AlertmanagerConfig GettableApiAlertingConfig `yaml:"alertmanager_config" json:"alertmanager_config"`
+	TemplateFiles           map[string]string            `yaml:"template_files" json:"template_files"`
+	TemplateFileProvenances map[string]models.Provenance `yaml:"template_file_provenances,omitempty" json:"template_file_provenances,omitempty"`
+	AlertmanagerConfig      GettableApiAlertingConfig    `yaml:"alertmanager_config" json:"alertmanager_config"`
 
 	// amSimple stores a map[string]interface of the decoded alertmanager config.
 	// This enables circumventing the underlying alertmanager secret type
diff --git a/pkg/services/ngalert/notifier/alertmanager_config.go b/pkg/services/ngalert/notifier/alertmanager_config.go
index a39beea1f38..1594ff75acf 100644
--- a/pkg/services/ngalert/notifier/alertmanager_config.go
+++ b/pkg/services/ngalert/notifier/alertmanager_config.go
@@ -83,7 +83,7 @@ func (moa *MultiOrgAlertmanager) GetAlertmanagerConfiguration(ctx context.Contex
 	}
 
 	if moa.settings.IsFeatureToggleEnabled(featuremgmt.FlagAlertProvisioning) {
-		result.AlertmanagerConfig, err = moa.mergeProvenance(ctx, result.AlertmanagerConfig, org)
+		result, err = moa.mergeProvenance(ctx, result, org)
 		if err != nil {
 			return definitions.GettableUserConfig{}, err
 		}
@@ -126,25 +126,34 @@ func (moa *MultiOrgAlertmanager) ApplyAlertmanagerConfiguration(ctx context.Cont
 	return nil
 }
 
-func (moa *MultiOrgAlertmanager) mergeProvenance(ctx context.Context, config definitions.GettableApiAlertingConfig, org int64) (definitions.GettableApiAlertingConfig, error) {
-	if config.Route != nil {
-		provenance, err := moa.ProvStore.GetProvenance(ctx, config.Route, org)
+func (moa *MultiOrgAlertmanager) mergeProvenance(ctx context.Context, config definitions.GettableUserConfig, org int64) (definitions.GettableUserConfig, error) {
+	if config.AlertmanagerConfig.Route != nil {
+		provenance, err := moa.ProvStore.GetProvenance(ctx, config.AlertmanagerConfig.Route, org)
 		if err != nil {
-			return definitions.GettableApiAlertingConfig{}, err
+			return definitions.GettableUserConfig{}, err
 		}
-		config.Route.Provenance = provenance
+		config.AlertmanagerConfig.Route.Provenance = provenance
 	}
+
 	cp := definitions.EmbeddedContactPoint{}
 	cpProvs, err := moa.ProvStore.GetProvenances(ctx, org, cp.ResourceType())
 	if err != nil {
-		return definitions.GettableApiAlertingConfig{}, err
+		return definitions.GettableUserConfig{}, err
 	}
-	for _, receiver := range config.Receivers {
+	for _, receiver := range config.AlertmanagerConfig.Receivers {
 		for _, contactPoint := range receiver.GrafanaManagedReceivers {
 			if provenance, exists := cpProvs[contactPoint.UID]; exists {
 				contactPoint.Provenance = provenance
 			}
 		}
 	}
+
+	tmpl := definitions.MessageTemplate{}
+	tmplProvs, err := moa.ProvStore.GetProvenances(ctx, org, tmpl.ResourceType())
+	if err != nil {
+		return definitions.GettableUserConfig{}, nil
+	}
+	config.TemplateFileProvenances = tmplProvs
+
 	return config, nil
 }