mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(alerting): add support for uploading images to webdav.
closes #5770
This commit is contained in:
parent
8878a55dc6
commit
99e99922b4
@ -383,8 +383,17 @@ interval_seconds = 60
|
||||
[grafana_net]
|
||||
url = https://grafana.net
|
||||
|
||||
#################################### S3 Temp Store ##########################
|
||||
[s3-temp-image-store]
|
||||
#################################### External image storage ##########################
|
||||
[external_image_storage]
|
||||
# You can choose between (s3, webdav or internal)
|
||||
provider = s3
|
||||
|
||||
[external_image_storage.s3]
|
||||
bucket_url =
|
||||
access_key =
|
||||
secret_key =
|
||||
|
||||
[external_image_storage.webdav]
|
||||
url =
|
||||
username =
|
||||
password =
|
||||
|
@ -312,3 +312,19 @@ enabled = true
|
||||
# Url used to to import dashboards directly from Grafana.net
|
||||
[grafana_net]
|
||||
url = https://grafana.net
|
||||
|
||||
#################################### External image storage ##########################
|
||||
[external_image_storage]
|
||||
# Used for uploading images to public servers so they can be included in slack/email messages.
|
||||
# you can choose between (s3, webdav or internal)
|
||||
provider = s3
|
||||
|
||||
[external_image_storage.s3]
|
||||
bucket_url =
|
||||
access_key =
|
||||
secret_key =
|
||||
|
||||
[external_image_storage.webdav]
|
||||
url =
|
||||
username =
|
||||
password =
|
||||
|
@ -1,57 +1,57 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/kr/s3/s3util"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type Uploader interface {
|
||||
type ImageUploader interface {
|
||||
Upload(path string) (string, error)
|
||||
}
|
||||
|
||||
type S3Uploader struct {
|
||||
bucket string
|
||||
secretKey string
|
||||
accessKey string
|
||||
}
|
||||
|
||||
func NewS3Uploader(bucket, accessKey, secretKey string) *S3Uploader {
|
||||
return &S3Uploader{
|
||||
bucket: bucket,
|
||||
accessKey: accessKey,
|
||||
secretKey: secretKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *S3Uploader) Upload(path string) (string, error) {
|
||||
|
||||
s3util.DefaultConfig.AccessKey = u.accessKey
|
||||
s3util.DefaultConfig.SecretKey = u.secretKey
|
||||
|
||||
header := make(http.Header)
|
||||
header.Add("x-amz-acl", "public-read")
|
||||
header.Add("Content-Type", "image/png")
|
||||
|
||||
fullUrl := u.bucket + util.GetRandomString(20) + ".png"
|
||||
writer, err := s3util.Create(fullUrl, header, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer writer.Close()
|
||||
|
||||
imgData, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = writer.Write(imgData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fullUrl, nil
|
||||
func NewImageUploader() (ImageUploader, error) {
|
||||
|
||||
switch setting.ImageUploadProvider {
|
||||
case "s3":
|
||||
s3sec, err := setting.Cfg.GetSection("external_image_storage.s3")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bucket := s3sec.Key("secret_key").String()
|
||||
accessKey := s3sec.Key("access_key").String()
|
||||
secretKey := s3sec.Key("secret_key").String()
|
||||
|
||||
if bucket == "" {
|
||||
return nil, fmt.Errorf("Could not find bucket setting for image.uploader.s3")
|
||||
}
|
||||
|
||||
if accessKey == "" {
|
||||
return nil, fmt.Errorf("Could not find accessKey setting for image.uploader.s3")
|
||||
}
|
||||
|
||||
if secretKey == "" {
|
||||
return nil, fmt.Errorf("Could not find secretKey setting for image.uploader.s3")
|
||||
}
|
||||
|
||||
return NewS3Uploader(bucket, accessKey, secretKey), nil
|
||||
case "webdav":
|
||||
webdavSec, err := setting.Cfg.GetSection("external_image_storage.webdav")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := webdavSec.Key("url").String()
|
||||
if url == "" {
|
||||
return nil, fmt.Errorf("Could not find url key for image.uploader.webdav")
|
||||
}
|
||||
|
||||
username := webdavSec.Key("username").String()
|
||||
password := webdavSec.Key("password").String()
|
||||
|
||||
return NewWebdavImageUploader(url, username, password)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("could not find specified provider")
|
||||
}
|
||||
|
53
pkg/components/imguploader/imguploader_test.go
Normal file
53
pkg/components/imguploader/imguploader_test.go
Normal file
@ -0,0 +1,53 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestImageUploaderFactory(t *testing.T) {
|
||||
Convey("Can create image uploader for ", t, func() {
|
||||
Convey("S3ImageUploader", func() {
|
||||
var err error
|
||||
setting.NewConfigContext(&setting.CommandLineArgs{
|
||||
HomePath: "../../../",
|
||||
})
|
||||
|
||||
setting.ImageUploadProvider = "s3"
|
||||
|
||||
s3sec, err := setting.Cfg.GetSection("external_image_storage.s3")
|
||||
s3sec.NewKey("bucket_url", "bucket_url")
|
||||
s3sec.NewKey("access_key", "access_key")
|
||||
s3sec.NewKey("secret_key", "secret_key")
|
||||
|
||||
uploader, err := NewImageUploader()
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(reflect.TypeOf(uploader), ShouldEqual, reflect.TypeOf(&S3Uploader{}))
|
||||
})
|
||||
|
||||
Convey("Webdav uploader", func() {
|
||||
var err error
|
||||
|
||||
setting.NewConfigContext(&setting.CommandLineArgs{
|
||||
HomePath: "../../../",
|
||||
})
|
||||
|
||||
setting.ImageUploadProvider = "webdav"
|
||||
|
||||
webdavSec, err := setting.Cfg.GetSection("external_image_storage.webdav")
|
||||
webdavSec.NewKey("url", "webdavUrl")
|
||||
webdavSec.NewKey("username", "username")
|
||||
webdavSec.NewKey("password", "password")
|
||||
|
||||
uploader, err := NewImageUploader()
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(reflect.TypeOf(uploader), ShouldEqual, reflect.TypeOf(&WebdavUploader{}))
|
||||
})
|
||||
})
|
||||
}
|
53
pkg/components/imguploader/s3uploader.go
Normal file
53
pkg/components/imguploader/s3uploader.go
Normal file
@ -0,0 +1,53 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/kr/s3/s3util"
|
||||
)
|
||||
|
||||
type S3Uploader struct {
|
||||
bucket string
|
||||
secretKey string
|
||||
accessKey string
|
||||
}
|
||||
|
||||
func NewS3Uploader(bucket, accessKey, secretKey string) *S3Uploader {
|
||||
return &S3Uploader{
|
||||
bucket: bucket,
|
||||
accessKey: accessKey,
|
||||
secretKey: secretKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (u *S3Uploader) Upload(path string) (string, error) {
|
||||
|
||||
s3util.DefaultConfig.AccessKey = u.accessKey
|
||||
s3util.DefaultConfig.SecretKey = u.secretKey
|
||||
|
||||
header := make(http.Header)
|
||||
header.Add("x-amz-acl", "public-read")
|
||||
header.Add("Content-Type", "image/png")
|
||||
|
||||
fullUrl := u.bucket + util.GetRandomString(20) + ".png"
|
||||
writer, err := s3util.Create(fullUrl, header, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer writer.Close()
|
||||
|
||||
imgData, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = writer.Write(imgData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fullUrl, nil
|
||||
}
|
51
pkg/components/imguploader/webdavuploader.go
Normal file
51
pkg/components/imguploader/webdavuploader.go
Normal file
@ -0,0 +1,51 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
type WebdavUploader struct {
|
||||
url string
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
func (u *WebdavUploader) Upload(pa string) (string, error) {
|
||||
log.Error2("Hej")
|
||||
client := http.Client{Timeout: time.Duration(10 * time.Second)}
|
||||
|
||||
url, _ := url.Parse(u.url)
|
||||
url.Path = path.Join(url.Path, util.GetRandomString(20)+".png")
|
||||
|
||||
imgData, err := ioutil.ReadFile(pa)
|
||||
req, err := http.NewRequest("PUT", url.String(), bytes.NewReader(imgData))
|
||||
res, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusCreated {
|
||||
body, _ := ioutil.ReadAll(res.Body)
|
||||
return "", fmt.Errorf("Failed to upload image. Returned statuscode %v body %s", res.StatusCode, body)
|
||||
}
|
||||
|
||||
return url.String(), nil
|
||||
}
|
||||
|
||||
func NewWebdavImageUploader(url, username, passwrod string) (*WebdavUploader, error) {
|
||||
return &WebdavUploader{
|
||||
url: url,
|
||||
username: username,
|
||||
password: passwrod,
|
||||
}, nil
|
||||
}
|
18
pkg/components/imguploader/webdavuploader_test.go
Normal file
18
pkg/components/imguploader/webdavuploader_test.go
Normal file
@ -0,0 +1,18 @@
|
||||
package imguploader
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestUploadToWebdav(t *testing.T) {
|
||||
webdavUploader, _ := NewWebdavImageUploader("http://localhost:9998/dav/", "username", "password")
|
||||
|
||||
SkipConvey("[Integration test] for external_image_store.webdav", t, func() {
|
||||
path, err := webdavUploader.Upload("../../../public/img/logo_transparent_400x.png")
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(path, ShouldNotEqual, "")
|
||||
})
|
||||
}
|
@ -8,7 +8,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/components/renderer"
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
m "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type RootNotifier struct {
|
||||
@ -54,10 +53,8 @@ func (n *RootNotifier) Notify(context *EvalContext) {
|
||||
}
|
||||
|
||||
func (n *RootNotifier) uploadImage(context *EvalContext) error {
|
||||
uploader := imguploader.NewS3Uploader(
|
||||
setting.S3TempImageStoreBucketUrl,
|
||||
setting.S3TempImageStoreAccessKey,
|
||||
setting.S3TempImageStoreSecretKey)
|
||||
|
||||
uploader, _ := imguploader.NewImageUploader()
|
||||
|
||||
imageUrl, err := context.GetImageUrl()
|
||||
if err != nil {
|
||||
|
@ -153,6 +153,8 @@ var (
|
||||
S3TempImageStoreBucketUrl string
|
||||
S3TempImageStoreAccessKey string
|
||||
S3TempImageStoreSecretKey string
|
||||
|
||||
ImageUploadProvider string
|
||||
)
|
||||
|
||||
type CommandLineArgs struct {
|
||||
@ -539,10 +541,8 @@ func NewConfigContext(args *CommandLineArgs) error {
|
||||
|
||||
GrafanaNetUrl = Cfg.Section("grafana.net").Key("url").MustString("https://grafana.net")
|
||||
|
||||
s3temp := Cfg.Section("s3-temp-image-store")
|
||||
S3TempImageStoreBucketUrl = s3temp.Key("bucket_url").String()
|
||||
S3TempImageStoreAccessKey = s3temp.Key("access_key").String()
|
||||
S3TempImageStoreSecretKey = s3temp.Key("secret_key").String()
|
||||
imageUploadingSection := Cfg.Section("external_image_storage")
|
||||
ImageUploadProvider = imageUploadingSection.Key("provider").MustString("internal")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user