gifs/api

Paddy 2014-10-17 Parent:1bbbe113f599

6:eb450538f079 Go to Latest

gifs/api/storage.go

Make UsageTracker an interface, ditch the channel interface. Stop using channels to track usage. Just call functions. Make UsageTracker an interface, so there can be multiple implementations, and create the in-memory implementation.

History
1 package api
3 import (
4 "errors"
5 "io"
6 "io/ioutil"
7 "net/http"
8 "sync"
10 "code.google.com/p/google-api-go-client/googleapi"
11 "code.google.com/p/google-api-go-client/storage/v1"
12 )
14 var (
15 ErrBucketNotFound = errors.New("bucket not found")
16 ErrBlobNotFound = errors.New("blob not found")
17 )
19 type Storage interface {
20 Upload(bucket, tmp string, r io.Reader) error
21 Delete(bucket, tmp string) error
22 Move(srcBucket, src, dstBucket, dst string) error
23 Download(bucket, id string, w io.Writer) (int64, error)
24 }
26 type memstorage struct {
27 buckets map[string]membucket
28 sync.RWMutex
29 }
31 type membucket map[string][]byte
33 func NewMemStorage() *memstorage {
34 return &memstorage{
35 buckets: map[string]membucket{},
36 }
37 }
39 func (m *memstorage) Upload(bucket, tmp string, r io.Reader) error {
40 m.Lock()
41 defer m.Unlock()
43 if _, ok := m.buckets[bucket]; !ok {
44 m.buckets[bucket] = membucket{}
45 }
46 bytes, err := ioutil.ReadAll(r)
47 if err != nil {
48 return err
49 }
50 m.buckets[bucket][tmp] = bytes
51 return nil
52 }
54 func (m *memstorage) Delete(bucket, tmp string) error {
55 m.Lock()
56 defer m.Unlock()
58 delete(m.buckets[bucket], tmp)
59 return nil
60 }
62 func (m *memstorage) Move(srcBucket, src, dstBucket, dst string) error {
63 m.Lock()
64 defer m.Unlock()
66 if _, ok := m.buckets[srcBucket]; !ok {
67 return ErrBucketNotFound
68 }
69 if _, ok := m.buckets[srcBucket][src]; !ok {
70 return ErrBlobNotFound
71 }
72 if _, ok := m.buckets[dstBucket]; !ok {
73 m.buckets[dstBucket] = membucket{}
74 }
75 m.buckets[dstBucket][dst] = m.buckets[srcBucket][src]
76 return m.Delete(srcBucket, src)
77 }
79 func (m *memstorage) Download(bucket, id string, w io.Writer) (int64, error) {
80 m.RLock()
81 defer m.RUnlock()
83 if _, ok := m.buckets[bucket]; !ok {
84 return 0, ErrBucketNotFound
85 }
86 if _, ok := m.buckets[bucket][id]; !ok {
87 return 0, ErrBlobNotFound
88 }
89 n, err := w.Write(m.buckets[bucket][id])
90 return int64(n), err
91 }
93 type GoogleCloudStorage struct {
94 *storage.Service
95 }
97 func (gcs *GoogleCloudStorage) Upload(bucket, tmp string, r io.Reader) error {
98 object := &storage.Object{Name: tmp}
99 _, err := gcs.Objects.Insert(bucket, object).Media(r).Do()
100 if err != nil {
101 // TODO: abstract out this error
102 return err
103 }
104 return nil
105 }
107 func (gcs *GoogleCloudStorage) Delete(bucket, tmp string) error {
108 // TODO: abstract out this error
109 return gcs.Objects.Delete(bucket, tmp).Do()
110 }
112 func (gcs *GoogleCloudStorage) Move(srcBucket, src, dstBucket, dst string) error {
113 _, err := gcs.Objects.Get(dstBucket, dst).Do()
114 if err == nil {
115 return nil
116 }
117 // ignore 404 errors on the destination; we don't expect it to exist, after all
118 if e, ok := err.(*googleapi.Error); !ok || e.Code != 404 {
119 // TODO: abstract out this error
120 return err
121 }
122 _, err = gcs.Objects.Copy(srcBucket, src, dstBucket, dst, nil).Do()
123 if err != nil {
124 // TODO: abstract out this error
125 return err
126 }
127 objectAcl := &storage.ObjectAccessControl{
128 Bucket: dstBucket, Entity: "allUsers", Object: dst, Role: "READER",
129 }
130 _, err = gcs.ObjectAccessControls.Insert(dstBucket, dst, objectAcl).Do()
131 if err != nil {
132 // TODO: abstract out this error
133 return err
134 }
135 return nil
136 }
138 func (gcs *GoogleCloudStorage) Download(bucket, id string, w io.Writer) (int64, error) {
139 res, err := gcs.Objects.Get(bucket, id).Do()
140 if err != nil {
141 // TODO: abstract out this error
142 return 0, err
143 }
144 resp, err := http.Get(res.MediaLink)
145 if err != nil {
146 // TODO: abstract out this error
147 return 0, err
148 }
149 defer resp.Body.Close()
150 // TODO: abstract out this error
151 return io.Copy(w, resp.Body)
152 }