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:
		@@ -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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user