Alerting: Allow administrators delete rules permanently via UI (#101974)

* add query parameter to existing APIs to control the permanent deletion of rules
* add GUID to gettable rule
* add new endpoint /ruler/grafana/api/v1/trash/rule/guid/{RuleGUID} to delete rules from trash permanently

---------

Signed-off-by: Yuri Tseretyan <yuriy.tseretyan@grafana.com>
This commit is contained in:
Yuri Tseretyan
2025-03-14 16:14:06 -04:00
committed by GitHub
parent e30034a42a
commit 309a2eb4e9
25 changed files with 585 additions and 115 deletions
+28 -12
View File
@@ -454,7 +454,7 @@ func (a apiClient) PostConfiguration(t *testing.T, c apimodels.PostableUserConfi
return false, errors.New(data.Message)
}
func (a apiClient) PostRulesGroupWithStatus(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig) (apimodels.UpdateRuleGroupResponse, int, string) {
func (a apiClient) PostRulesGroupWithStatus(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig, permanentlyDelete bool) (apimodels.UpdateRuleGroupResponse, int, string) {
t.Helper()
buf := bytes.Buffer{}
enc := json.NewEncoder(&buf)
@@ -462,6 +462,14 @@ func (a apiClient) PostRulesGroupWithStatus(t *testing.T, folder string, group *
require.NoError(t, err)
u := fmt.Sprintf("%s/api/ruler/grafana/api/v1/rules/%s", a.url, folder)
uri, err := url.Parse(u)
require.NoError(t, err)
q := uri.Query()
if permanentlyDelete {
q.Set("deletePermanently", "true")
}
uri.RawQuery = q.Encode()
u = uri.String()
// nolint:gosec
resp, err := http.Post(u, "application/json", &buf)
require.NoError(t, err)
@@ -477,9 +485,9 @@ func (a apiClient) PostRulesGroupWithStatus(t *testing.T, folder string, group *
return m, resp.StatusCode, string(b)
}
func (a apiClient) PostRulesGroup(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig) apimodels.UpdateRuleGroupResponse {
func (a apiClient) PostRulesGroup(t *testing.T, folder string, group *apimodels.PostableRuleGroupConfig, permanentlyDelete bool) apimodels.UpdateRuleGroupResponse {
t.Helper()
m, status, raw := a.PostRulesGroupWithStatus(t, folder, group)
m, status, raw := a.PostRulesGroupWithStatus(t, folder, group, permanentlyDelete)
requireStatusCode(t, http.StatusAccepted, status, raw)
return m
}
@@ -520,22 +528,21 @@ func (a apiClient) PostRulesExportWithStatus(t *testing.T, folder string, group
return resp.StatusCode, string(b)
}
func (a apiClient) DeleteRulesGroup(t *testing.T, folder string, group string) (int, string) {
func (a apiClient) DeleteRulesGroup(t *testing.T, folder string, group string, permanently bool) (int, string) {
t.Helper()
u := fmt.Sprintf("%s/api/ruler/grafana/api/v1/rules/%s/%s", a.url, folder, group)
req, err := http.NewRequest(http.MethodDelete, u, nil)
require.NoError(t, err)
client := &http.Client{}
resp, err := client.Do(req)
require.NoError(t, err)
defer func() {
_ = resp.Body.Close()
}()
b, err := io.ReadAll(resp.Body)
if permanently {
req.URL.RawQuery = url.Values{"deletePermanently": []string{"true"}}.Encode()
}
resp, status, err := sendRequestRaw(t, req)
require.NoError(t, err)
return resp.StatusCode, string(b)
return status, string(resp)
}
func (a apiClient) PostSilence(t *testing.T, s apimodels.PostableSilence) (apimodels.PostSilencesOKBody, int, string) {
@@ -657,6 +664,15 @@ func (a apiClient) GetDeletedRulesWithStatus(t *testing.T) (apimodels.NamespaceC
return sendRequestJSON[apimodels.NamespaceConfigResponse](t, req, http.StatusOK)
}
func (a apiClient) DeleteRuleFromTrashByGUID(t *testing.T, ruleGUID string) (int, string) {
t.Helper()
req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/ruler/grafana/api/v1/trash/rule/guid/%s", a.url, ruleGUID), nil)
require.NoError(t, err)
raw, status, err := sendRequestRaw(t, req)
require.NoError(t, err)
return status, string(raw)
}
func (a apiClient) ExportRulesWithStatus(t *testing.T, params *apimodels.AlertRulesExportParameters) (int, string) {
t.Helper()
u, err := url.Parse(fmt.Sprintf("%s/api/ruler/grafana/api/v1/export/rules", a.url))