launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/provider/dummy/storage.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package dummy 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "net/http" 12 "sort" 13 "strings" 14 "time" 15 16 "launchpad.net/juju-core/environs/storage" 17 "launchpad.net/juju-core/errors" 18 "launchpad.net/juju-core/utils" 19 ) 20 21 // IsSameStorage returns whether the storage instances are the same. 22 // Both storages must have been created through the dummy provider. 23 func IsSameStorage(s1, s2 storage.Storage) bool { 24 localS1, localS2 := s1.(*dummyStorage), s2.(*dummyStorage) 25 return localS1.env.name == localS2.env.name 26 } 27 28 func (e *environ) Storage() storage.Storage { 29 return &dummyStorage{env: e} 30 } 31 32 // storageServer holds the storage for an environState. 33 type storageServer struct { 34 path string // path prefix in http space. 35 state *environState 36 files map[string][]byte 37 poisoned map[string]error 38 } 39 40 func newStorageServer(state *environState, path string) *storageServer { 41 return &storageServer{ 42 state: state, 43 files: make(map[string][]byte), 44 path: path, 45 poisoned: make(map[string]error), 46 } 47 } 48 49 // Poison causes all fetches of the given path to 50 // return the given error. 51 func Poison(ss storage.Storage, path string, poisonErr error) { 52 s := ss.(*dummyStorage) 53 srv, err := s.server() 54 if err != nil { 55 panic("cannot poison destroyed storage") 56 } 57 srv.state.mu.Lock() 58 srv.poisoned[path] = poisonErr 59 srv.state.mu.Unlock() 60 } 61 62 func (s *storageServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { 63 if req.Method != "GET" { 64 http.Error(w, "only GET is supported", http.StatusMethodNotAllowed) 65 return 66 } 67 data, err := s.dataWithDelay(req.URL.Path) 68 if err != nil { 69 http.Error(w, "404 "+err.Error(), http.StatusNotFound) 70 return 71 } 72 w.Header().Set("Content-Type", "application/octet-stream") 73 // If the write fails, the rest of the tests should pick up the problem. 74 // It's more likely because the client has legitimately dropped the 75 // connection. 76 w.Write(data) 77 } 78 79 // dataWithDelay returns the data for the given path, 80 // waiting for the configured amount of time before 81 // accessing it. 82 func (s *storageServer) dataWithDelay(path string) (data []byte, err error) { 83 s.state.mu.Lock() 84 delay := s.state.storageDelay 85 s.state.mu.Unlock() 86 time.Sleep(delay) 87 s.state.mu.Lock() 88 defer s.state.mu.Unlock() 89 if err := s.poisoned[path]; err != nil { 90 return nil, err 91 } 92 data, ok := s.files[path] 93 if !ok { 94 return nil, errors.NotFoundf("file %q not found", path) 95 } 96 return data, nil 97 } 98 99 func (s *storageServer) Put(name string, r io.Reader, length int64) error { 100 // Allow Put to be poisoned as well. 101 if err := s.poisoned[name]; err != nil { 102 return err 103 } 104 105 // We only log Put requests on private storage. 106 if strings.HasSuffix(s.path, "/private") { 107 s.state.ops <- OpPutFile{s.state.name, name} 108 } 109 var buf bytes.Buffer 110 _, err := io.Copy(&buf, r) 111 if err != nil { 112 return err 113 } 114 s.state.mu.Lock() 115 s.files[name] = buf.Bytes() 116 s.state.mu.Unlock() 117 return nil 118 } 119 120 func (s *storageServer) Get(name string) (io.ReadCloser, error) { 121 data, err := s.dataWithDelay(name) 122 if err != nil { 123 return nil, err 124 } 125 return ioutil.NopCloser(bytes.NewBuffer(data)), nil 126 } 127 128 func (s *storageServer) URL(name string) (string, error) { 129 // Mimic the MAAS behaviour so we are testing with the 130 // lowest common denominator. 131 if name != "" { 132 if _, ok := s.files[name]; !ok { 133 found := false 134 for file, _ := range s.files { 135 found = strings.HasPrefix(file, name+"/") 136 if found { 137 break 138 } 139 } 140 if !found { 141 return "", errors.NotFoundf(name) 142 } 143 } 144 } 145 return fmt.Sprintf("http://%v%s/%s", s.state.httpListener.Addr(), s.path, name), nil 146 } 147 148 func (s *storageServer) Remove(name string) error { 149 s.state.mu.Lock() 150 delete(s.files, name) 151 s.state.mu.Unlock() 152 return nil 153 } 154 155 func (s *storageServer) DefaultConsistencyStrategy() utils.AttemptStrategy { 156 return utils.AttemptStrategy{} 157 } 158 159 // ShouldRetry is specified in the StorageReader interface. 160 func (s *storageServer) ShouldRetry(err error) bool { 161 return false 162 } 163 164 func (s *storageServer) RemoveAll() error { 165 s.state.mu.Lock() 166 s.files = make(map[string][]byte) 167 s.state.mu.Unlock() 168 return nil 169 } 170 171 func (s *storageServer) List(prefix string) ([]string, error) { 172 s.state.mu.Lock() 173 defer s.state.mu.Unlock() 174 var names []string 175 for name := range s.files { 176 if strings.HasPrefix(name, prefix) { 177 names = append(names, name) 178 } 179 } 180 sort.Strings(names) 181 return names, nil 182 } 183 184 // dummyStorage implements the client side of the Storage interface. 185 type dummyStorage struct { 186 env *environ 187 } 188 189 // server returns the server side of the given storage. 190 func (s *dummyStorage) server() (*storageServer, error) { 191 st, err := s.env.state() 192 if err != nil { 193 return nil, err 194 } 195 return st.storage, nil 196 } 197 198 func (s *dummyStorage) Get(name string) (io.ReadCloser, error) { 199 srv, err := s.server() 200 if err != nil { 201 return nil, err 202 } 203 return srv.Get(name) 204 } 205 206 func (s *dummyStorage) URL(name string) (string, error) { 207 srv, err := s.server() 208 if err != nil { 209 return "", err 210 } 211 return srv.URL(name) 212 } 213 214 func (s *dummyStorage) DefaultConsistencyStrategy() utils.AttemptStrategy { 215 return utils.AttemptStrategy{} 216 } 217 218 // ShouldRetry is specified in the StorageReader interface. 219 func (s *dummyStorage) ShouldRetry(err error) bool { 220 return false 221 } 222 223 func (s *dummyStorage) Put(name string, r io.Reader, length int64) error { 224 srv, err := s.server() 225 if err != nil { 226 return err 227 } 228 return srv.Put(name, r, length) 229 } 230 231 func (s *dummyStorage) Remove(name string) error { 232 srv, err := s.server() 233 if err != nil { 234 return err 235 } 236 return srv.Remove(name) 237 } 238 239 func (s *dummyStorage) RemoveAll() error { 240 srv, err := s.server() 241 if err != nil { 242 return err 243 } 244 return srv.RemoveAll() 245 } 246 247 func (s *dummyStorage) List(prefix string) ([]string, error) { 248 srv, err := s.server() 249 if err != nil { 250 return nil, err 251 } 252 return srv.List(prefix) 253 }