add remote image storage support to backend

This commit is contained in:
Dmitry Verkhoturov
2020-04-04 14:06:39 -05:00
committed by Umputun
parent b6ef4f393a
commit ce92f63215
5 changed files with 163 additions and 4 deletions
+13 -4
View File
@@ -129,7 +129,7 @@ type StoreGroup struct {
// ImageGroup defines options group for store pictures
type ImageGroup struct {
Type string `long:"type" env:"TYPE" description:"type of storage" choice:"fs" choice:"bolt" default:"fs"` // nolint
Type string `long:"type" env:"TYPE" description:"type of storage" choice:"fs" choice:"bolt" choice:"rpc" default:"fs"` // nolint
FS struct {
Path string `long:"path" env:"PATH" default:"./var/pictures" description:"images location"`
Staging string `long:"staging" env:"STAGING" default:"./var/pictures.staging" description:"staging location"`
@@ -138,9 +138,10 @@ type ImageGroup struct {
Bolt struct {
File string `long:"file" env:"FILE" default:"./var/pictures.db" description:"images bolt file location"`
} `group:"bolt" namespace:"bolt" env-namespace:"bolt"`
MaxSize int `long:"max-size" env:"MAX_SIZE" default:"5000000" description:"max size of image file"`
ResizeWidth int `long:"resize-width" env:"RESIZE_WIDTH" default:"2400" description:"width of resized image"`
ResizeHeight int `long:"resize-height" env:"RESIZE_HEIGHT" default:"900" description:"height of resized image"`
MaxSize int `long:"max-size" env:"MAX_SIZE" default:"5000000" description:"max size of image file"`
ResizeWidth int `long:"resize-width" env:"RESIZE_WIDTH" default:"2400" description:"width of resized image"`
ResizeHeight int `long:"resize-height" env:"RESIZE_HEIGHT" default:"900" description:"height of resized image"`
RPC RPCGroup `group:"rpc" namespace:"rpc" env-namespace:"RPC"`
}
// AvatarGroup defines options group for avatar params
@@ -590,6 +591,14 @@ func (s *ServerCommand) makePicturesStore() (*image.Service, error) {
Staging: s.Image.FS.Staging,
Partitions: s.Image.FS.Partitions,
}, imageServiceParams), nil
case "rpc":
return image.NewService(&image.RPC{
Client: jrpc.Client{
API: s.Image.RPC.API,
Client: http.Client{Timeout: s.Image.RPC.TimeOut},
AuthUser: s.Image.RPC.AuthUser,
AuthPasswd: s.Image.RPC.AuthPassword,
}}, imageServiceParams), nil
}
return nil, errors.Errorf("unsupported pictures store type %s", s.Image.Type)
}
@@ -56,6 +56,7 @@ func TestBoltStore_LoadAfterSave(t *testing.T) {
data, err := svc.Load(id)
assert.NoError(t, err)
assert.Equal(t, 1462, len(data))
assert.Equal(t, gopherPNGBytes(), data)
_, err = svc.Load("abcd")
assert.Error(t, err)
+1
View File
@@ -113,6 +113,7 @@ func TestFsStore_LoadAfterSave(t *testing.T) {
data, err := svc.Load(id)
assert.NoError(t, err)
assert.Equal(t, 1462, len(data))
assert.Equal(t, gopherPNGBytes(), data)
_, err = svc.Load("abcd")
assert.Error(t, err)
}
+58
View File
@@ -0,0 +1,58 @@
package image
import (
"context"
"encoding/base64"
"encoding/json"
"io/ioutil"
"strings"
"time"
"github.com/go-pkgz/jrpc"
)
// RPC implements remote engine and delegates all Calls to remote http server
type RPC struct {
jrpc.Client
}
func (r *RPC) Save(userID string, img []byte) (id string, err error) {
resp, err := r.Call("image.save", userID, img)
if err != nil {
return "", err
}
err = json.Unmarshal(*resp.Result, &id)
return id, err
}
func (r *RPC) SaveWithID(id string, img []byte) (string, error) {
resp, err := r.Call("image.save_with_id", id, img)
if err != nil {
return "", err
}
var newID string
err = json.Unmarshal(*resp.Result, &newID)
return newID, err
}
func (r *RPC) Load(id string) ([]byte, error) {
resp, err := r.Call("image.load", id)
if err != nil {
return nil, err
}
var rawImg string
if err = json.Unmarshal(*resp.Result, &rawImg); err != nil {
return nil, err
}
return ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, strings.NewReader(rawImg)))
}
func (r *RPC) Commit(id string) error {
_, err := r.Call("image.commit", id)
return err
}
func (r *RPC) Cleanup(_ context.Context, ttl time.Duration) error {
_, err := r.Call("image.cleanup", ttl)
return err
}
@@ -0,0 +1,90 @@
package image
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-pkgz/jrpc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRemote_Save(t *testing.T) {
ts := testServer(t, fmt.Sprintf(`{"method":"image.save","params":["admin","%s"],"id":1}`, gopher),
`{"result":"12345","id":1}`)
defer ts.Close()
c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
var a Store = &c
_ = a
res, err := c.Save("admin", gopherPNGBytes())
assert.NoError(t, err)
assert.Equal(t, "12345", res)
}
func TestRemote_SaveWithID(t *testing.T) {
ts := testServer(t, fmt.Sprintf(`{"method":"image.save_with_id","params":["54321","%s"],"id":1}`, gopher),
`{"result":"12345","id":1}`)
defer ts.Close()
c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
var a Store = &c
_ = a
res, err := c.SaveWithID("54321", gopherPNGBytes())
assert.NoError(t, err)
assert.Equal(t, "12345", res)
}
func TestRemote_Load(t *testing.T) {
ts := testServer(t, `{"method":"image.load","params":"54321","id":1}`,
fmt.Sprintf(`{"result":"%v","id":1}`, gopher))
defer ts.Close()
c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
var a Store = &c
_ = a
res, err := c.Load("54321")
assert.NoError(t, err)
assert.Equal(t, gopherPNGBytes(), res)
}
func TestRemote_Commit(t *testing.T) {
ts := testServer(t, `{"method":"image.commit","params":"gopher_id","id":1}`, `{"id":1}`)
defer ts.Close()
c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
var a Store = &c
_ = a
err := c.Commit("gopher_id")
assert.NoError(t, err)
}
func TestRemote_Cleanup(t *testing.T) {
ts := testServer(t, `{"method":"image.cleanup","params":60000000000,"id":1}`, `{"id":1}`)
defer ts.Close()
c := RPC{Client: jrpc.Client{API: ts.URL, Client: http.Client{}}}
var a Store = &c
_ = a
err := c.Cleanup(context.TODO(), time.Minute)
assert.NoError(t, err)
}
func testServer(t *testing.T, req, resp string) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, err := ioutil.ReadAll(r.Body)
require.NoError(t, err)
assert.Equal(t, req, string(body))
_, _ = fmt.Fprint(w, resp)
}))
}