github.com/jbking/gohan@v0.0.0-20151217002006-b41ccf1c2a96/server/server_test.go (about) 1 // Copyright (C) 2015 NTT Innovation Institute, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 // implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package server_test 17 18 import ( 19 "bytes" 20 "encoding/json" 21 "fmt" 22 "io" 23 "net/http" 24 "strconv" 25 "strings" 26 "testing" 27 28 "github.com/coreos/go-etcd/etcd" 29 . "github.com/onsi/ginkgo" 30 . "github.com/onsi/gomega" 31 32 "github.com/cloudwan/gohan/db" 33 "github.com/cloudwan/gohan/db/transaction" 34 "github.com/cloudwan/gohan/schema" 35 srv "github.com/cloudwan/gohan/server" 36 gohan_sync "github.com/cloudwan/gohan/sync" 37 gohan_etcd "github.com/cloudwan/gohan/sync/etcd" 38 "github.com/cloudwan/gohan/util" 39 ) 40 41 var ( 42 server *srv.Server 43 baseURL = "http://localhost:19090" 44 schemaURL = baseURL + "/gohan/v0.1/schemas" 45 networkPluralURL = baseURL + "/v2.0/networks" 46 subnetPluralURL = baseURL + "/v2.0/subnets" 47 testPluralURL = baseURL + "/v2.0/tests" 48 ) 49 50 var _ = Describe("Server package test", func() { 51 52 AfterEach(func() { 53 tx, err := testDB.Begin() 54 Expect(err).ToNot(HaveOccurred(), "Failed to create transaction.") 55 defer tx.Close() 56 for _, schema := range schema.GetManager().Schemas() { 57 if whitelist[schema.ID] { 58 continue 59 } 60 err = clearTable(tx, schema) 61 Expect(err).ToNot(HaveOccurred(), "Failed to clear table.") 62 } 63 err = tx.Commit() 64 Expect(err).ToNot(HaveOccurred(), "Failed to commite transaction.") 65 }) 66 67 Describe("Http request", func() { 68 Context("with invalid request body", func() { 69 malformedRequestBody := "malformed" 70 71 It("should not create network", func() { 72 data := testURL("POST", networkPluralURL, adminTokenID, 73 malformedRequestBody, http.StatusBadRequest) 74 Expect(data).To(HaveKeyWithValue("error", ContainSubstring("parse data"))) 75 }) 76 77 It("should not update network", func() { 78 network := getNetwork("yellow", adminTenantID) 79 testURL("POST", networkPluralURL, adminTokenID, network, http.StatusCreated) 80 81 data := testURL("PUT", getNetworkSingularURL("yellow"), 82 adminTokenID, malformedRequestBody, http.StatusBadRequest) 83 Expect(data).To(HaveKeyWithValue("error", ContainSubstring("parse data"))) 84 }) 85 }) 86 87 Context("getting from baseURL", func() { 88 It("should return 404(Not Found)", func() { 89 testURL("GET", baseURL, adminTokenID, nil, http.StatusNotFound) 90 }) 91 }) 92 93 Context("getting networks while no networks", func() { 94 It("should return 200(OK) status code", func() { 95 testURL("GET", networkPluralURL, adminTokenID, nil, http.StatusOK) 96 }) 97 }) 98 99 It("should not authorize getting networks with no token", func() { 100 testURL("GET", networkPluralURL, "", nil, http.StatusUnauthorized) 101 }) 102 103 Context("having one network", func() { 104 var result interface{} 105 106 network := getNetwork("red", "red") 107 108 BeforeEach(func() { 109 result = testURL("POST", networkPluralURL, adminTokenID, network, http.StatusCreated) 110 Expect(result).To(HaveKeyWithValue("network", util.MatchAsJSON(network))) 111 }) 112 113 It("should get networks list", func() { 114 result = testURL("GET", networkPluralURL, adminTokenID, nil, http.StatusOK) 115 Expect(result).To(HaveKeyWithValue("networks", ConsistOf(util.MatchAsJSON(network)))) 116 }) 117 118 It("should get particular network", func() { 119 result = testURL("GET", getNetworkSingularURL("red"), adminTokenID, nil, http.StatusOK) 120 Expect(result).To(HaveKeyWithValue("network", util.MatchAsJSON(network))) 121 }) 122 123 It("should not get invalid network", func() { 124 testURL("GET", baseURL+"/v2.0/network/unknownID", adminTokenID, nil, http.StatusNotFound) 125 }) 126 127 It("should delete particular network", func() { 128 testURL("DELETE", getNetworkSingularURL("red"), adminTokenID, nil, http.StatusNoContent) 129 }) 130 131 Describe("updating network using PUT", func() { 132 networkUpdate := map[string]interface{}{ 133 "name": "NetworkRed2", 134 } 135 invalidNetwork := map[string]interface{}{ 136 "id": 10, 137 "name": "NetworkRed", 138 } 139 networkUpdated := network 140 networkUpdated["name"] = "NetworkRed2" 141 142 It("should not update network with invalid or the same network", func() { 143 testURL("PUT", getNetworkSingularURL("red"), adminTokenID, invalidNetwork, http.StatusBadRequest) 144 testURL("PUT", getNetworkSingularURL("red"), adminTokenID, network, http.StatusBadRequest) 145 }) 146 147 It("should update and get updated network", func() { 148 result = testURL("PUT", getNetworkSingularURL("red"), adminTokenID, networkUpdate, http.StatusOK) 149 Expect(result).To(HaveKeyWithValue("network", util.MatchAsJSON(networkUpdated))) 150 result = testURL("GET", getNetworkSingularURL("red"), adminTokenID, nil, http.StatusOK) 151 Expect(result).To(HaveKeyWithValue("network", util.MatchAsJSON(networkUpdated))) 152 }) 153 }) 154 155 Describe("updating network using PATCH", func() { 156 networkUpdate := map[string]interface{}{ 157 "name": "NetworkRed2", 158 } 159 invalidNetwork := map[string]interface{}{ 160 "id": 10, 161 "name": "NetworkRed", 162 } 163 networkUpdated := network 164 networkUpdated["name"] = "NetworkRed2" 165 166 It("should not update network with invalid or the same network", func() { 167 testURL("PATCH", getNetworkSingularURL("red"), adminTokenID, invalidNetwork, http.StatusBadRequest) 168 testURL("PATCH", getNetworkSingularURL("red"), adminTokenID, network, http.StatusBadRequest) 169 }) 170 171 It("should update and get updated network", func() { 172 result = testURL("PATCH", getNetworkSingularURL("red"), adminTokenID, networkUpdate, http.StatusOK) 173 By(fmt.Sprintf("%s", result)) 174 Expect(result).To(HaveKeyWithValue("network", util.MatchAsJSON(networkUpdated))) 175 result = testURL("GET", getNetworkSingularURL("red"), adminTokenID, nil, http.StatusOK) 176 Expect(result).To(HaveKeyWithValue("network", util.MatchAsJSON(networkUpdated))) 177 }) 178 }) 179 }) 180 181 Context("trying to create network with no tenant_id", func() { 182 It("should add adminTenantID as a default.", func() { 183 networkRed := getNetwork("red", "red") 184 delete(networkRed, "tenant_id") 185 186 data := testURL("POST", networkPluralURL, adminTokenID, networkRed, http.StatusCreated) 187 Expect(data).To(HaveKeyWithValue("network", HaveKeyWithValue("tenant_id", adminTenantID))) 188 }) 189 }) 190 }) 191 192 Describe("PaginationAndSorting", func() { 193 It("should work", func() { 194 By("creating 2 networks") 195 networkRed := getNetwork("red", "red") 196 testURL("POST", networkPluralURL, adminTokenID, networkRed, http.StatusCreated) 197 networkBlue := getNetwork("blue", "red") 198 testURL("POST", networkPluralURL, adminTokenID, networkBlue, http.StatusCreated) 199 200 By("assuring 2 networks were returned") 201 result := testURL("GET", networkPluralURL, adminTokenID, nil, http.StatusOK) 202 res := result.(map[string]interface{}) 203 networks := res["networks"].([]interface{}) 204 Expect(networks).To(HaveLen(2)) 205 206 By("assuring returned networks are sorted") 207 res = result.(map[string]interface{}) 208 networks = res["networks"].([]interface{}) 209 n0, n1 := networks[0].(map[string]interface{}), networks[1].(map[string]interface{}) 210 Expect(n0).To(HaveKeyWithValue("id", "networkblue")) 211 Expect(n1).To(HaveKeyWithValue("id", "networkred")) 212 213 By("assuring pagination works") 214 result = testURL("GET", networkPluralURL+"?limit=1&offset=1&sort_order=desc", adminTokenID, nil, http.StatusOK) 215 res = result.(map[string]interface{}) 216 networks = res["networks"].([]interface{}) 217 n0 = networks[0].(map[string]interface{}) 218 Expect(networks).To(HaveLen(1)) 219 Expect(n0).To(HaveKeyWithValue("id", "networkblue")) 220 221 result, resp := httpRequest("GET", networkPluralURL+"?limit=1&offset=1", adminTokenID, nil) 222 Expect(resp.StatusCode).To(Equal(http.StatusOK)) 223 res = result.(map[string]interface{}) 224 networks = res["networks"].([]interface{}) 225 n0 = networks[0].(map[string]interface{}) 226 Expect(networks).To(HaveLen(1)) 227 Expect(n0).To(HaveKeyWithValue("id", "networkred")) 228 229 testURL("GET", networkPluralURL+"?limit=-1", adminTokenID, nil, http.StatusBadRequest) 230 testURL("GET", networkPluralURL+"?offset=-1", adminTokenID, nil, http.StatusBadRequest) 231 testURL("GET", networkPluralURL+"?sort_key=bad_key", adminTokenID, nil, http.StatusBadRequest) 232 testURL("GET", networkPluralURL+"?sort_order=bad_order", adminTokenID, nil, http.StatusBadRequest) 233 234 Expect(resp.Header.Get("X-Total-Count")).To(Equal("2")) 235 testURL("DELETE", getNetworkSingularURL("red"), adminTokenID, nil, http.StatusNoContent) 236 testURL("DELETE", getNetworkSingularURL("blue"), adminTokenID, nil, http.StatusNoContent) 237 }) 238 }) 239 240 Describe("Subnets", func() { 241 It("should work", func() { 242 network := getNetwork("red", "red") 243 testURL("POST", networkPluralURL, adminTokenID, network, http.StatusCreated) 244 245 subnet := getSubnet("red", "red", "") 246 247 delete(subnet, "network_id") 248 249 var result interface{} 250 testURL("POST", subnetPluralURL, adminTokenID, subnet, http.StatusBadRequest) 251 result = testURL("POST", getSubnetFullPluralURL("red"), adminTokenID, subnet, http.StatusCreated) 252 253 subnet["network_id"] = "networkred" 254 Expect(result).To(HaveKeyWithValue("subnet", util.MatchAsJSON(subnet))) 255 256 result = testURL("GET", getSubnetSingularURL("red"), adminTokenID, subnet, http.StatusOK) 257 Expect(result).To(HaveKeyWithValue("subnet", util.MatchAsJSON(subnet))) 258 259 noCidrSubnet := getSubnet("NoCIDR", "red", "networkred") 260 delete(noCidrSubnet, "cidr") 261 testURL("POST", getSubnetFullPluralURL("red"), adminTokenID, noCidrSubnet, http.StatusBadRequest) 262 263 subnetUpdate := map[string]interface{}{ 264 "name": "subnetRed2", 265 } 266 testURL("PUT", getSubnetSingularURL("red"), adminTokenID, subnetUpdate, http.StatusOK) 267 268 testURL("DELETE", getNetworkSingularURL("red"), adminTokenID, nil, http.StatusConflict) 269 testURL("DELETE", getSubnetSingularURL("red"), adminTokenID, nil, http.StatusNoContent) 270 testURL("DELETE", getNetworkSingularURL("red"), adminTokenID, nil, http.StatusNoContent) 271 result = testURL("GET", networkPluralURL, adminTokenID, nil, http.StatusOK) 272 Expect(result).To(HaveKeyWithValue("networks", BeEmpty())) 273 testURL("DELETE", getNetworkSingularURL("red"), adminTokenID, nil, http.StatusNotFound) 274 }) 275 }) 276 277 Describe("NullableProperties", func() { 278 It("should work", func() { 279 network := getNetwork("red", "red") 280 testURL("POST", networkPluralURL, adminTokenID, network, http.StatusCreated) 281 282 // Create subnet with null name. Ensure it's not defaulted to "" 283 subnet := getSubnet("red", "red", "networkred") 284 subnet["name"] = nil 285 testURL("POST", subnetPluralURL, adminTokenID, subnet, http.StatusCreated) 286 287 result := testURL("GET", getSubnetSingularURL("red"), adminTokenID, nil, http.StatusOK) 288 Expect(result).To(HaveKey("subnet")) 289 Expect(result.(map[string]interface{})["subnet"]).To(HaveKeyWithValue("name", BeNil())) 290 291 subnetUpdateName := map[string]interface{}{ 292 "name": "Red network", 293 } 294 testURL("PUT", getSubnetSingularURL("red"), adminTokenID, subnetUpdateName, http.StatusOK) 295 result = testURL("GET", getSubnetSingularURL("red"), adminTokenID, nil, http.StatusOK) 296 Expect(result).To(HaveKeyWithValue("subnet", HaveKeyWithValue("name", subnetUpdateName["name"]))) 297 298 // Test setting nullable property to null 299 subnetUpdateNullName := map[string]interface{}{ 300 "name": nil, 301 } 302 testURL("PUT", getSubnetSingularURL("red"), adminTokenID, subnetUpdateNullName, http.StatusOK) 303 result = testURL("GET", getSubnetSingularURL("red"), adminTokenID, nil, http.StatusOK) 304 Expect(result).To(HaveKey("subnet")) 305 Expect(result.(map[string]interface{})["subnet"]).To(HaveKeyWithValue("name", BeNil())) 306 307 testURL("DELETE", getSubnetSingularURL("red"), adminTokenID, nil, http.StatusNoContent) 308 testURL("DELETE", getNetworkSingularURL("red"), adminTokenID, nil, http.StatusNoContent) 309 }) 310 }) 311 312 Describe("MemberToken", func() { 313 It("should work", func() { 314 network := getNetwork("red", "red") 315 316 testURL("GET", baseURL, memberTokenID, nil, http.StatusNotFound) 317 testURL("GET", networkPluralURL, memberTokenID, nil, http.StatusOK) 318 testURL("GET", networkPluralURL, "", nil, http.StatusUnauthorized) 319 testURL("GET", schemaURL, memberTokenID, nil, http.StatusOK) 320 321 network = map[string]interface{}{ 322 "id": "networkred", 323 "name": "Networkred", 324 } 325 networkExpected := map[string]interface{}{ 326 "id": "networkred", 327 "name": "Networkred", 328 "description": "", 329 "tenant_id": memberTenantID, 330 } 331 332 invalidNetwork := getNetwork("red", "demo") 333 invalidNetwork["tenant_id"] = "demo" 334 335 resultExpected := map[string]interface{}{ 336 "network": networkExpected, 337 } 338 339 testURL("POST", networkPluralURL, memberTokenID, invalidNetwork, http.StatusUnauthorized) 340 result := testURL("POST", networkPluralURL, memberTokenID, network, http.StatusCreated) 341 Expect(result).To(util.MatchAsJSON(resultExpected)) 342 343 result = testURL("GET", networkPluralURL, memberTokenID, nil, http.StatusOK) 344 Expect(result).To(HaveKeyWithValue("networks", ConsistOf(util.MatchAsJSON(networkExpected)))) 345 346 result = testURL("GET", getNetworkSingularURL("red"), memberTokenID, nil, http.StatusOK) 347 Expect(result).To(HaveKeyWithValue("network", networkExpected)) 348 349 result = testURL("GET", baseURL+"/_all", memberTokenID, nil, http.StatusOK) 350 Expect(result).To(HaveLen(3)) 351 Expect(result).To(HaveKeyWithValue("networks", []interface{}{networkExpected})) 352 Expect(result).To(HaveKey("schemas")) 353 Expect(result).To(HaveKey("tests")) 354 355 testURL("GET", baseURL+"/v2.0/network/unknownID", memberTokenID, nil, http.StatusNotFound) 356 357 testURL("POST", subnetPluralURL, memberTokenID, getSubnet("red", "red", "networkred"), http.StatusUnauthorized) 358 testURL("GET", getSubnetSingularURL("red"), memberTokenID, nil, http.StatusUnauthorized) 359 testURL("PUT", getSubnetSingularURL("red"), memberTokenID, getSubnet("red", "red", "networkred"), http.StatusUnauthorized) 360 361 testURL("PUT", getNetworkSingularURL("red"), memberTokenID, invalidNetwork, http.StatusUnauthorized) 362 testURL("PUT", getNetworkSingularURL("red"), memberTokenID, network, http.StatusBadRequest) 363 364 testURL("DELETE", getSubnetSingularURL("red"), memberTokenID, nil, http.StatusUnauthorized) 365 testURL("DELETE", getNetworkSingularURL("red"), memberTokenID, nil, http.StatusNoContent) 366 testURL("DELETE", getNetworkSingularURL("red"), memberTokenID, nil, http.StatusNotFound) 367 }) 368 }) 369 370 Describe("StringQueries", func() { 371 It("should work", func() { 372 testURL("POST", networkPluralURL, adminTokenID, getNetwork("red", "red"), http.StatusCreated) 373 testURL("POST", networkPluralURL, adminTokenID, getNetwork("red1", "red"), http.StatusCreated) 374 375 testURL("POST", networkPluralURL, adminTokenID, getNetwork("red2", "blue"), http.StatusCreated) 376 testURL("POST", networkPluralURL, adminTokenID, getNetwork("red3", "blue"), http.StatusCreated) 377 378 result := testURL("GET", networkPluralURL, adminTokenID, nil, http.StatusOK) 379 Expect(result).To(HaveKeyWithValue("networks", ConsistOf( 380 util.MatchAsJSON(getNetwork("red", "red")), 381 util.MatchAsJSON(getNetwork("red1", "red")), 382 util.MatchAsJSON(getNetwork("red2", "blue")), 383 util.MatchAsJSON(getNetwork("red3", "blue"))))) 384 385 result = testURL("GET", networkPluralURL+"?tenant_id=red", adminTokenID, nil, http.StatusOK) 386 Expect(result).To(HaveKeyWithValue("networks", ConsistOf( 387 util.MatchAsJSON(getNetwork("red", "red")), 388 util.MatchAsJSON(getNetwork("red1", "red"))))) 389 390 result = testURL("GET", networkPluralURL+"?id=networkred&id=networkred1", adminTokenID, nil, http.StatusOK) 391 Expect(result).To(HaveKeyWithValue("networks", ConsistOf( 392 util.MatchAsJSON(getNetwork("red", "red")), 393 util.MatchAsJSON(getNetwork("red1", "red"))))) 394 395 result = testURL("GET", networkPluralURL+"?id=networkred&id=networkred1&id=networkred2&tenant_id=blue", adminTokenID, nil, http.StatusOK) 396 Expect(result).To(HaveKeyWithValue("networks", ConsistOf( 397 util.MatchAsJSON(getNetwork("red2", "blue"))))) 398 testURL("DELETE", getNetworkSingularURL("red"), adminTokenID, nil, http.StatusNoContent) 399 testURL("DELETE", getNetworkSingularURL("red1"), adminTokenID, nil, http.StatusNoContent) 400 testURL("DELETE", getNetworkSingularURL("red2"), adminTokenID, nil, http.StatusNoContent) 401 testURL("DELETE", getNetworkSingularURL("red3"), adminTokenID, nil, http.StatusNoContent) 402 }) 403 }) 404 405 Describe("BoolQueries", func() { 406 It("should work", func() { 407 network1 := getNetwork("red1", "red") 408 network1["shared"] = true 409 network2 := getNetwork("red2", "red") 410 network2["shared"] = true 411 network3 := getNetwork("red3", "red") 412 network4 := getNetwork("red4", "red") 413 414 testURL("POST", networkPluralURL, adminTokenID, network1, http.StatusCreated) 415 testURL("POST", networkPluralURL, adminTokenID, network2, http.StatusCreated) 416 testURL("POST", networkPluralURL, adminTokenID, network3, http.StatusCreated) 417 testURL("POST", networkPluralURL, adminTokenID, network4, http.StatusCreated) 418 419 result := testURL("GET", networkPluralURL+"?shared=true", adminTokenID, nil, http.StatusOK) 420 Expect(result).To(HaveKeyWithValue("networks", ConsistOf( 421 util.MatchAsJSON(network1), 422 util.MatchAsJSON(network2)))) 423 result = testURL("GET", networkPluralURL+"?shared=True", adminTokenID, nil, http.StatusOK) 424 Expect(result).To(HaveKeyWithValue("networks", ConsistOf( 425 util.MatchAsJSON(network1), 426 util.MatchAsJSON(network2)))) 427 428 result = testURL("GET", networkPluralURL+"?shared=false", adminTokenID, nil, http.StatusOK) 429 Expect(result).To(HaveKeyWithValue("networks", ConsistOf( 430 util.MatchAsJSON(network3), 431 util.MatchAsJSON(network4)))) 432 result = testURL("GET", networkPluralURL+"?shared=False", adminTokenID, nil, http.StatusOK) 433 Expect(result).To(HaveKeyWithValue("networks", ConsistOf( 434 util.MatchAsJSON(network3), 435 util.MatchAsJSON(network4)))) 436 }) 437 }) 438 439 Describe("FullParentPath", func() { 440 It("should work", func() { 441 networkRed := getNetwork("red", "red") 442 networkBlue := getNetwork("blue", "red") 443 subnetRed := getSubnet("red", "red", "networkred") 444 subnetBlue := getSubnet("blue", "red", "networkred") 445 subnetYellow := getSubnet("yellow", "red", "networkblue") 446 447 testURL("POST", networkPluralURL, adminTokenID, networkRed, http.StatusCreated) 448 testURL("POST", networkPluralURL, adminTokenID, networkBlue, http.StatusCreated) 449 testURL("POST", getSubnetFullPluralURL("red"), adminTokenID, subnetRed, http.StatusCreated) 450 testURL("POST", getSubnetFullPluralURL("red"), adminTokenID, subnetBlue, http.StatusCreated) 451 testURL("POST", getSubnetFullPluralURL("blue"), adminTokenID, subnetYellow, http.StatusCreated) 452 result := testURL("GET", getSubnetFullPluralURL("red"), adminTokenID, nil, http.StatusOK) 453 Expect(result).To(HaveKeyWithValue("subnets", ConsistOf( 454 util.MatchAsJSON(subnetBlue), 455 util.MatchAsJSON(subnetRed)))) 456 457 subnetRed["name"] = "subnetRedUpdated" 458 459 testURL("PUT", getSubnetFullSingularURL("red", "red"), adminTokenID, map[string]interface{}{"name": "subnetRedUpdated"}, http.StatusOK) 460 result = testURL("GET", getSubnetFullPluralURL("red"), adminTokenID, nil, http.StatusOK) 461 Expect(result).To(HaveKeyWithValue("subnets", ConsistOf( 462 util.MatchAsJSON(subnetBlue), 463 util.MatchAsJSON(subnetRed)))) 464 465 testURL("DELETE", getSubnetFullSingularURL("red", "red"), adminTokenID, nil, http.StatusNoContent) 466 testURL("DELETE", getSubnetFullSingularURL("red", "blue"), adminTokenID, nil, http.StatusNoContent) 467 }) 468 }) 469 470 Describe("ExtensionErrorReporting", func() { 471 It("should work", func() { 472 dummyError := map[string]interface{}{ 473 "error": "Dummy error.", 474 } 475 result := testURL("POST", testPluralURL, adminTokenID, map[string]interface{}{ 476 "id": "dummyid", 477 }, 390) 478 Expect(result).To(util.MatchAsJSON(dummyError)) 479 }) 480 }) 481 482 Describe("WrappedResourceRequests", func() { 483 It("should work", func() { 484 testURL("GET", getNetworkSingularURL("cyan"), adminTokenID, nil, http.StatusNotFound) 485 486 network := getNetwork("cyan", adminTenantID) 487 wrappedRequest := map[string]interface{}{"network": network} 488 testURL("POST", networkPluralURL, adminTokenID, wrappedRequest, http.StatusCreated) 489 defer testURL("DELETE", getNetworkSingularURL("cyan"), adminTokenID, nil, http.StatusNoContent) 490 491 wrappedRequest = map[string]interface{}{ 492 "network": map[string]interface{}{ 493 "name": "UpdatedName", 494 }, 495 } 496 response := testURL("PUT", getNetworkSingularURL("cyan"), adminTokenID, wrappedRequest, http.StatusOK) 497 Expect(response).To(HaveKeyWithValue("network", HaveKeyWithValue("name", "UpdatedName"))) 498 }) 499 }) 500 501 Describe("ResourceSharing", func() { 502 It("should work", func() { 503 memberNetwork := map[string]interface{}{ 504 "id": "networkbeige", 505 "name": "Networkbeige", 506 "description": "The Beige Network", 507 "tenant_id": memberTenantID, 508 } 509 testURL("POST", networkPluralURL, powerUserTokenID, memberNetwork, http.StatusCreated) 510 511 powerUserNetwork := getNetwork("pink", powerUserTenantID) 512 testURL("POST", networkPluralURL, powerUserTokenID, powerUserNetwork, http.StatusCreated) 513 defer testURL("DELETE", getNetworkSingularURL("pink"), powerUserTokenID, nil, http.StatusNoContent) 514 515 expectedNetworks := []interface{}{ 516 HaveKeyWithValue("tenant_id", memberTenantID), 517 HaveKeyWithValue("tenant_id", powerUserTenantID), 518 } 519 memberNetworks := testURL("GET", networkPluralURL, memberTokenID, nil, http.StatusOK) 520 Expect(memberNetworks).To(HaveKeyWithValue("networks", ConsistOf(expectedNetworks...))) 521 powerUserNetworks := testURL("GET", networkPluralURL, powerUserTokenID, nil, http.StatusOK) 522 Expect(powerUserNetworks).To(HaveKeyWithValue("networks", ConsistOf(expectedNetworks...))) 523 524 pinkUpdate := map[string]interface{}{ 525 "description": "Updated Pink Network", 526 } 527 testURL("PUT", getNetworkSingularURL("pink"), memberTokenID, pinkUpdate, http.StatusOK) 528 beigeUpdate := map[string]interface{}{ 529 "description": "Updated Beige Network", 530 } 531 testURL("PUT", getNetworkSingularURL("beige"), powerUserTokenID, beigeUpdate, http.StatusOK) 532 533 testURL("DELETE", getNetworkSingularURL("pink"), memberTokenID, nil, http.StatusNotFound) 534 }) 535 }) 536 537 Describe("Sync", func() { 538 It("should work", func() { 539 manager := schema.GetManager() 540 networkSchema, _ := manager.Schema("network") 541 network := getNetwork("Red", "red") 542 networkResource, err := manager.LoadResource("network", network) 543 Expect(err).ToNot(HaveOccurred()) 544 testDB1 := &srv.DbSyncWrapper{DB: testDB} 545 tx, err := testDB1.Begin() 546 Expect(err).ToNot(HaveOccurred()) 547 Expect(tx.Create(networkResource)).To(Succeed()) 548 Expect(tx.Commit()).To(Succeed()) 549 tx.Close() 550 551 Expect(server.Sync()).To(Succeed()) 552 553 sync := gohan_etcd.NewSync(nil) 554 555 writtenConfigRaw, err := sync.Fetch("config/" + networkResource.Path()) 556 Expect(err).ToNot(HaveOccurred()) 557 writtenConfig, ok := writtenConfigRaw.(*etcd.Response) 558 Expect(ok).To(BeTrue()) 559 560 var configContentsRaw interface{} 561 Expect(json.Unmarshal([]byte(writtenConfig.Node.Value), &configContentsRaw)).To(Succeed()) 562 configContents, ok := configContentsRaw.(map[string]interface{}) 563 Expect(ok).To(BeTrue()) 564 Expect(configContents).To(HaveKeyWithValue("version", float64(1))) 565 var configNetworkRaw interface{} 566 Expect(json.Unmarshal([]byte(configContents["body"].(string)), &configNetworkRaw)).To(Succeed()) 567 configNetwork, ok := configNetworkRaw.(map[string]interface{}) 568 Expect(ok).To(BeTrue()) 569 Expect(configNetwork).To(util.MatchAsJSON(network)) 570 571 tx, err = testDB1.Begin() 572 Expect(err).ToNot(HaveOccurred()) 573 Expect(tx.Delete(networkSchema, networkResource.ID())).To(Succeed()) 574 Expect(tx.Commit()).To(Succeed()) 575 tx.Close() 576 577 Expect(server.Sync()).To(Succeed()) 578 579 _, err = sync.Fetch(networkResource.Path()) 580 Expect(err).To(HaveOccurred(), "Failed to sync db resource deletion to sync backend") 581 }) 582 }) 583 584 Describe("Updating the state", func() { 585 const ( 586 statePrefix = "/state" 587 monitoringPrefix = "/monitoring" 588 ) 589 var ( 590 networkSchema *schema.Schema 591 networkResource *schema.Resource 592 wrappedTestDB db.DB 593 possibleEvent gohan_sync.Event 594 ) 595 596 BeforeEach(func() { 597 manager := schema.GetManager() 598 var ok bool 599 networkSchema, ok = manager.Schema("network") 600 Expect(ok).To(BeTrue()) 601 network := getNetwork("Red", "red") 602 var err error 603 networkResource, err = manager.LoadResource("network", network) 604 Expect(err).ToNot(HaveOccurred()) 605 wrappedTestDB = &srv.DbSyncWrapper{DB: testDB} 606 tx, err := wrappedTestDB.Begin() 607 defer tx.Close() 608 Expect(err).ToNot(HaveOccurred()) 609 Expect(tx.Create(networkResource)).To(Succeed()) 610 Expect(tx.Commit()).To(Succeed()) 611 }) 612 613 Describe("Updating state", func() { 614 Context("Invoked correctly", func() { 615 It("Should work", func() { 616 possibleEvent = gohan_sync.Event{ 617 Action: "this is ignored here", 618 Data: map[string]interface{}{ 619 "version": float64(1), 620 "error": "", 621 "state": "Ni malvarmetas", 622 }, 623 Key: statePrefix + networkResource.Path(), 624 } 625 Expect(srv.StateUpdate(&possibleEvent, server)).To(Succeed()) 626 627 tx, err := wrappedTestDB.Begin() 628 Expect(err).ToNot(HaveOccurred()) 629 defer tx.Close() 630 afterState, err := tx.StateFetch(networkSchema, networkResource.ID(), nil) 631 Expect(err).ToNot(HaveOccurred()) 632 Expect(tx.Commit()).To(Succeed()) 633 Expect(afterState.ConfigVersion).To(Equal(int64(1))) 634 Expect(afterState.StateVersion).To(Equal(int64(1))) 635 Expect(afterState.State).To(Equal("Ni malvarmetas")) 636 Expect(afterState.Error).To(Equal("")) 637 Expect(afterState.Monitoring).To(Equal("")) 638 }) 639 640 It("Should ignore backwards updates", func() { 641 possibleEvent = gohan_sync.Event{ 642 Action: "this is ignored here", 643 Data: map[string]interface{}{ 644 "version": float64(1), 645 "error": "", 646 "state": "Ni malvarmetas", 647 }, 648 Key: statePrefix + networkResource.Path(), 649 } 650 Expect(srv.StateUpdate(&possibleEvent, server)).To(Succeed()) 651 possibleEvent = gohan_sync.Event{ 652 Action: "this is ignored here", 653 Data: map[string]interface{}{ 654 "version": float64(0), 655 "error": "", 656 "state": "Ni varmegas", 657 }, 658 Key: statePrefix + networkResource.Path(), 659 } 660 Expect(srv.StateUpdate(&possibleEvent, server)).To(Succeed()) 661 662 tx, err := wrappedTestDB.Begin() 663 Expect(err).ToNot(HaveOccurred()) 664 defer tx.Close() 665 afterState, err := tx.StateFetch(networkSchema, networkResource.ID(), nil) 666 Expect(err).ToNot(HaveOccurred()) 667 Expect(tx.Commit()).To(Succeed()) 668 Expect(afterState.ConfigVersion).To(Equal(int64(1))) 669 Expect(afterState.StateVersion).To(Equal(int64(1))) 670 Expect(afterState.State).To(Equal("Ni malvarmetas")) 671 Expect(afterState.Error).To(Equal("")) 672 Expect(afterState.Monitoring).To(Equal("")) 673 }) 674 675 It("Should ignore status updates beyond the most recent config version", func() { 676 possibleEvent = gohan_sync.Event{ 677 Action: "this is ignored here", 678 Data: map[string]interface{}{ 679 "version": float64(1), 680 "error": "", 681 "state": "Ni malvarmetas", 682 }, 683 Key: statePrefix + networkResource.Path(), 684 } 685 Expect(srv.StateUpdate(&possibleEvent, server)).To(Succeed()) 686 possibleEvent = gohan_sync.Event{ 687 Action: "this is ignored here", 688 Data: map[string]interface{}{ 689 "version": float64(1), 690 "error": "", 691 "state": "Ni varmegas", 692 }, 693 Key: statePrefix + networkResource.Path(), 694 } 695 Expect(srv.StateUpdate(&possibleEvent, server)).To(Succeed()) 696 697 tx, err := wrappedTestDB.Begin() 698 Expect(err).ToNot(HaveOccurred()) 699 defer tx.Close() 700 afterState, err := tx.StateFetch(networkSchema, networkResource.ID(), nil) 701 Expect(err).ToNot(HaveOccurred()) 702 Expect(tx.Commit()).To(Succeed()) 703 Expect(afterState.ConfigVersion).To(Equal(int64(1))) 704 Expect(afterState.StateVersion).To(Equal(int64(1))) 705 Expect(afterState.State).To(Equal("Ni malvarmetas")) 706 Expect(afterState.Error).To(Equal("")) 707 Expect(afterState.Monitoring).To(Equal("")) 708 }) 709 }) 710 711 Context("Invoked incorrectly", func() { 712 It("With wrong key should return nil", func() { 713 possibleEvent = gohan_sync.Event{ 714 Action: "this is ignored here", 715 Data: map[string]interface{}{ 716 "version": float64(1), 717 "error": "", 718 "state": "Ni malvarmetas", 719 }, 720 Key: statePrefix + strings.Replace(networkResource.Path(), "network", "netwerk", 1), 721 } 722 err := srv.StateUpdate(&possibleEvent, server) 723 Expect(err).ToNot(HaveOccurred()) 724 }) 725 726 It("With wrong resource ID should return the proper error", func() { 727 possibleEvent = gohan_sync.Event{ 728 Action: "this is ignored here", 729 Data: map[string]interface{}{ 730 "version": float64(1), 731 "error": "", 732 "state": "Ni malvarmetas", 733 }, 734 Key: statePrefix + networkResource.Path() + "malesta", 735 } 736 err := srv.StateUpdate(&possibleEvent, server) 737 Expect(err).To(MatchError(ContainSubstring("Failed to fetch"))) 738 }) 739 740 It("Without version should return the proper error", func() { 741 possibleEvent = gohan_sync.Event{ 742 Action: "this is ignored here", 743 Data: map[string]interface{}{ 744 "error": "", 745 "state": "Ni malvarmetas", 746 }, 747 Key: statePrefix + networkResource.Path(), 748 } 749 err := srv.StateUpdate(&possibleEvent, server) 750 Expect(err).To(MatchError(ContainSubstring("No version"))) 751 }) 752 }) 753 }) 754 755 Context("Updating the monitoring state", func() { 756 Context("Invoked correctly", func() { 757 It("Should work", func() { 758 possibleEvent = gohan_sync.Event{ 759 Action: "this is ignored here", 760 Data: map[string]interface{}{ 761 "version": float64(1), 762 "error": "", 763 "state": "Ni malvarmetas", 764 }, 765 Key: statePrefix + networkResource.Path(), 766 } 767 Expect(srv.StateUpdate(&possibleEvent, server)).To(Succeed()) 768 possibleEvent = gohan_sync.Event{ 769 Action: "this is ignored here", 770 Data: map[string]interface{}{ 771 "monitoring": "Ni rigardas tio", 772 }, 773 Key: monitoringPrefix + networkResource.Path(), 774 } 775 Expect(srv.MonitoringUpdate(&possibleEvent, server)).To(Succeed()) 776 777 tx, err := wrappedTestDB.Begin() 778 Expect(err).ToNot(HaveOccurred()) 779 defer tx.Close() 780 afterMonitoring, err := tx.StateFetch(networkSchema, networkResource.ID(), nil) 781 Expect(err).ToNot(HaveOccurred()) 782 Expect(tx.Commit()).To(Succeed()) 783 Expect(afterMonitoring.ConfigVersion).To(Equal(int64(1))) 784 Expect(afterMonitoring.StateVersion).To(Equal(int64(1))) 785 Expect(afterMonitoring.State).To(Equal("Ni malvarmetas")) 786 Expect(afterMonitoring.Error).To(Equal("")) 787 Expect(afterMonitoring.Monitoring).To(Equal("Ni rigardas tio")) 788 }) 789 790 It("Should igonre updates if state is not up to date", func() { 791 possibleEvent = gohan_sync.Event{ 792 Action: "this is ignored here", 793 Data: map[string]interface{}{ 794 "monitoring": "Ni rigardas tio", 795 }, 796 Key: monitoringPrefix + networkResource.Path(), 797 } 798 Expect(srv.MonitoringUpdate(&possibleEvent, server)).To(Succeed()) 799 800 tx, err := wrappedTestDB.Begin() 801 Expect(err).ToNot(HaveOccurred()) 802 defer tx.Close() 803 afterMonitoring, err := tx.StateFetch(networkSchema, networkResource.ID(), nil) 804 Expect(err).ToNot(HaveOccurred()) 805 Expect(tx.Commit()).To(Succeed()) 806 Expect(afterMonitoring.ConfigVersion).To(Equal(int64(1))) 807 Expect(afterMonitoring.StateVersion).To(Equal(int64(0))) 808 Expect(afterMonitoring.State).To(Equal("")) 809 Expect(afterMonitoring.Error).To(Equal("")) 810 Expect(afterMonitoring.Monitoring).To(Equal("")) 811 }) 812 }) 813 814 Context("Invoked incorrectly", func() { 815 It("With wrong key should return nil", func() { 816 possibleEvent = gohan_sync.Event{ 817 Action: "this is ignored here", 818 Data: map[string]interface{}{ 819 "monitoring": "Ni rigardas tio", 820 }, 821 Key: monitoringPrefix + strings.Replace(networkResource.Path(), "network", "netwerk", 1), 822 } 823 err := srv.MonitoringUpdate(&possibleEvent, server) 824 Expect(err).ToNot(HaveOccurred()) 825 }) 826 827 It("With wrong resource ID should return the proper error", func() { 828 possibleEvent = gohan_sync.Event{ 829 Action: "this is ignored here", 830 Data: map[string]interface{}{ 831 "monitoring": "Ni rigardas tio", 832 }, 833 Key: monitoringPrefix + networkResource.Path() + "malesta", 834 } 835 err := srv.MonitoringUpdate(&possibleEvent, server) 836 Expect(err).To(MatchError(ContainSubstring("Failed to fetch"))) 837 }) 838 839 It("Without monitoring should return the proper error", func() { 840 possibleEvent = gohan_sync.Event{ 841 Action: "this is ignored here", 842 Data: map[string]interface{}{ 843 "version": float64(1), 844 "error": "", 845 "state": "Ni malvarmetas", 846 }, 847 Key: statePrefix + networkResource.Path(), 848 } 849 Expect(srv.StateUpdate(&possibleEvent, server)).To(Succeed()) 850 possibleEvent = gohan_sync.Event{ 851 Action: "this is ignored here", 852 Data: map[string]interface{}{}, 853 Key: monitoringPrefix + networkResource.Path(), 854 } 855 err := srv.MonitoringUpdate(&possibleEvent, server) 856 Expect(err).To(MatchError(ContainSubstring("No monitoring"))) 857 }) 858 }) 859 }) 860 }) 861 862 Describe("Resource Actions", func() { 863 responderPluralURL := baseURL + "/v2.0/responders" 864 865 BeforeEach(func() { 866 responder := map[string]interface{}{ 867 "id": "r1", 868 "pattern": "Hello %s!", 869 "tenant_id": memberTenantID, 870 } 871 testURL("POST", responderPluralURL, adminTokenID, responder, http.StatusCreated) 872 }) 873 874 It("should work", func() { 875 testHelloAction := map[string]interface{}{ 876 "name": "Heisenberg", 877 } 878 879 result := testURL("POST", responderPluralURL+"/r1/hello", memberTokenID, testHelloAction, http.StatusOK) 880 Expect(result).To(Equal(map[string]interface{}{ 881 "output": "Hello, Heisenberg!", 882 })) 883 }) 884 885 It("should be unauthorized", func() { 886 testHiAction := map[string]interface{}{ 887 "name": "Heisenberg", 888 } 889 890 result := testURL("POST", responderPluralURL+"/r1/hi", memberTokenID, testHiAction, http.StatusUnauthorized) 891 Expect(result).To(HaveKey("error")) 892 }) 893 894 It("should be invalid requests", func() { 895 badAction1 := map[string]interface{}{} 896 result := testURL("POST", responderPluralURL+"/r1/hello", memberTokenID, badAction1, http.StatusBadRequest) 897 Expect(result).To(HaveKey("error")) 898 899 badAction2 := map[string]interface{}{ 900 "hello": "Heisenberg", 901 "hi": "Heisenberg", 902 } 903 result = testURL("POST", responderPluralURL+"/r1/hello", memberTokenID, badAction2, http.StatusBadRequest) 904 Expect(result).To(HaveKey("error")) 905 906 badAction3 := map[string]interface{}{ 907 "hello": map[string]interface{}{ 908 "familyName": "Heisenberg", 909 }, 910 } 911 result = testURL("POST", responderPluralURL+"/r1/hello", memberTokenID, badAction3, http.StatusBadRequest) 912 Expect(result).To(HaveKey("error")) 913 914 unknownAction := map[string]interface{}{ 915 "name": "Heisenberg", 916 } 917 result = testURL("POST", responderPluralURL+"/r1/dzien_dobry", memberTokenID, unknownAction, http.StatusNotFound) 918 919 result = testURL("POST", responderPluralURL+"/r1/dzien_dobry", adminTokenID, unknownAction, http.StatusNotFound) 920 }) 921 }) 922 }) 923 924 func BenchmarkPOSTAPI(b *testing.B) { 925 err := initBenchmarkDatabase() 926 if err != nil { 927 b.Fatal(err) 928 } 929 930 err = startTestServer("./server_test_mysql_config.yaml") 931 if err != nil { 932 b.Fatal(err) 933 } 934 935 b.ResetTimer() 936 for i := 0; i < b.N; i++ { 937 network := getNetwork("red"+strconv.Itoa(i), "red") 938 httpRequest("POST", networkPluralURL, adminTokenID, network) 939 } 940 } 941 942 func BenchmarkGETAPI(b *testing.B) { 943 err := initBenchmarkDatabase() 944 if err != nil { 945 b.Fatal(err) 946 } 947 948 err = startTestServer("./server_test_mysql_config.yaml") 949 if err != nil { 950 b.Fatal(err) 951 } 952 953 network := getNetwork("red", "red") 954 httpRequest("POST", networkPluralURL, adminTokenID, network) 955 956 b.ResetTimer() 957 for i := 0; i < b.N; i++ { 958 httpRequest("GET", getNetworkSingularURL("red"), adminTokenID, nil) 959 } 960 } 961 962 func initBenchmarkDatabase() error { 963 schema.ClearManager() 964 manager := schema.GetManager() 965 manager.LoadSchemasFromFiles("../tests/test_schema.yaml", "../etc/schema/gohan.json") 966 err := db.InitDBWithSchemas("mysql", "root@tcp(localhost:3306)/gohan_test", false, false) 967 if err != nil { 968 return err 969 } 970 return nil 971 } 972 973 func startTestServer(config string) error { 974 var err error 975 server, err = srv.NewServer(config) 976 if err != nil { 977 return err 978 } 979 go func() { 980 err := server.Start() 981 if err != nil { 982 return 983 } 984 }() 985 defer server.Stop() 986 return nil 987 } 988 989 func getNetwork(color string, tenant string) map[string]interface{} { 990 return map[string]interface{}{ 991 "id": "network" + color, 992 "name": "Network" + color, 993 "description": "The " + color + " Network", 994 "tenant_id": tenant, 995 "route_targets": []string{"1000:10000", "2000:20000"}, 996 "shared": false, 997 "providor_networks": map[string]interface{}{"segmentation_id": 12, "segmentation_type": "vlan"}, 998 } 999 } 1000 1001 func getSubnet(color string, tenant string, parent string) map[string]interface{} { 1002 return map[string]interface{}{ 1003 "id": "subnet" + color, 1004 "name": "Subnet" + color, 1005 "description": "The " + color + " Subnet", 1006 "tenant_id": tenant, 1007 "cidr": "10.0.0.0/24", 1008 "network_id": parent, 1009 } 1010 } 1011 1012 func getNetworkSingularURL(color string) string { 1013 s, _ := schema.GetManager().Schema("network") 1014 return baseURL + s.URL + "/network" + color 1015 } 1016 1017 func getSubnetSingularURL(color string) string { 1018 s, _ := schema.GetManager().Schema("subnet") 1019 return baseURL + s.URL + "/subnet" + color 1020 } 1021 1022 func getSubnetFullSingularURL(networkColor, subnetColor string) string { 1023 return getSubnetFullPluralURL(networkColor) + "/subnet" + subnetColor 1024 } 1025 1026 func getSubnetFullPluralURL(networkColor string) string { 1027 s, _ := schema.GetManager().Schema("network") 1028 return baseURL + s.URL + "/network" + networkColor + "/subnets" 1029 } 1030 1031 func testURL(method, url, token string, postData interface{}, expectedCode int) interface{} { 1032 data, resp := httpRequest(method, url, token, postData) 1033 jsonData, _ := json.MarshalIndent(data, "", " ") 1034 ExpectWithOffset(1, resp.StatusCode).To(Equal(expectedCode), string(jsonData)) 1035 return data 1036 } 1037 1038 func httpRequest(method, url, token string, postData interface{}) (interface{}, *http.Response) { 1039 client := &http.Client{} 1040 var reader io.Reader 1041 if postData != nil { 1042 jsonByte, err := json.Marshal(postData) 1043 Expect(err).ToNot(HaveOccurred()) 1044 reader = bytes.NewBuffer(jsonByte) 1045 } 1046 request, err := http.NewRequest(method, url, reader) 1047 Expect(err).ToNot(HaveOccurred()) 1048 request.Header.Set("X-Auth-Token", token) 1049 var data interface{} 1050 resp, err := client.Do(request) 1051 Expect(err).ToNot(HaveOccurred()) 1052 defer resp.Body.Close() 1053 decoder := json.NewDecoder(resp.Body) 1054 decoder.Decode(&data) 1055 return data, resp 1056 } 1057 1058 func clearTable(tx transaction.Transaction, s *schema.Schema) error { 1059 for _, schema := range schema.GetManager().Schemas() { 1060 if schema.ParentSchema == s { 1061 err := clearTable(tx, schema) 1062 if err != nil { 1063 return err 1064 } 1065 } 1066 } 1067 resources, _, err := tx.List(s, nil, nil) 1068 if err != nil { 1069 return err 1070 } 1071 for _, resource := range resources { 1072 err = tx.Delete(s, resource.ID()) 1073 if err != nil { 1074 return err 1075 } 1076 } 1077 return nil 1078 }