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