The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/ngalert/store/provisioning_store.go

107 lines
3.7 KiB

package store
import (
"context"
"fmt"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
)
type provenanceRecord struct {
Id int `xorm:"pk autoincr 'id'"`
OrgID int64 `xorm:"'org_id'"`
RecordKey string
RecordType string
Provenance models.Provenance
}
func (pr provenanceRecord) TableName() string {
return "provenance_type"
}
// GetProvenance gets the provenance status for a provisionable object.
func (st DBstore) GetProvenance(ctx context.Context, o models.Provisionable) (models.Provenance, error) {
recordType := o.ResourceType()
recordKey := o.ResourceID()
orgID := o.ResourceOrgID()
provenance := models.ProvenanceNone
err := st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
filter := "record_key = ? AND record_type = ? AND org_id = ?"
var result models.Provenance
has, err := sess.Table(provenanceRecord{}).Where(filter, recordKey, recordType, orgID).Desc("id").Cols("provenance").Get(&result)
if err != nil {
return fmt.Errorf("failed to query for existing provenance status: %w", err)
}
if has {
provenance = result
}
return nil
})
if err != nil {
return models.ProvenanceNone, err
}
return provenance, nil
}
// GetProvenance gets the provenance status for a provisionable object.
func (st DBstore) GetProvenances(ctx context.Context, orgID int64, resourceType string) (map[string]models.Provenance, error) {
resultMap := make(map[string]models.Provenance)
err := st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
filter := "record_type = ? AND org_id = ?"
rawData, err := sess.Table(provenanceRecord{}).Where(filter, resourceType, orgID).Desc("id").Cols("record_key", "provenance").QueryString()
if err != nil {
return fmt.Errorf("failed to query for existing provenance status: %w", err)
}
for _, data := range rawData {
resultMap[data["record_key"]] = models.Provenance(data["provenance"])
}
return nil
})
return resultMap, err
}
// SetProvenance changes the provenance status for a provisionable object.
func (st DBstore) SetProvenance(ctx context.Context, o models.Provisionable, p models.Provenance) error {
recordType := o.ResourceType()
recordKey := o.ResourceID()
orgID := o.ResourceOrgID()
return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
// TODO: Add a unit-of-work pattern, so updating objects + provenance will happen consistently with rollbacks across stores.
// TODO: Need to make sure that writing a record where our concurrency key fails will also fail the whole transaction. That way, this gets rolled back too. can't just check that 0 updates happened inmemory. Check with jp. If not possible, we need our own concurrency key.
// TODO: Clean up stale provenance records periodically.
filter := "record_key = ? AND record_type = ? AND org_id = ?"
_, err := sess.Table(provenanceRecord{}).Where(filter, recordKey, recordType, orgID).Delete(provenanceRecord{})
if err != nil {
return fmt.Errorf("failed to delete pre-existing provisioning status: %w", err)
}
record := provenanceRecord{
RecordKey: recordKey,
RecordType: recordType,
Provenance: p,
OrgID: orgID,
}
if _, err := sess.Insert(record); err != nil {
return fmt.Errorf("failed to store provisioning status: %w", err)
}
return nil
})
}
// DeleteProvenance deletes the provenance record from the table
func (st DBstore) DeleteProvenance(ctx context.Context, o models.Provisionable) error {
return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
_, err := sess.Delete(provenanceRecord{
RecordKey: o.ResourceID(),
RecordType: o.ResourceType(),
OrgID: o.ResourceOrgID(),
})
return err
})
}