mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Add stored screenshot utilities to the channels package. (#49470)
Adds three functions: `withStoredImages` iterates over a list of models.Alerts, extracting a stored image's data from storage, if available, and executing a user-provided function. `withStoredImage` does this for an image attached to a specific alert. `openImage` finds and opens an image file on disk. Moves `store.Image` to `models.Image` Simplifies `channels.ImageStore` interface and updates notifiers that use it to use the simpler methods. Updates all pkg/alert/notifier/channels to use withStoredImage routines.
This commit is contained in:
@@ -2,61 +2,32 @@ package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrImageNotFound is returned when the image does not exist.
|
||||
ErrImageNotFound = errors.New("image not found")
|
||||
)
|
||||
|
||||
type Image struct {
|
||||
ID int64 `xorm:"pk autoincr 'id'"`
|
||||
Token string `xorm:"token"`
|
||||
Path string `xorm:"path"`
|
||||
URL string `xorm:"url"`
|
||||
CreatedAt time.Time `xorm:"created_at"`
|
||||
ExpiresAt time.Time `xorm:"expires_at"`
|
||||
}
|
||||
|
||||
// A XORM interface that lets us clean up our SQL session definition.
|
||||
func (i *Image) TableName() string {
|
||||
return "alert_image"
|
||||
}
|
||||
|
||||
type ImageStore interface {
|
||||
// Get returns the image with the token or ErrImageNotFound.
|
||||
GetImage(ctx context.Context, token string) (*Image, error)
|
||||
GetImage(ctx context.Context, token string) (*models.Image, error)
|
||||
|
||||
// Saves the image or returns an error.
|
||||
SaveImage(ctx context.Context, img *Image) error
|
||||
|
||||
GetURL(ctx context.Context, token string) (string, error)
|
||||
|
||||
GetFilepath(ctx context.Context, token string) (string, error)
|
||||
|
||||
// Returns an io.ReadCloser that reads out the image data for the provided
|
||||
// token, if available. May return ErrImageNotFound.
|
||||
GetData(ctx context.Context, token string) (io.ReadCloser, error)
|
||||
SaveImage(ctx context.Context, img *models.Image) error
|
||||
}
|
||||
|
||||
func (st DBstore) GetImage(ctx context.Context, token string) (*Image, error) {
|
||||
var img Image
|
||||
func (st DBstore) GetImage(ctx context.Context, token string) (*models.Image, error) {
|
||||
var img models.Image
|
||||
if err := st.SQLStore.WithDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
exists, err := sess.Where("token = ?", token).Get(&img)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get image: %w", err)
|
||||
}
|
||||
if !exists {
|
||||
return ErrImageNotFound
|
||||
return models.ErrImageNotFound
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
@@ -65,7 +36,7 @@ func (st DBstore) GetImage(ctx context.Context, token string) (*Image, error) {
|
||||
return &img, nil
|
||||
}
|
||||
|
||||
func (st DBstore) SaveImage(ctx context.Context, img *Image) error {
|
||||
func (st DBstore) SaveImage(ctx context.Context, img *models.Image) error {
|
||||
return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
// TODO: Is this a good idea? Do we actually want to automatically expire
|
||||
// rows? See issue https://github.com/grafana/grafana/issues/49366
|
||||
@@ -93,47 +64,10 @@ func (st DBstore) SaveImage(ctx context.Context, img *Image) error {
|
||||
})
|
||||
}
|
||||
|
||||
func (st *DBstore) GetURL(ctx context.Context, token string) (string, error) {
|
||||
img, err := st.GetImage(ctx, token)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return img.URL, nil
|
||||
}
|
||||
|
||||
func (st *DBstore) GetFilepath(ctx context.Context, token string) (string, error) {
|
||||
img, err := st.GetImage(ctx, token)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return img.Path, nil
|
||||
}
|
||||
|
||||
func (st *DBstore) GetData(ctx context.Context, token string) (io.ReadCloser, error) {
|
||||
// TODO: Should we support getting data from image.URL? One could configure
|
||||
// the system to upload to S3 while still reading data for notifiers like
|
||||
// Slack that take multipart uploads.
|
||||
img, err := st.GetImage(ctx, token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(img.Path) == 0 {
|
||||
return nil, ErrImageNotFound
|
||||
}
|
||||
|
||||
f, err := os.Open(img.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
//nolint:unused
|
||||
func (st DBstore) DeleteExpiredImages(ctx context.Context) error {
|
||||
return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
|
||||
n, err := sess.Where("expires_at < ?", TimeNow()).Delete(&Image{})
|
||||
n, err := sess.Where("expires_at < ?", TimeNow()).Delete(&models.Image{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete expired images: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user