gifs/api
2014-10-17
Parent:1bbbe113f599
gifs/api/storage.go
Create a Dockerfile and binary. Write a Dockerfile that compiles everything and runs it. Start the binary; load a Context from etcd, and start everything running. The binary is pretty useless until we get HTTP handlers, though, obviously.
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 }