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