github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/pfsagentd/swift_proxy_emulator_test.go (about) 1 // Copyright (c) 2015-2021, NVIDIA CORPORATION. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package main 5 6 import ( 7 "context" 8 "io/ioutil" 9 "net" 10 "net/http" 11 "strconv" 12 "strings" 13 "sync" 14 "testing" 15 "time" 16 17 "github.com/swiftstack/ProxyFS/conf" 18 ) 19 20 const ( 21 testAuthToken = "AUTH_tkTestToken" 22 testJrpcResponseBufSize = 1024 * 1024 23 ) 24 25 type testSwiftProxyEmulatorGlobalsStruct struct { 26 sync.WaitGroup 27 t *testing.T 28 ramswiftNoAuthURL string 29 proxyfsdJrpcTCPAddr *net.TCPAddr 30 jrpcResponsePool *sync.Pool 31 httpClient *http.Client 32 httpServer *http.Server 33 } 34 35 var testSwiftProxyEmulatorGlobals testSwiftProxyEmulatorGlobalsStruct 36 37 func startSwiftProxyEmulator(t *testing.T, confMap conf.ConfMap) { 38 var ( 39 err error 40 infoResponse *http.Response 41 jrpcServerIPAddr string 42 jrpcServerTCPPort uint16 43 swiftClientNoAuthIPAddr string 44 swiftClientNoAuthTCPPort uint16 45 whoAmI string 46 ) 47 48 testSwiftProxyEmulatorGlobals.t = t 49 50 swiftClientNoAuthIPAddr, err = confMap.FetchOptionValueString("SwiftClient", "NoAuthIPAddr") 51 if nil != err { 52 t.Fatal(err) 53 } 54 55 swiftClientNoAuthTCPPort, err = confMap.FetchOptionValueUint16("SwiftClient", "NoAuthTCPPort") 56 if nil != err { 57 t.Fatal(err) 58 } 59 60 testSwiftProxyEmulatorGlobals.ramswiftNoAuthURL = "http://" + net.JoinHostPort(swiftClientNoAuthIPAddr, strconv.FormatUint(uint64(swiftClientNoAuthTCPPort), 10)) 61 62 whoAmI, err = confMap.FetchOptionValueString("Cluster", "WhoAmI") 63 if nil != err { 64 t.Fatal(err) 65 } 66 67 jrpcServerIPAddr, err = confMap.FetchOptionValueString("Peer:"+whoAmI, "PrivateIPAddr") 68 if nil != err { 69 t.Fatal(err) 70 } 71 72 jrpcServerTCPPort, err = confMap.FetchOptionValueUint16("JSONRPCServer", "TCPPort") 73 if nil != err { 74 t.Fatal(err) 75 } 76 77 testSwiftProxyEmulatorGlobals.proxyfsdJrpcTCPAddr, err = net.ResolveTCPAddr("tcp", net.JoinHostPort(jrpcServerIPAddr, strconv.FormatUint(uint64(jrpcServerTCPPort), 10))) 78 if nil != err { 79 t.Fatal(err) 80 } 81 82 testSwiftProxyEmulatorGlobals.httpClient = &http.Client{} 83 84 testSwiftProxyEmulatorGlobals.httpServer = &http.Server{ 85 Addr: testSwiftProxyAddr, 86 Handler: &testSwiftProxyEmulatorGlobals, 87 } 88 89 testSwiftProxyEmulatorGlobals.jrpcResponsePool = &sync.Pool{ 90 New: func() (bufAsInterface interface{}) { 91 var ( 92 bufAsByteSlice []byte 93 ) 94 95 bufAsByteSlice = make([]byte, testJrpcResponseBufSize) 96 97 bufAsInterface = bufAsByteSlice 98 99 return 100 }, 101 } 102 103 testSwiftProxyEmulatorGlobals.Add(1) 104 105 go func() { 106 _ = testSwiftProxyEmulatorGlobals.httpServer.ListenAndServe() 107 108 testSwiftProxyEmulatorGlobals.Done() 109 }() 110 111 for { 112 infoResponse, err = http.Get("http://" + testSwiftProxyAddr + "/info") 113 if nil == err { 114 break 115 } 116 117 time.Sleep(testDaemonStartPollInterval) 118 } 119 120 if http.StatusOK != infoResponse.StatusCode { 121 t.Fatalf("GET /info from ServeHTTP() got unexpected status %s", infoResponse.Status) 122 } 123 } 124 125 func stopSwiftProxyEmulator() { 126 var ( 127 err error 128 ) 129 130 err = testSwiftProxyEmulatorGlobals.httpServer.Shutdown(context.Background()) 131 if nil != err { 132 testSwiftProxyEmulatorGlobals.t.Fatalf("testSwiftProxyEmulatorGlobals.httpServer.Shutdown() failed: %v", err) 133 } 134 135 testSwiftProxyEmulatorGlobals.Wait() 136 137 testSwiftProxyEmulatorGlobals.jrpcResponsePool = nil 138 } 139 140 func (dummy *testSwiftProxyEmulatorGlobalsStruct) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) { 141 // Handle the GET of/on info & AuthURL cases 142 143 if http.MethodGet == request.Method { 144 switch request.URL.Path { 145 case "/info": 146 doInfo(responseWriter) 147 return 148 case "/auth/v1.0": 149 doAuth(responseWriter, request) 150 return 151 default: 152 // Fall through to normal processing 153 } 154 } 155 156 // Reject unauthorized requests 157 158 if request.Header.Get("X-Auth-Token") != testAuthToken { 159 responseWriter.WriteHeader(http.StatusUnauthorized) 160 return 161 } 162 163 // Branch off to individual request method handlers 164 165 switch request.Method { 166 case http.MethodGet: 167 doGET(responseWriter, request) 168 case http.MethodPut: 169 doPUT(responseWriter, request) 170 case "PROXYFS": 171 doRPC(responseWriter, request) 172 default: 173 responseWriter.WriteHeader(http.StatusMethodNotAllowed) 174 } 175 } 176 177 func doInfo(authResponseWriter http.ResponseWriter) { 178 var ( 179 err error 180 getBuf []byte 181 noAuthResponse *http.Response 182 ) 183 184 noAuthResponse, err = http.Get(testSwiftProxyEmulatorGlobals.ramswiftNoAuthURL + "/info") 185 if nil != err { 186 testSwiftProxyEmulatorGlobals.t.Fatalf("GET of /info from ramswift failed: %v", err) 187 } 188 189 if http.StatusOK != noAuthResponse.StatusCode { 190 testSwiftProxyEmulatorGlobals.t.Fatalf("GET of /info from ramswift returned bad status: %v", noAuthResponse.Status) 191 } 192 193 getBuf, err = ioutil.ReadAll(noAuthResponse.Body) 194 if nil != err { 195 testSwiftProxyEmulatorGlobals.t.Fatalf("GET of /info returned unreadable Body: %v", err) 196 } 197 198 err = noAuthResponse.Body.Close() 199 if nil != err { 200 testSwiftProxyEmulatorGlobals.t.Fatalf("GET of /info returned uncloseable Body: %v", err) 201 } 202 203 authResponseWriter.WriteHeader(http.StatusOK) 204 _, _ = authResponseWriter.Write(getBuf) 205 } 206 207 func doAuth(authResponseWriter http.ResponseWriter, authRequest *http.Request) { 208 if authRequest.Header.Get("X-Auth-User") != testAuthUser { 209 authResponseWriter.WriteHeader(http.StatusUnauthorized) 210 return 211 } 212 if authRequest.Header.Get("X-Auth-Key") != testAuthKey { 213 authResponseWriter.WriteHeader(http.StatusUnauthorized) 214 return 215 } 216 authResponseWriter.Header().Add("X-Auth-Token", testAuthToken) 217 authResponseWriter.Header().Add("X-Storage-Url", "http://"+testSwiftProxyAddr+"/v1/"+testAccountName) 218 authResponseWriter.WriteHeader(http.StatusOK) 219 } 220 221 // doGET proxies the GET over to ramswift 222 // 223 func doGET(authResponseWriter http.ResponseWriter, authRequest *http.Request) { 224 var ( 225 contentRangeHeader string 226 contentTypeHeader string 227 err error 228 getBuf []byte 229 hostHeader string 230 noAuthPath string 231 noAuthRequest *http.Request 232 noAuthResponse *http.Response 233 noAuthStatusCode int 234 rangeHeader string 235 ) 236 237 if !strings.HasPrefix(authRequest.URL.Path, "/proxyfs/"+testAccountName) { 238 authResponseWriter.WriteHeader(http.StatusNotFound) 239 return 240 } 241 242 noAuthPath = strings.Replace(authRequest.URL.Path, "proxyfs", "v1", 1) 243 244 noAuthRequest, err = http.NewRequest("GET", testSwiftProxyEmulatorGlobals.ramswiftNoAuthURL+noAuthPath, nil) 245 if nil != err { 246 authResponseWriter.WriteHeader(http.StatusBadRequest) 247 return 248 } 249 250 hostHeader = authRequest.Header.Get("Host") 251 if "" != hostHeader { 252 noAuthRequest.Header.Add("Host", hostHeader) 253 } 254 255 rangeHeader = authRequest.Header.Get("Range") 256 if "" != rangeHeader { 257 noAuthRequest.Header.Add("Range", rangeHeader) 258 } 259 260 noAuthResponse, err = testSwiftProxyEmulatorGlobals.httpClient.Do(noAuthRequest) 261 if nil != err { 262 authResponseWriter.WriteHeader(http.StatusBadRequest) 263 return 264 } 265 266 noAuthStatusCode = noAuthResponse.StatusCode 267 268 if (http.StatusOK != noAuthStatusCode) && (http.StatusPartialContent != noAuthStatusCode) { 269 _ = noAuthResponse.Body.Close() 270 authResponseWriter.WriteHeader(noAuthStatusCode) 271 return 272 } 273 274 getBuf, err = ioutil.ReadAll(noAuthResponse.Body) 275 if nil != err { 276 _ = noAuthResponse.Body.Close() 277 authResponseWriter.WriteHeader(http.StatusBadRequest) 278 return 279 } 280 281 err = noAuthResponse.Body.Close() 282 if nil != err { 283 authResponseWriter.WriteHeader(http.StatusBadRequest) 284 return 285 } 286 287 contentTypeHeader = noAuthResponse.Header.Get("Content-Type") 288 if "" != contentTypeHeader { 289 authResponseWriter.Header().Add("Content-Type", contentTypeHeader) 290 } 291 292 contentRangeHeader = noAuthResponse.Header.Get("Content-Range") 293 if "" != contentRangeHeader { 294 authResponseWriter.Header().Add("Content-Range", contentRangeHeader) 295 } 296 297 authResponseWriter.WriteHeader(noAuthStatusCode) 298 299 _, _ = authResponseWriter.Write(getBuf) 300 } 301 302 // doPUT proxies the GET over to ramswift 303 // 304 func doPUT(authResponseWriter http.ResponseWriter, authRequest *http.Request) { 305 var ( 306 err error 307 hostHeader string 308 noAuthPath string 309 noAuthRequest *http.Request 310 noAuthResponse *http.Response 311 ) 312 313 if !strings.HasPrefix(authRequest.URL.Path, "/proxyfs/"+testAccountName) { 314 _ = authRequest.Body.Close() 315 authResponseWriter.WriteHeader(http.StatusNotFound) 316 return 317 } 318 319 noAuthPath = strings.Replace(authRequest.URL.Path, "proxyfs", "v1", 1) 320 321 noAuthRequest, err = http.NewRequest("PUT", testSwiftProxyEmulatorGlobals.ramswiftNoAuthURL+noAuthPath, authRequest.Body) 322 if nil != err { 323 _ = authRequest.Body.Close() 324 authResponseWriter.WriteHeader(http.StatusBadRequest) 325 return 326 } 327 328 hostHeader = authRequest.Header.Get("Host") 329 if "" != hostHeader { 330 noAuthRequest.Header.Add("Host", hostHeader) 331 } 332 333 noAuthResponse, err = testSwiftProxyEmulatorGlobals.httpClient.Do(noAuthRequest) 334 if nil != err { 335 _ = authRequest.Body.Close() 336 authResponseWriter.WriteHeader(http.StatusBadRequest) 337 return 338 } 339 340 err = authRequest.Body.Close() 341 if nil != err { 342 authResponseWriter.WriteHeader(http.StatusBadRequest) 343 return 344 } 345 346 authResponseWriter.WriteHeader(noAuthResponse.StatusCode) 347 } 348 349 // doRPC proxies the payload as a JSON RPC request over to proxyfsd 350 // 351 func doRPC(responseWriter http.ResponseWriter, request *http.Request) { 352 var ( 353 err error 354 jrpcResponseBuf []byte 355 jrpcResponseLen int 356 jrpcRequestBuf []byte 357 tcpConn *net.TCPConn 358 ) 359 360 if !strings.HasPrefix(request.URL.Path, "/proxyfs/"+testAccountName) { 361 _ = request.Body.Close() 362 responseWriter.WriteHeader(http.StatusNotFound) 363 return 364 } 365 366 jrpcRequestBuf, err = ioutil.ReadAll(request.Body) 367 _ = request.Body.Close() 368 if nil != err { 369 responseWriter.WriteHeader(http.StatusBadRequest) 370 return 371 } 372 373 if request.Header.Get("Content-Type") != "application/json" { 374 responseWriter.WriteHeader(http.StatusBadRequest) 375 return 376 } 377 378 tcpConn, err = net.DialTCP("tcp", nil, testSwiftProxyEmulatorGlobals.proxyfsdJrpcTCPAddr) 379 if nil != err { 380 responseWriter.WriteHeader(http.StatusServiceUnavailable) 381 return 382 } 383 384 _, err = tcpConn.Write(jrpcRequestBuf) 385 if nil != err { 386 _ = tcpConn.Close() 387 responseWriter.WriteHeader(http.StatusServiceUnavailable) 388 return 389 } 390 391 jrpcResponseBuf = testSwiftProxyEmulatorGlobals.jrpcResponsePool.Get().([]byte) 392 393 jrpcResponseLen, err = tcpConn.Read(jrpcResponseBuf) 394 if nil != err { 395 _ = tcpConn.Close() 396 responseWriter.WriteHeader(http.StatusServiceUnavailable) 397 return 398 } 399 400 err = tcpConn.Close() 401 if nil != err { 402 responseWriter.WriteHeader(http.StatusServiceUnavailable) 403 return 404 } 405 406 responseWriter.Header().Add("Content-Type", "application/json") 407 responseWriter.WriteHeader(http.StatusOK) 408 _, _ = responseWriter.Write(jrpcResponseBuf[:jrpcResponseLen]) 409 410 testSwiftProxyEmulatorGlobals.jrpcResponsePool.Put(jrpcResponseBuf) 411 }