github.com/Tyktechnologies/tyk@v2.9.5+incompatible/gateway/rpc_test.go (about) 1 // +build !race 2 3 package gateway 4 5 import ( 6 "testing" 7 "time" 8 9 "github.com/gorilla/mux" 10 11 "github.com/TykTechnologies/tyk/cli" 12 13 "github.com/TykTechnologies/gorpc" 14 "github.com/TykTechnologies/tyk/apidef" 15 "github.com/TykTechnologies/tyk/config" 16 "github.com/TykTechnologies/tyk/rpc" 17 "github.com/TykTechnologies/tyk/test" 18 ) 19 20 func startRPCMock(dispatcher *gorpc.Dispatcher) *gorpc.Server { 21 22 rpc.GlobalRPCCallTimeout = 100 * time.Millisecond 23 24 globalConf := config.Global() 25 globalConf.SlaveOptions.UseRPC = true 26 globalConf.SlaveOptions.RPCKey = "test_org" 27 globalConf.SlaveOptions.APIKey = "test" 28 globalConf.Policies.PolicySource = "rpc" 29 globalConf.SlaveOptions.CallTimeout = 1 30 globalConf.SlaveOptions.RPCPoolSize = 2 31 globalConf.AuthOverride.ForceAuthProvider = true 32 globalConf.AuthOverride.AuthProvider.StorageEngine = "rpc" 33 34 server := gorpc.NewTCPServer("127.0.0.1:0", dispatcher.NewHandlerFunc()) 35 list := &customListener{} 36 server.Listener = list 37 server.LogError = gorpc.NilErrorLogger 38 39 if err := server.Start(); err != nil { 40 panic(err) 41 } 42 globalConf.SlaveOptions.ConnectionString = list.L.Addr().String() 43 44 config.SetGlobal(globalConf) 45 46 return server 47 } 48 49 func stopRPCMock(server *gorpc.Server) { 50 globalConf := config.Global() 51 globalConf.SlaveOptions.ConnectionString = "" 52 globalConf.SlaveOptions.RPCKey = "" 53 globalConf.SlaveOptions.APIKey = "" 54 globalConf.SlaveOptions.UseRPC = false 55 globalConf.Policies.PolicySource = "" 56 globalConf.AuthOverride.ForceAuthProvider = false 57 config.SetGlobal(globalConf) 58 59 if server != nil { 60 server.Listener.Close() 61 server.Stop() 62 } 63 64 rpc.Reset() 65 } 66 67 const apiDefListTest = `[{ 68 "api_id": "1", 69 "definition": { 70 "location": "header", 71 "key": "version" 72 }, 73 "auth": {"auth_header_name": "authorization"}, 74 "version_data": { 75 "versions": { 76 "v1": {"name": "v1"} 77 } 78 }, 79 "proxy": { 80 "listen_path": "/v1", 81 "target_url": "` + TestHttpAny + `" 82 } 83 }]` 84 85 const apiDefListTest2 = `[{ 86 "api_id": "1", 87 "definition": { 88 "location": "header", 89 "key": "version" 90 }, 91 "auth": {"auth_header_name": "authorization"}, 92 "version_data": { 93 "versions": { 94 "v1": {"name": "v1"} 95 } 96 }, 97 "proxy": { 98 "listen_path": "/v1", 99 "target_url": "` + TestHttpAny + `" 100 } 101 }, 102 { 103 "api_id": "2", 104 "definition": { 105 "location": "header", 106 "key": "version" 107 }, 108 "auth": {"auth_header_name": "authorization"}, 109 "version_data": { 110 "versions": { 111 "v2": {"name": "v2"} 112 } 113 }, 114 "proxy": { 115 "listen_path": "/v2", 116 "target_url": "` + TestHttpAny + `" 117 } 118 }]` 119 120 func TestSyncAPISpecsRPCFailure_CheckGlobals(t *testing.T) { 121 ts := StartTest() 122 defer ts.Close() 123 defer ResetTestConfig() 124 125 // Test RPC 126 callCount := 0 127 dispatcher := gorpc.NewDispatcher() 128 dispatcher.AddFunc("GetApiDefinitions", func(clientAddr string, dr *apidef.DefRequest) (string, error) { 129 if callCount == 0 { 130 callCount += 1 131 return `[]`, nil 132 } 133 134 if callCount == 1 { 135 callCount += 1 136 return apiDefListTest, nil 137 } 138 139 if callCount == 2 { 140 callCount += 1 141 return apiDefListTest2, nil 142 } 143 144 if callCount == 3 { 145 callCount += 1 146 return "malformed json", nil 147 } 148 149 // clean up 150 return `[]`, nil 151 }) 152 dispatcher.AddFunc("Login", func(clientAddr, userKey string) bool { 153 return true 154 }) 155 dispatcher.AddFunc("GetPolicies", func(orgId string) (string, error) { 156 return `[]`, nil 157 }) 158 159 rpc := startRPCMock(dispatcher) 160 defer stopRPCMock(rpc) 161 162 // Three cases: 1 API, 2 APIs and Malformed data 163 exp := []int{1, 4, 6, 6, 2} 164 if *cli.HTTPProfile { 165 exp = []int{4, 6, 8, 8, 4} 166 } 167 for _, e := range exp { 168 DoReload() 169 170 rtCnt := 0 171 mainRouter().Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { 172 rtCnt += 1 173 //fmt.Println(route.GetPathTemplate()) 174 return nil 175 }) 176 177 if rtCnt != e { 178 t.Errorf("There should be %v routes, got %v", e, rtCnt) 179 } 180 } 181 } 182 183 // Our RPC layer too racy, but not harmul, mostly global variables like RPCIsClientConnected 184 func TestSyncAPISpecsRPCFailure(t *testing.T) { 185 // Test RPC 186 dispatcher := gorpc.NewDispatcher() 187 dispatcher.AddFunc("GetApiDefinitions", func(clientAddr string, dr *apidef.DefRequest) (string, error) { 188 return "malformed json", nil 189 }) 190 dispatcher.AddFunc("Login", func(clientAddr, userKey string) bool { 191 return true 192 }) 193 194 rpc := startRPCMock(dispatcher) 195 defer stopRPCMock(rpc) 196 197 count, _ := syncAPISpecs() 198 if count != 0 { 199 t.Error("Should return empty value for malformed rpc response", apiSpecs) 200 } 201 } 202 203 func TestSyncAPISpecsRPCSuccess(t *testing.T) { 204 // Test RPC 205 dispatcher := gorpc.NewDispatcher() 206 dispatcher.AddFunc("GetApiDefinitions", func(clientAddr string, dr *apidef.DefRequest) (string, error) { 207 return jsonMarshalString(BuildAPI(func(spec *APISpec) { 208 spec.UseKeylessAccess = false 209 })), nil 210 }) 211 dispatcher.AddFunc("GetPolicies", func(clientAddr string, orgid string) (string, error) { 212 return `[{"_id":"507f191e810c19729de860ea", "rate":1, "per":1}]`, nil 213 }) 214 dispatcher.AddFunc("Login", func(clientAddr, userKey string) bool { 215 return true 216 }) 217 dispatcher.AddFunc("GetKey", func(clientAddr, key string) (string, error) { 218 return jsonMarshalString(CreateStandardSession()), nil 219 }) 220 221 t.Run("RPC is live", func(t *testing.T) { 222 rpc := startRPCMock(dispatcher) 223 defer stopRPCMock(rpc) 224 ts := StartTest() 225 defer ts.Close() 226 227 apiBackup, _ := LoadDefinitionsFromRPCBackup() 228 if len(apiBackup) != 1 { 229 t.Fatal("Should have APIs in backup") 230 } 231 232 policyBackup, _ := LoadPoliciesFromRPCBackup() 233 if len(policyBackup) != 1 { 234 t.Fatal("Should have Policies in backup") 235 } 236 237 authHeaders := map[string]string{"Authorization": "test"} 238 ts.Run(t, []test.TestCase{ 239 {Path: "/sample", Headers: authHeaders, Code: 200}, 240 }...) 241 242 count, _ := syncAPISpecs() 243 if count != 1 { 244 t.Error("Should return array with one spec", apiSpecs) 245 } 246 }) 247 248 t.Run("RPC down, cold start, load backup", func(t *testing.T) { 249 // Point rpc to non existent address 250 globalConf := config.Global() 251 globalConf.SlaveOptions.ConnectionString = testHttpFailure 252 globalConf.SlaveOptions.UseRPC = true 253 globalConf.SlaveOptions.RPCKey = "test_org" 254 globalConf.SlaveOptions.APIKey = "test" 255 globalConf.Policies.PolicySource = "rpc" 256 config.SetGlobal(globalConf) 257 258 // RPC layer is down 259 ts := StartTest() 260 defer ts.Close() 261 262 // Wait for backup to load 263 time.Sleep(100 * time.Millisecond) 264 ReloadTestCase.Tick() 265 time.Sleep(100 * time.Millisecond) 266 267 cachedAuth := map[string]string{"Authorization": "test"} 268 notCachedAuth := map[string]string{"Authorization": "nope1"} 269 // Stil works, since it knows about cached key 270 ts.Run(t, []test.TestCase{ 271 {Path: "/sample", Headers: cachedAuth, Code: 200}, 272 {Path: "/sample", Headers: notCachedAuth, Code: 403}, 273 }...) 274 275 stopRPCMock(nil) 276 }) 277 278 t.Run("RPC is back, hard reload", func(t *testing.T) { 279 rpc.ResetEmergencyMode() 280 281 dispatcher := gorpc.NewDispatcher() 282 dispatcher.AddFunc("GetApiDefinitions", func(clientAddr string, dr *apidef.DefRequest) (string, error) { 283 return jsonMarshalString(BuildAPI( 284 func(spec *APISpec) { spec.UseKeylessAccess = false }, 285 func(spec *APISpec) { spec.UseKeylessAccess = false }, 286 )), nil 287 }) 288 dispatcher.AddFunc("GetPolicies", func(clientAddr string, orgid string) (string, error) { 289 return `[{"_id":"507f191e810c19729de860ea", "rate":1, "per":1}, {"_id":"507f191e810c19729de860eb", "rate":1, "per":1}]`, nil 290 }) 291 dispatcher.AddFunc("Login", func(clientAddr, userKey string) bool { 292 return true 293 }) 294 dispatcher.AddFunc("GetKey", func(clientAddr, key string) (string, error) { 295 return jsonMarshalString(CreateStandardSession()), nil 296 }) 297 // Back to live 298 rpc := startRPCMock(dispatcher) 299 defer stopRPCMock(rpc) 300 ts := StartTest() 301 defer ts.Close() 302 303 time.Sleep(100 * time.Millisecond) 304 305 cachedAuth := map[string]string{"Authorization": "test"} 306 notCachedAuth := map[string]string{"Authorization": "nope2"} 307 ts.Run(t, []test.TestCase{ 308 {Path: "/sample", Headers: cachedAuth, Code: 200}, 309 {Path: "/sample", Headers: notCachedAuth, Code: 200}, 310 }...) 311 312 if count, _ := syncAPISpecs(); count != 2 { 313 t.Error("Should fetch latest specs", count) 314 } 315 316 if count, _ := syncPolicies(); count != 2 { 317 t.Error("Should fetch latest policies", count) 318 } 319 }) 320 321 t.Run("RPC is back, live reload", func(t *testing.T) { 322 rpc := startRPCMock(dispatcher) 323 ts := StartTest() 324 defer ts.Close() 325 326 time.Sleep(100 * time.Millisecond) 327 328 authHeaders := map[string]string{"Authorization": "test"} 329 ts.Run(t, []test.TestCase{ 330 {Path: "/sample", Headers: authHeaders, Code: 200}, 331 }...) 332 333 rpc.Listener.Close() 334 rpc.Stop() 335 336 cached := map[string]string{"Authorization": "test"} 337 notCached := map[string]string{"Authorization": "nope3"} 338 ts.Run(t, []test.TestCase{ 339 {Path: "/sample", Headers: cached, Code: 200}, 340 {Path: "/sample", Headers: notCached, Code: 403}, 341 }...) 342 343 // Dynamically restart RPC layer 344 rpc = gorpc.NewTCPServer(rpc.Listener.(*customListener).L.Addr().String(), dispatcher.NewHandlerFunc()) 345 list := &customListener{} 346 rpc.Listener = list 347 rpc.LogError = gorpc.NilErrorLogger 348 if err := rpc.Start(); err != nil { 349 panic(err) 350 } 351 defer stopRPCMock(rpc) 352 353 // Internal gorpc reconnect timeout is 1 second 354 time.Sleep(1000 * time.Millisecond) 355 356 notCached = map[string]string{"Authorization": "nope4"} 357 ts.Run(t, []test.TestCase{ 358 {Path: "/sample", Headers: notCached, Code: 200}, 359 }...) 360 }) 361 }