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