diff --git a/adapter.go b/adapter.go index 2c6a833..cbe5633 100644 --- a/adapter.go +++ b/adapter.go @@ -17,3 +17,4 @@ type Adapter interface { } var _ Adapter = &GCSAdapter{} +var _ Adapter = &AliyunAdapter{} diff --git a/aliyun_adapter.go b/aliyun_adapter.go new file mode 100644 index 0000000..3e3f5dc --- /dev/null +++ b/aliyun_adapter.go @@ -0,0 +1,196 @@ +package storage + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "mime/multipart" + "net/http" + "strings" + "time" + + oss "github.com/aliyun/aliyun-oss-go-sdk/oss" +) + +// AliyunAdapter : The adapter empty because they do not have any data to share for now +type AliyunAdapter struct { + Endpoint string + AccessKeyID string + AccessKeySecret string +} + +// UploadFile : Upload file to the bucket +func (adapter *AliyunAdapter) UploadFile(file *multipart.FileHeader, bucket, filename string) (string, error) { + var reader io.Reader + name := "" + + fileExt := strings.ToLower(strings.Split(file.Header.Get("Content-Type"), "/")[1]) + + if ext, isExist := extensionMapper[fileExt]; isExist { + name = fmt.Sprintf("%s.%s", filename, ext) + } else { + name = fmt.Sprintf("%s.%s", filename, fileExt) + } + + src, err := file.Open() + if err != nil { + return "", err + } + + defer src.Close() + reader = src + + fileURL, err := adapter.UploadReader(bucket, name, reader, strings.ToLower(fileExt)) + if err != nil { + return "", err + } + + return fileURL, nil +} + +// DeleteFileUsingURL : Delete file from the bucket using url +func (adapter *AliyunAdapter) DeleteFileUsingURL(bucket, fileURL string) error { + storageClient, err := adapter.getClient() + if err != nil { + return err + } + + object, err := storageClient.Bucket(bucket) + if err != nil { + return err + } + + filepath := adapter.getFilePathFromURL(bucket, fileURL) + + return object.DeleteObject(filepath) +} + +// TemporaryServingFile : TemporaryServingFile file serving +func (adapter *AliyunAdapter) TemporaryServingFile(bucket, fileURL string, expiredDateTime time.Time, aliClient interface{}) (string, error) { + storageClient, err := adapter.getClient() + if err != nil { + return "", err + } + + object, err := storageClient.Bucket(bucket) + if err != nil { + return "", err + } + + filepath := adapter.getFilePathFromURL(bucket, fileURL) + + url, err := object.SignURL(filepath, http.MethodGet, int64(expiredDateTime.UTC().Sub(time.Now().UTC()).Seconds())) + if err != nil { + return "", err + } + + return url, nil +} + +// UploadReader : +func (adapter *AliyunAdapter) UploadReader(bucket, filename string, reader io.Reader, contentType string) (string, error) { + storageClient, err := adapter.getClient() + if err != nil { + return "", err + } + + object, err := storageClient.Bucket(bucket) + if err != nil { + return "", err + } + + options := make([]oss.Option, 0) + if contentType == "" { + options = aliyunContentTypeAny(filename) + } else { + contentFunc, isExist := aliyunContentTypeMapper[contentType] + if !isExist { + return "", fmt.Errorf("Content type %s does not supported", contentType) + } + options = contentFunc(filename) + } + + if err := object.PutObject(filename, reader, options...); err != nil { + msg := fmt.Sprintf("Could not write file: %v", err) + return "", errors.New(msg) + } + + return getAliyunFileURL(adapter.Endpoint, bucket, filename), nil +} + +// ReadFile : +func (adapter *AliyunAdapter) ReadFile(bucket, path string) ([]byte, error) { + storageClient, err := adapter.getClient() + if err != nil { + return nil, err + } + + object, err := storageClient.Bucket(bucket) + if err != nil { + return nil, err + } + + rc, err := object.GetObject(path) + defer rc.Close() + slurp, err := ioutil.ReadAll(rc) + if err != nil { + return nil, err + } + return slurp, nil +} + +// UploadBuffer : +func (adapter *AliyunAdapter) UploadBuffer(bucket, filename string, contentType string) (*Buffer, error) { + buf := new(Buffer) + buf.adapter = ALIYUN + + storageClient, err := adapter.getClient() + if err != nil { + return nil, err + } + + object, err := storageClient.Bucket(bucket) + if err != nil { + return nil, err + } + + contentFunc, isExist := aliyunContentTypeMapper[contentType] + if !isExist { + return nil, fmt.Errorf("Content type %s does not supported", contentType) + } + options := contentFunc(filename) + + buffer := new(bytes.Buffer) + + // delete the object before append + object.DeleteObject(filename) + + position, err := object.AppendObject(filename, buffer, buf.position, options...) + if err != nil { + log.Println("error: ", err) + return nil, err + } + buf.position = position + + buf.position = position + buf.object = object + buf.filename = filename + buf.bucket = bucket + buf.endpoint = adapter.Endpoint + return buf, nil +} + +func (adapter *AliyunAdapter) getClient() (*oss.Client, error) { + return oss.New(adapter.Endpoint, adapter.AccessKeyID, adapter.AccessKeySecret) +} + +func getAliyunFileURL(endpoint, bucket string, filename string) string { + return fmt.Sprintf("https://%s.%s/%s", bucket, endpoint, filename) +} + +func (adapter *AliyunAdapter) getFilePathFromURL(bucket, fileURL string) string { + return strings.Replace(fileURL, fmt.Sprintf("https://%s.%s/", bucket, adapter.Endpoint), "", -1) +} diff --git a/aliyun_content_type.go b/aliyun_content_type.go new file mode 100644 index 0000000..39cfcda --- /dev/null +++ b/aliyun_content_type.go @@ -0,0 +1,111 @@ +package storage + +import ( + "fmt" + "strings" + + "github.com/aliyun/aliyun-oss-go-sdk/oss" +) + +var aliyunContentTypeMapper = map[string]func(filename string) []oss.Option{ + ContentTypeCSV: aliyunContentTypeCSV, + ContentTypePNG: aliyunContentTypePNG, + ContentTypeJPEG: aliyunContentTypeJPEG, + ContentTypePDF: aliyunContentTypePDF, + ContentTypeZip: aliyunContentTypeZip, + ContentTypeAPK: aliyunContentTypeApk, + ContentTypeHTML: aliyunContentTypeHTML, + ContentTypeCSS: aliyunContentTypeCSS, + ContentTypeJS: aliyunContentTypeJS, + ContentTypeExcel: aliyunContentTypeExcel, +} + +func aliyunContentTypeZip(filename string) []oss.Option { + options := make([]oss.Option, 0) + options = append(options, oss.ContentType("application/zip")) + options = append(options, oss.ContentDisposition(fmt.Sprintf("attachment;filename=%s", filename))) + + return options +} + +func aliyunContentTypeCSV(filename string) []oss.Option { + options := make([]oss.Option, 0) + options = append(options, oss.ContentType("text/csv")) + options = append(options, oss.ContentDisposition(fmt.Sprintf("attachment;filename=%s", filename))) + + return options +} + +func aliyunContentTypePNG(filename string) []oss.Option { + options := make([]oss.Option, 0) + options = append(options, oss.ContentType("image/png")) + + return options +} + +func aliyunContentTypeJPEG(filename string) []oss.Option { + options := make([]oss.Option, 0) + options = append(options, oss.ContentType("image/jpeg")) + + return options +} + +func aliyunContentTypePDF(filename string) []oss.Option { + options := make([]oss.Option, 0) + options = append(options, oss.ContentType("application/pdf")) + + return options +} + +func aliyunContentTypeAny(filename string) []oss.Option { + filenameArr := strings.Split(filename, ".") + fileExtension := strings.TrimSpace(filenameArr[len(filenameArr)-1]) + + options := make([]oss.Option, 0) + options = append(options, oss.ContentDisposition(fmt.Sprintf("attachment;filename=%s", filename))) + + contentFunc, isExist := aliyunContentTypeMapper[fileExtension] + if isExist { + o := contentFunc(filename) + options = append(options, o...) + } + + return options +} + +func aliyunContentTypeApk(filename string) []oss.Option { + options := make([]oss.Option, 0) + options = append(options, oss.ContentType("application/vnd.android.package-archive")) + options = append(options, oss.ContentDisposition(fmt.Sprintf("attachment;filename=%s", filename))) + + return options +} + +func aliyunContentTypeHTML(filename string) []oss.Option { + options := make([]oss.Option, 0) + options = append(options, oss.ContentType("text/html")) + + return options +} + +func aliyunContentTypeCSS(filename string) []oss.Option { + options := make([]oss.Option, 0) + options = append(options, oss.ContentType("text/css")) + + return options +} + +func aliyunContentTypeJS(filename string) []oss.Option { + options := make([]oss.Option, 0) + options = append(options, oss.ContentType("application/javascript")) + + return options +} + +func aliyunContentTypeExcel(filename string) []oss.Option { + options := make([]oss.Option, 0) + options = append(options, oss.ContentType("application/vnd.ms-excel")) + options = append(options, oss.ContentDisposition(fmt.Sprintf("attachment;filename=%s", filename))) + + return options +} diff --git a/builder.go b/builder.go index 6cc0a16..167b880 100644 --- a/builder.go +++ b/builder.go @@ -14,6 +14,17 @@ type Builder struct { err error } +// AliyunClient : +type AliyunClient struct { + Endpoint string + AccessKeyID string + AccessKeySecret string +} + +// GoogleCloudStorageClient : +type GoogleCloudStorageClient struct { +} + var client = map[string]bool{ GCS: true, } @@ -35,21 +46,62 @@ func NewClient(name string) *Builder { return builder } +// New : +func New(client interface{}) *Builder { + builder := new(Builder) + + switch v := client.(type) { + case AliyunClient: + adapter := new(AliyunAdapter) + adapter.Endpoint = v.Endpoint + adapter.AccessKeyID = v.AccessKeyID + adapter.AccessKeySecret = v.AccessKeySecret + builder.adapter = adapter + + case GoogleCloudStorageClient: + adapter := new(GCSAdapter) + builder.adapter = adapter + + default: + builder.err = errors.New("invalid client interface") + return builder + } + + return builder +} + // UploadFile : func (b *Builder) UploadFile(file *multipart.FileHeader, bucket, name string) (string, error) { + if b.err != nil { + return "", b.err + } return b.adapter.UploadFile(file, bucket, name) } // ReadFile : func (b *Builder) ReadFile(bucket, path string) ([]byte, error) { + if b.err != nil { + return nil, b.err + } return b.adapter.ReadFile(bucket, path) } // DeleteFileUsingURL : func (b *Builder) DeleteFileUsingURL(bucket, fileURL string) error { + if b.err != nil { + return b.err + } return b.adapter.DeleteFileUsingURL(bucket, fileURL) } +// TemporaryServingFile : +func (b *Builder) TemporaryServingFile(bucket, fileURL string, expiredTime time.Time, client interface{}) (string, error) { + if b.err != nil { + return "", b.err + } + return b.adapter.TemporaryServingFile(bucket, fileURL, expiredTime, client) +} + // GoogleTemporaryServingFile : func GoogleTemporaryServingFile(bucket, fileURL string, expiredTime time.Time, client GoogleClient) (string, error) { builder := new(Builder) @@ -60,6 +112,9 @@ func GoogleTemporaryServingFile(bucket, fileURL string, expiredTime time.Time, c // UploadReader : func (b *Builder) UploadReader(bucket, filename string, reader io.Reader, contentType string) (string, error) { + if b.err != nil { + return "", b.err + } var ( errNameIsRequired = errors.New("storage: filename is required") errBucketIsRequired = errors.New("storage: bucket is required") @@ -83,6 +138,9 @@ func (b *Builder) UploadReader(bucket, filename string, reader io.Reader, conten // UploadBuffer : func (b *Builder) UploadBuffer(bucket, filename string, contentType string) (*Buffer, error) { + if b.err != nil { + return nil, b.err + } var ( errNameIsRequired = errors.New("storage: filename is required") errBucketIsRequired = errors.New("storage: bucket is required") diff --git a/constant.go b/constant.go index 9b87519..8d51a13 100644 --- a/constant.go +++ b/constant.go @@ -2,5 +2,21 @@ package storage // Storage supporting available const ( - GCS = "GCS" + GCS = "GCS" + ALIYUN = "ALIYUN" +) + +// Content Type +const ( + ContentTypeCSV = "csv" + ContentTypePNG = "png" + ContentTypeJPEG = "jpeg" + ContentTypePDF = "pdf" + ContentTypeZip = "zip" + ContentTypeAPK = "vnd.android.package-archive" + ContentTypeHTML = "html" + ContentTypeCSS = "css" + ContentTypeJS = "js" + ContentTypeExcel = "xlsx" + ContentTypeAny = "" ) diff --git a/gcs_adapter.go b/gcs_adapter.go index 6eaa384..a8e5246 100644 --- a/gcs_adapter.go +++ b/gcs_adapter.go @@ -1,7 +1,6 @@ package storage import ( - "bytes" "context" "errors" "fmt" @@ -153,15 +152,10 @@ func (adapter *GCSAdapter) ReadFile(bucket, path string) ([]byte, error) { return slurp, nil } -// Buffer : -type Buffer struct { - storageWriter *s.Writer - bucket string -} - // UploadBuffer : func (adapter *GCSAdapter) UploadBuffer(bucket, filename string, contentType string) (*Buffer, error) { buf := new(Buffer) + buf.adapter = GCS ctx := context.Background() storageClient, err := s.NewClient(ctx) @@ -176,45 +170,3 @@ func (adapter *GCSAdapter) UploadBuffer(bucket, filename string, contentType str return buf, nil } - -// Copy : -func (buf *Buffer) Copy(reader io.Reader) error { - if _, err := io.Copy(buf.storageWriter, reader); err != nil { - msg := fmt.Sprintf("Could not write file: %v", err) - return errors.New(msg) - } - - return nil -} - -// CopyByte : -func (buf *Buffer) CopyByte(data []byte) error { - d := bytes.NewReader(data) - if _, err := io.Copy(buf.storageWriter, d); err != nil { - msg := fmt.Sprintf("Could not write file: %v", err) - return errors.New(msg) - } - - return nil -} - -// CopyString : -func (buf *Buffer) CopyString(data string) error { - d := strings.NewReader(data) - if _, err := io.Copy(buf.storageWriter, d); err != nil { - msg := fmt.Sprintf("Could not write file: %v", err) - return errors.New(msg) - } - - return nil -} - -// Close : -func (buf *Buffer) Close() (string, error) { - if err := buf.storageWriter.Close(); err != nil { - msg := fmt.Sprintf("Could not put file: %v", err) - return "", errors.New(msg) - } - - return fmt.Sprintf("%s/%s/%s", googleGCSDomain, buf.bucket, buf.storageWriter.Name), nil -} diff --git a/content_type_mapper.go b/gcs_content_type.go similarity index 70% rename from content_type_mapper.go rename to gcs_content_type.go index 0af955a..254c362 100644 --- a/content_type_mapper.go +++ b/gcs_content_type.go @@ -7,36 +7,21 @@ import ( "cloud.google.com/go/storage" ) -// Content Type -const ( - ContentTypeCSV = "csv" - ContentTypePNG = "png" - ContentTypeJPEG = "jpeg" - ContentTypePDF = "pdf" - ContentTypeGZip = "gzip" - ContentTypeAPK = "vnd.android.package-archive" - ContentTypeHTML = "html" - ContentTypeCSS = "css" - ContentTypeJS = "js" - ContentTypeExcel = "xlsx" - ContentTypeAny = "" -) - var contentTypeMapper = map[string]func(sw *storage.Writer){ - ContentTypeCSV: contentTypeCSV, - ContentTypePNG: contentTypePNG, - ContentTypeJPEG: contentTypeJPEG, - ContentTypePDF: contentTypePDF, - ContentTypeGZip: contentTypeGZip, - ContentTypeAPK: contentTypeApk, - ContentTypeHTML: contentTypeHTML, - ContentTypeCSS: contentTypeCSS, - ContentTypeJS: contentTypeJS, + ContentTypeCSV: contentTypeCSV, + ContentTypePNG: contentTypePNG, + ContentTypeJPEG: contentTypeJPEG, + ContentTypePDF: contentTypePDF, + ContentTypeZip: contentTypeZip, + ContentTypeAPK: contentTypeApk, + ContentTypeHTML: contentTypeHTML, + ContentTypeCSS: contentTypeCSS, + ContentTypeJS: contentTypeJS, ContentTypeExcel: contentTypeExcel, // ContentTypeAny: contentTypeAny, } -func contentTypeGZip(sw *storage.Writer) { +func contentTypeZip(sw *storage.Writer) { sw.ContentType = "application/zip" sw.ContentDisposition = fmt.Sprintf("attachment;filename=%s", sw.Name) } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cb73c4f --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module github.com/myussufz/cloud-storage + +require ( + cloud.google.com/go v0.37.2 + github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5 + github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect + github.com/kr/pretty v0.1.0 // indirect + github.com/satori/go.uuid v1.2.0 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + gopkg.in/yaml.v2 v2.2.2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ffe8693 --- /dev/null +++ b/go.sum @@ -0,0 +1,199 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.2 h1:4y4L7BdHenTfZL0HervofNTHh9Ad6mNX72cQvl+5eH0= +cloud.google.com/go v0.37.2/go.mod h1:H8IAquKe2L30IxoupDgqTaQvKSwF/c8prYHynGIWQbA= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5 h1:nWDRPCyCltiTsANwC/n3QZH7Vww33Npq9MKqlwRzI/c= +github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= +go.opencensus.io v0.19.2 h1:ZZpq6xI6kv/LuE/5s5UQvBU5vMjvRnPb8PvJrIntAnc= +go.opencensus.io v0.19.2/go.mod h1:NO/8qkisMZLZ1FCsKNqtJPwc8/TaclWyY0B6wcYNg9M= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU= +google.golang.org/api v0.3.0 h1:UIJY20OEo3+tK5MBlcdx37kmdH6EnRjGkW78mc6+EeA= +google.golang.org/api v0.3.0/go.mod h1:IuvZyQh8jgscv8qWfQ4ABd8m7hEudgBFM/EdhA3BnXw= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/upload_buffer.go b/upload_buffer.go new file mode 100644 index 0000000..60c17c7 --- /dev/null +++ b/upload_buffer.go @@ -0,0 +1,77 @@ +package storage + +import ( + "bytes" + "errors" + "fmt" + "io" + "strings" + + s "cloud.google.com/go/storage" + oss "github.com/aliyun/aliyun-oss-go-sdk/oss" +) + +// Buffer : +type Buffer struct { + adapter string // gcs and aliyun + bucket string // gcs and aliyun + filename string // gcs and aliyun + storageWriter *s.Writer // gcs + object *oss.Bucket // aliyun + endpoint string // aliyun + position int64 // aliyun +} + +// Copy : +func (buf *Buffer) Copy(reader io.Reader) error { + switch buf.adapter { + case GCS: + if _, err := io.Copy(buf.storageWriter, reader); err != nil { + msg := fmt.Sprintf("Could not write file: %v", err) + return errors.New(msg) + } + + case ALIYUN: + position, err := buf.object.AppendObject(buf.filename, reader, buf.position) + if err != nil { + msg := fmt.Sprintf("Could not write file: %v", err) + return errors.New(msg) + } + buf.position = position + default: + return errors.New("invalid adapter") + } + + return nil +} + +// CopyByte : +func (buf *Buffer) CopyByte(data []byte) error { + d := bytes.NewReader(data) + return buf.Copy(d) +} + +// CopyString : +func (buf *Buffer) CopyString(data string) error { + d := strings.NewReader(data) + return buf.Copy(d) +} + +// Close : +func (buf *Buffer) Close() (string, error) { + switch buf.adapter { + case GCS: + if err := buf.storageWriter.Close(); err != nil { + msg := fmt.Sprintf("Could not put file: %v", err) + return "", errors.New(msg) + } + + return fmt.Sprintf("%s/%s/%s", googleGCSDomain, buf.bucket, buf.storageWriter.Name), nil + + case ALIYUN: + return getAliyunFileURL(buf.endpoint, buf.bucket, buf.filename), nil + + default: + return "", errors.New("invalid adapter") + } +}