github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/state/prepared_query_test.go (about) 1 package state 2 3 import ( 4 "reflect" 5 "strings" 6 "testing" 7 8 "github.com/hashicorp/consul/agent/structs" 9 "github.com/hashicorp/go-memdb" 10 ) 11 12 func TestStateStore_PreparedQuery_isUUID(t *testing.T) { 13 cases := map[string]bool{ 14 "": false, 15 "nope": false, 16 "f004177f-2c28-83b7-4229-eacc25fe55d1": true, 17 "F004177F-2C28-83B7-4229-EACC25FE55D1": true, 18 "x004177f-2c28-83b7-4229-eacc25fe55d1": false, // Bad hex 19 "f004177f-xc28-83b7-4229-eacc25fe55d1": false, // Bad hex 20 "f004177f-2c28-x3b7-4229-eacc25fe55d1": false, // Bad hex 21 "f004177f-2c28-83b7-x229-eacc25fe55d1": false, // Bad hex 22 "f004177f-2c28-83b7-4229-xacc25fe55d1": false, // Bad hex 23 " f004177f-2c28-83b7-4229-eacc25fe55d1": false, // Leading whitespace 24 "f004177f-2c28-83b7-4229-eacc25fe55d1 ": false, // Trailing whitespace 25 } 26 for i := 0; i < 100; i++ { 27 cases[testUUID()] = true 28 } 29 30 for str, expected := range cases { 31 if actual := isUUID(str); actual != expected { 32 t.Fatalf("bad: '%s'", str) 33 } 34 } 35 } 36 37 func TestStateStore_PreparedQuerySet_PreparedQueryGet(t *testing.T) { 38 s := testStateStore(t) 39 40 // Querying with no results returns nil. 41 ws := memdb.NewWatchSet() 42 idx, res, err := s.PreparedQueryGet(ws, testUUID()) 43 if idx != 0 || res != nil || err != nil { 44 t.Fatalf("expected (0, nil, nil), got: (%d, %#v, %#v)", idx, res, err) 45 } 46 47 // Inserting a query with empty ID is disallowed. 48 if err := s.PreparedQuerySet(1, &structs.PreparedQuery{}); err == nil { 49 t.Fatalf("expected %#v, got: %#v", ErrMissingQueryID, err) 50 } 51 52 // Index is not updated if nothing is saved. 53 if idx := s.maxIndex("prepared-queries"); idx != 0 { 54 t.Fatalf("bad index: %d", idx) 55 } 56 if watchFired(ws) { 57 t.Fatalf("bad") 58 } 59 60 // Build a legit-looking query with the most basic options. 61 query := &structs.PreparedQuery{ 62 ID: testUUID(), 63 Session: "nope", 64 Service: structs.ServiceQuery{ 65 Service: "redis", 66 }, 67 } 68 69 // The set will still fail because the session is bogus. 70 err = s.PreparedQuerySet(1, query) 71 if err == nil || !strings.Contains(err.Error(), "failed session lookup") { 72 t.Fatalf("bad: %v", err) 73 } 74 75 // Index is not updated if nothing is saved. 76 if idx := s.maxIndex("prepared-queries"); idx != 0 { 77 t.Fatalf("bad index: %d", idx) 78 } 79 if watchFired(ws) { 80 t.Fatalf("bad") 81 } 82 83 // Now register the service and remove the bogus session. 84 testRegisterNode(t, s, 1, "foo") 85 testRegisterService(t, s, 2, "foo", "redis") 86 query.Session = "" 87 88 // This should go through. 89 if err := s.PreparedQuerySet(3, query); err != nil { 90 t.Fatalf("err: %s", err) 91 } 92 93 // Make sure the index got updated. 94 if idx := s.maxIndex("prepared-queries"); idx != 3 { 95 t.Fatalf("bad index: %d", idx) 96 } 97 if !watchFired(ws) { 98 t.Fatalf("bad") 99 } 100 101 // Read it back out and verify it. 102 expected := &structs.PreparedQuery{ 103 ID: query.ID, 104 Service: structs.ServiceQuery{ 105 Service: "redis", 106 }, 107 RaftIndex: structs.RaftIndex{ 108 CreateIndex: 3, 109 ModifyIndex: 3, 110 }, 111 } 112 ws = memdb.NewWatchSet() 113 idx, actual, err := s.PreparedQueryGet(ws, query.ID) 114 if err != nil { 115 t.Fatalf("err: %s", err) 116 } 117 if idx != 3 { 118 t.Fatalf("bad index: %d", idx) 119 } 120 if !reflect.DeepEqual(actual, expected) { 121 t.Fatalf("bad: %v", actual) 122 } 123 124 // Give it a name and set it again. 125 query.Name = "test-query" 126 if err := s.PreparedQuerySet(4, query); err != nil { 127 t.Fatalf("err: %s", err) 128 } 129 130 // Make sure the index got updated. 131 if idx := s.maxIndex("prepared-queries"); idx != 4 { 132 t.Fatalf("bad index: %d", idx) 133 } 134 if !watchFired(ws) { 135 t.Fatalf("bad") 136 } 137 138 // Read it back and verify the data was updated as well as the index. 139 expected.Name = "test-query" 140 expected.ModifyIndex = 4 141 ws = memdb.NewWatchSet() 142 idx, actual, err = s.PreparedQueryGet(ws, query.ID) 143 if err != nil { 144 t.Fatalf("err: %s", err) 145 } 146 if idx != 4 { 147 t.Fatalf("bad index: %d", idx) 148 } 149 if !reflect.DeepEqual(actual, expected) { 150 t.Fatalf("bad: %v", actual) 151 } 152 153 // Try to tie it to a bogus session. 154 query.Session = testUUID() 155 err = s.PreparedQuerySet(5, query) 156 if err == nil || !strings.Contains(err.Error(), "invalid session") { 157 t.Fatalf("bad: %v", err) 158 } 159 160 // Index is not updated if nothing is saved. 161 if idx := s.maxIndex("prepared-queries"); idx != 4 { 162 t.Fatalf("bad index: %d", idx) 163 } 164 if watchFired(ws) { 165 t.Fatalf("bad") 166 } 167 168 // Now make a session and try again. 169 session := &structs.Session{ 170 ID: query.Session, 171 Node: "foo", 172 } 173 if err := s.SessionCreate(5, session); err != nil { 174 t.Fatalf("err: %s", err) 175 } 176 if err := s.PreparedQuerySet(6, query); err != nil { 177 t.Fatalf("err: %s", err) 178 } 179 180 // Make sure the index got updated. 181 if idx := s.maxIndex("prepared-queries"); idx != 6 { 182 t.Fatalf("bad index: %d", idx) 183 } 184 if !watchFired(ws) { 185 t.Fatalf("bad") 186 } 187 188 // Read it back and verify the data was updated as well as the index. 189 expected.Session = query.Session 190 expected.ModifyIndex = 6 191 ws = memdb.NewWatchSet() 192 idx, actual, err = s.PreparedQueryGet(ws, query.ID) 193 if err != nil { 194 t.Fatalf("err: %s", err) 195 } 196 if idx != 6 { 197 t.Fatalf("bad index: %d", idx) 198 } 199 if !reflect.DeepEqual(actual, expected) { 200 t.Fatalf("bad: %v", actual) 201 } 202 203 // Try to register a query with the same name and make sure it fails. 204 { 205 evil := &structs.PreparedQuery{ 206 ID: testUUID(), 207 Name: query.Name, 208 Service: structs.ServiceQuery{ 209 Service: "redis", 210 }, 211 } 212 err := s.PreparedQuerySet(7, evil) 213 if err == nil || !strings.Contains(err.Error(), "aliases an existing query name") { 214 t.Fatalf("bad: %v", err) 215 } 216 217 // Sanity check to make sure it's not there. 218 idx, actual, err := s.PreparedQueryGet(nil, evil.ID) 219 if err != nil { 220 t.Fatalf("err: %s", err) 221 } 222 if idx != 6 { 223 t.Fatalf("bad index: %d", idx) 224 } 225 if actual != nil { 226 t.Fatalf("bad: %v", actual) 227 } 228 } 229 230 // Try to abuse the system by trying to register a query whose name 231 // aliases a real query ID. 232 { 233 evil := &structs.PreparedQuery{ 234 ID: testUUID(), 235 Name: query.ID, 236 Service: structs.ServiceQuery{ 237 Service: "redis", 238 }, 239 } 240 err := s.PreparedQuerySet(8, evil) 241 if err == nil || !strings.Contains(err.Error(), "aliases an existing query ID") { 242 t.Fatalf("bad: %v", err) 243 } 244 245 // Sanity check to make sure it's not there. 246 idx, actual, err := s.PreparedQueryGet(nil, evil.ID) 247 if err != nil { 248 t.Fatalf("err: %s", err) 249 } 250 if idx != 6 { 251 t.Fatalf("bad index: %d", idx) 252 } 253 if actual != nil { 254 t.Fatalf("bad: %v", actual) 255 } 256 } 257 258 // Try to register a template that squats on the existing query's name. 259 { 260 evil := &structs.PreparedQuery{ 261 ID: testUUID(), 262 Name: query.Name, 263 Template: structs.QueryTemplateOptions{ 264 Type: structs.QueryTemplateTypeNamePrefixMatch, 265 }, 266 Service: structs.ServiceQuery{ 267 Service: "redis", 268 }, 269 } 270 err := s.PreparedQuerySet(8, evil) 271 if err == nil || !strings.Contains(err.Error(), "aliases an existing query name") { 272 t.Fatalf("bad: %v", err) 273 } 274 275 // Sanity check to make sure it's not there. 276 idx, actual, err := s.PreparedQueryGet(nil, evil.ID) 277 if err != nil { 278 t.Fatalf("err: %s", err) 279 } 280 if idx != 6 { 281 t.Fatalf("bad index: %d", idx) 282 } 283 if actual != nil { 284 t.Fatalf("bad: %v", actual) 285 } 286 } 287 288 // Index is not updated if nothing is saved. 289 if idx := s.maxIndex("prepared-queries"); idx != 6 { 290 t.Fatalf("bad index: %d", idx) 291 } 292 if watchFired(ws) { 293 t.Fatalf("bad") 294 } 295 296 // Turn the query into a template with an empty name. 297 query.Name = "" 298 query.Template = structs.QueryTemplateOptions{ 299 Type: structs.QueryTemplateTypeNamePrefixMatch, 300 } 301 if err := s.PreparedQuerySet(9, query); err != nil { 302 t.Fatalf("err: %s", err) 303 } 304 305 // Make sure the index got updated. 306 if idx := s.maxIndex("prepared-queries"); idx != 9 { 307 t.Fatalf("bad index: %d", idx) 308 } 309 if !watchFired(ws) { 310 t.Fatalf("bad") 311 } 312 313 // Read it back and verify the data was updated as well as the index. 314 expected.Name = "" 315 expected.Template = structs.QueryTemplateOptions{ 316 Type: structs.QueryTemplateTypeNamePrefixMatch, 317 } 318 expected.ModifyIndex = 9 319 ws = memdb.NewWatchSet() 320 idx, actual, err = s.PreparedQueryGet(ws, query.ID) 321 if err != nil { 322 t.Fatalf("err: %s", err) 323 } 324 if idx != 9 { 325 t.Fatalf("bad index: %d", idx) 326 } 327 if !reflect.DeepEqual(actual, expected) { 328 t.Fatalf("bad: %v", actual) 329 } 330 331 // Try to register a template that squats on the empty prefix. 332 { 333 evil := &structs.PreparedQuery{ 334 ID: testUUID(), 335 Name: "", 336 Template: structs.QueryTemplateOptions{ 337 Type: structs.QueryTemplateTypeNamePrefixMatch, 338 }, 339 Service: structs.ServiceQuery{ 340 Service: "redis", 341 }, 342 } 343 err := s.PreparedQuerySet(10, evil) 344 if err == nil || !strings.Contains(err.Error(), "query template with an empty name already exists") { 345 t.Fatalf("bad: %v", err) 346 } 347 348 // Sanity check to make sure it's not there. 349 idx, actual, err := s.PreparedQueryGet(nil, evil.ID) 350 if err != nil { 351 t.Fatalf("err: %s", err) 352 } 353 if idx != 9 { 354 t.Fatalf("bad index: %d", idx) 355 } 356 if actual != nil { 357 t.Fatalf("bad: %v", actual) 358 } 359 } 360 361 // Give the query template a name. 362 query.Name = "prefix" 363 if err := s.PreparedQuerySet(11, query); err != nil { 364 t.Fatalf("err: %s", err) 365 } 366 367 // Make sure the index got updated. 368 if idx := s.maxIndex("prepared-queries"); idx != 11 { 369 t.Fatalf("bad index: %d", idx) 370 } 371 if !watchFired(ws) { 372 t.Fatalf("bad") 373 } 374 375 // Read it back and verify the data was updated as well as the index. 376 expected.Name = "prefix" 377 expected.ModifyIndex = 11 378 ws = memdb.NewWatchSet() 379 idx, actual, err = s.PreparedQueryGet(ws, query.ID) 380 if err != nil { 381 t.Fatalf("err: %s", err) 382 } 383 if idx != 11 { 384 t.Fatalf("bad index: %d", idx) 385 } 386 if !reflect.DeepEqual(actual, expected) { 387 t.Fatalf("bad: %v", actual) 388 } 389 390 // Try to register a template that squats on the prefix. 391 { 392 evil := &structs.PreparedQuery{ 393 ID: testUUID(), 394 Name: "prefix", 395 Template: structs.QueryTemplateOptions{ 396 Type: structs.QueryTemplateTypeNamePrefixMatch, 397 }, 398 Service: structs.ServiceQuery{ 399 Service: "redis", 400 }, 401 } 402 err := s.PreparedQuerySet(12, evil) 403 if err == nil || !strings.Contains(err.Error(), "aliases an existing query name") { 404 t.Fatalf("bad: %v", err) 405 } 406 407 // Sanity check to make sure it's not there. 408 idx, actual, err := s.PreparedQueryGet(nil, evil.ID) 409 if err != nil { 410 t.Fatalf("err: %s", err) 411 } 412 if idx != 11 { 413 t.Fatalf("bad index: %d", idx) 414 } 415 if actual != nil { 416 t.Fatalf("bad: %v", actual) 417 } 418 } 419 420 // Try to register a template that doesn't compile. 421 { 422 evil := &structs.PreparedQuery{ 423 ID: testUUID(), 424 Name: "legit-prefix", 425 Template: structs.QueryTemplateOptions{ 426 Type: structs.QueryTemplateTypeNamePrefixMatch, 427 }, 428 Service: structs.ServiceQuery{ 429 Service: "${nope", 430 }, 431 } 432 err := s.PreparedQuerySet(13, evil) 433 if err == nil || !strings.Contains(err.Error(), "failed compiling template") { 434 t.Fatalf("bad: %v", err) 435 } 436 437 // Sanity check to make sure it's not there. 438 idx, actual, err := s.PreparedQueryGet(nil, evil.ID) 439 if err != nil { 440 t.Fatalf("err: %s", err) 441 } 442 if idx != 11 { 443 t.Fatalf("bad index: %d", idx) 444 } 445 if actual != nil { 446 t.Fatalf("bad: %v", actual) 447 } 448 } 449 450 if watchFired(ws) { 451 t.Fatalf("bad") 452 } 453 } 454 455 func TestStateStore_PreparedQueryDelete(t *testing.T) { 456 s := testStateStore(t) 457 458 // Set up our test environment. 459 testRegisterNode(t, s, 1, "foo") 460 testRegisterService(t, s, 2, "foo", "redis") 461 462 // Create a new query. 463 query := &structs.PreparedQuery{ 464 ID: testUUID(), 465 Service: structs.ServiceQuery{ 466 Service: "redis", 467 }, 468 } 469 470 // Deleting a query that doesn't exist should be a no-op. 471 if err := s.PreparedQueryDelete(3, query.ID); err != nil { 472 t.Fatalf("err: %s", err) 473 } 474 475 // Index is not updated if nothing is saved. 476 if idx := s.maxIndex("prepared-queries"); idx != 0 { 477 t.Fatalf("bad index: %d", idx) 478 } 479 480 // Now add the query to the data store. 481 if err := s.PreparedQuerySet(3, query); err != nil { 482 t.Fatalf("err: %s", err) 483 } 484 485 // Make sure the index got updated. 486 if idx := s.maxIndex("prepared-queries"); idx != 3 { 487 t.Fatalf("bad index: %d", idx) 488 } 489 490 // Read it back out and verify it. 491 expected := &structs.PreparedQuery{ 492 ID: query.ID, 493 Service: structs.ServiceQuery{ 494 Service: "redis", 495 }, 496 RaftIndex: structs.RaftIndex{ 497 CreateIndex: 3, 498 ModifyIndex: 3, 499 }, 500 } 501 ws := memdb.NewWatchSet() 502 idx, actual, err := s.PreparedQueryGet(ws, query.ID) 503 if err != nil { 504 t.Fatalf("err: %s", err) 505 } 506 if idx != 3 { 507 t.Fatalf("bad index: %d", idx) 508 } 509 if !reflect.DeepEqual(actual, expected) { 510 t.Fatalf("bad: %v", actual) 511 } 512 513 // Now delete it. 514 if err := s.PreparedQueryDelete(4, query.ID); err != nil { 515 t.Fatalf("err: %s", err) 516 } 517 518 // Make sure the index got updated. 519 if idx := s.maxIndex("prepared-queries"); idx != 4 { 520 t.Fatalf("bad index: %d", idx) 521 } 522 if !watchFired(ws) { 523 t.Fatalf("bad") 524 } 525 526 // Sanity check to make sure it's not there. 527 idx, actual, err = s.PreparedQueryGet(nil, query.ID) 528 if err != nil { 529 t.Fatalf("err: %s", err) 530 } 531 if idx != 4 { 532 t.Fatalf("bad index: %d", idx) 533 } 534 if actual != nil { 535 t.Fatalf("bad: %v", actual) 536 } 537 } 538 539 func TestStateStore_PreparedQueryResolve(t *testing.T) { 540 s := testStateStore(t) 541 542 // Set up our test environment. 543 testRegisterNode(t, s, 1, "foo") 544 testRegisterService(t, s, 2, "foo", "redis") 545 546 // Create a new query. 547 query := &structs.PreparedQuery{ 548 ID: testUUID(), 549 Name: "my-test-query", 550 Service: structs.ServiceQuery{ 551 Service: "redis", 552 }, 553 } 554 555 // Try to lookup a query that's not there using something that looks 556 // like a real ID. 557 idx, actual, err := s.PreparedQueryResolve(query.ID, structs.QuerySource{}) 558 if err != nil { 559 t.Fatalf("err: %s", err) 560 } 561 if idx != 0 { 562 t.Fatalf("bad index: %d", idx) 563 } 564 if actual != nil { 565 t.Fatalf("bad: %v", actual) 566 } 567 568 // Try to lookup a query that's not there using something that looks 569 // like a name 570 idx, actual, err = s.PreparedQueryResolve(query.Name, structs.QuerySource{}) 571 if err != nil { 572 t.Fatalf("err: %s", err) 573 } 574 if idx != 0 { 575 t.Fatalf("bad index: %d", idx) 576 } 577 if actual != nil { 578 t.Fatalf("bad: %v", actual) 579 } 580 581 // Now actually insert the query. 582 if err := s.PreparedQuerySet(3, query); err != nil { 583 t.Fatalf("err: %s", err) 584 } 585 586 // Make sure the index got updated. 587 if idx := s.maxIndex("prepared-queries"); idx != 3 { 588 t.Fatalf("bad index: %d", idx) 589 } 590 591 // Read it back out using the ID and verify it. 592 expected := &structs.PreparedQuery{ 593 ID: query.ID, 594 Name: "my-test-query", 595 Service: structs.ServiceQuery{ 596 Service: "redis", 597 }, 598 RaftIndex: structs.RaftIndex{ 599 CreateIndex: 3, 600 ModifyIndex: 3, 601 }, 602 } 603 idx, actual, err = s.PreparedQueryResolve(query.ID, structs.QuerySource{}) 604 if err != nil { 605 t.Fatalf("err: %s", err) 606 } 607 if idx != 3 { 608 t.Fatalf("bad index: %d", idx) 609 } 610 if !reflect.DeepEqual(actual, expected) { 611 t.Fatalf("bad: %v", actual) 612 } 613 614 // Read it back using the name and verify it again. 615 idx, actual, err = s.PreparedQueryResolve(query.Name, structs.QuerySource{}) 616 if err != nil { 617 t.Fatalf("err: %s", err) 618 } 619 if idx != 3 { 620 t.Fatalf("bad index: %d", idx) 621 } 622 if !reflect.DeepEqual(actual, expected) { 623 t.Fatalf("bad: %v", actual) 624 } 625 626 // Make sure an empty lookup is well-behaved if there are actual queries 627 // in the state store. 628 idx, actual, err = s.PreparedQueryResolve("", structs.QuerySource{}) 629 if err != ErrMissingQueryID { 630 t.Fatalf("bad: %v ", err) 631 } 632 if idx != 0 { 633 t.Fatalf("bad index: %d", idx) 634 } 635 if actual != nil { 636 t.Fatalf("bad: %v", actual) 637 } 638 639 // Create two prepared query templates, one a longer prefix of the 640 // other. 641 tmpl1 := &structs.PreparedQuery{ 642 ID: testUUID(), 643 Name: "prod-", 644 Template: structs.QueryTemplateOptions{ 645 Type: structs.QueryTemplateTypeNamePrefixMatch, 646 }, 647 Service: structs.ServiceQuery{ 648 Service: "${name.suffix}", 649 }, 650 } 651 if err := s.PreparedQuerySet(4, tmpl1); err != nil { 652 t.Fatalf("err: %s", err) 653 } 654 tmpl2 := &structs.PreparedQuery{ 655 ID: testUUID(), 656 Name: "prod-redis", 657 Template: structs.QueryTemplateOptions{ 658 Type: structs.QueryTemplateTypeNamePrefixMatch, 659 Regexp: "^prod-(.*)$", 660 }, 661 Service: structs.ServiceQuery{ 662 Service: "${match(1)}-master", 663 }, 664 } 665 if err := s.PreparedQuerySet(5, tmpl2); err != nil { 666 t.Fatalf("err: %s", err) 667 } 668 669 // Resolve the less-specific prefix. 670 expected = &structs.PreparedQuery{ 671 ID: tmpl1.ID, 672 Name: "prod-", 673 Template: structs.QueryTemplateOptions{ 674 Type: structs.QueryTemplateTypeNamePrefixMatch, 675 }, 676 Service: structs.ServiceQuery{ 677 Service: "mongodb", 678 }, 679 RaftIndex: structs.RaftIndex{ 680 CreateIndex: 4, 681 ModifyIndex: 4, 682 }, 683 } 684 idx, actual, err = s.PreparedQueryResolve("prod-mongodb", structs.QuerySource{}) 685 if err != nil { 686 t.Fatalf("err: %s", err) 687 } 688 if idx != 5 { 689 t.Fatalf("bad index: %d", idx) 690 } 691 if !reflect.DeepEqual(actual, expected) { 692 t.Fatalf("bad: %v", actual) 693 } 694 695 // Now resolve the more specific prefix. 696 expected = &structs.PreparedQuery{ 697 ID: tmpl2.ID, 698 Name: "prod-redis", 699 Template: structs.QueryTemplateOptions{ 700 Type: structs.QueryTemplateTypeNamePrefixMatch, 701 Regexp: "^prod-(.*)$", 702 }, 703 Service: structs.ServiceQuery{ 704 Service: "redis-foobar-master", 705 }, 706 RaftIndex: structs.RaftIndex{ 707 CreateIndex: 5, 708 ModifyIndex: 5, 709 }, 710 } 711 idx, actual, err = s.PreparedQueryResolve("prod-redis-foobar", structs.QuerySource{}) 712 if err != nil { 713 t.Fatalf("err: %s", err) 714 } 715 if idx != 5 { 716 t.Fatalf("bad index: %d", idx) 717 } 718 if !reflect.DeepEqual(actual, expected) { 719 t.Fatalf("bad: %v", actual) 720 } 721 722 // Resolve an exact-match prefix. The output of this one doesn't match a 723 // sensical service name, but it still renders. 724 expected = &structs.PreparedQuery{ 725 ID: tmpl1.ID, 726 Name: "prod-", 727 Template: structs.QueryTemplateOptions{ 728 Type: structs.QueryTemplateTypeNamePrefixMatch, 729 }, 730 Service: structs.ServiceQuery{ 731 Service: "", 732 }, 733 RaftIndex: structs.RaftIndex{ 734 CreateIndex: 4, 735 ModifyIndex: 4, 736 }, 737 } 738 idx, actual, err = s.PreparedQueryResolve("prod-", structs.QuerySource{}) 739 if err != nil { 740 t.Fatalf("err: %s", err) 741 } 742 if idx != 5 { 743 t.Fatalf("bad index: %d", idx) 744 } 745 if !reflect.DeepEqual(actual, expected) { 746 t.Fatalf("bad: %v", actual) 747 } 748 749 // Make sure you can't run a prepared query template by ID, since that 750 // makes no sense. 751 _, _, err = s.PreparedQueryResolve(tmpl1.ID, structs.QuerySource{}) 752 if err == nil || !strings.Contains(err.Error(), "prepared query templates can only be resolved up by name") { 753 t.Fatalf("bad: %v", err) 754 } 755 } 756 757 func TestStateStore_PreparedQueryList(t *testing.T) { 758 s := testStateStore(t) 759 760 // Make sure nothing is returned for an empty query 761 ws := memdb.NewWatchSet() 762 idx, actual, err := s.PreparedQueryList(ws) 763 if err != nil { 764 t.Fatalf("err: %s", err) 765 } 766 if idx != 0 { 767 t.Fatalf("bad index: %d", idx) 768 } 769 if len(actual) != 0 { 770 t.Fatalf("bad: %v", actual) 771 } 772 773 // Set up our test environment. 774 testRegisterNode(t, s, 1, "foo") 775 testRegisterService(t, s, 2, "foo", "redis") 776 testRegisterService(t, s, 3, "foo", "mongodb") 777 778 // Create some queries. 779 queries := structs.PreparedQueries{ 780 &structs.PreparedQuery{ 781 ID: testUUID(), 782 Name: "alice", 783 Service: structs.ServiceQuery{ 784 Service: "redis", 785 }, 786 }, 787 &structs.PreparedQuery{ 788 ID: testUUID(), 789 Name: "bob", 790 Service: structs.ServiceQuery{ 791 Service: "mongodb", 792 }, 793 }, 794 } 795 796 // Force the sort order of the UUIDs before we create them so the 797 // order is deterministic. 798 queries[0].ID = "a" + queries[0].ID[1:] 799 queries[1].ID = "b" + queries[1].ID[1:] 800 801 // Now create the queries. 802 for i, query := range queries { 803 if err := s.PreparedQuerySet(uint64(4+i), query); err != nil { 804 t.Fatalf("err: %s", err) 805 } 806 } 807 if !watchFired(ws) { 808 t.Fatalf("bad") 809 } 810 811 // Read it back and verify. 812 expected := structs.PreparedQueries{ 813 &structs.PreparedQuery{ 814 ID: queries[0].ID, 815 Name: "alice", 816 Service: structs.ServiceQuery{ 817 Service: "redis", 818 }, 819 RaftIndex: structs.RaftIndex{ 820 CreateIndex: 4, 821 ModifyIndex: 4, 822 }, 823 }, 824 &structs.PreparedQuery{ 825 ID: queries[1].ID, 826 Name: "bob", 827 Service: structs.ServiceQuery{ 828 Service: "mongodb", 829 }, 830 RaftIndex: structs.RaftIndex{ 831 CreateIndex: 5, 832 ModifyIndex: 5, 833 }, 834 }, 835 } 836 idx, actual, err = s.PreparedQueryList(nil) 837 if err != nil { 838 t.Fatalf("err: %s", err) 839 } 840 if idx != 5 { 841 t.Fatalf("bad index: %d", idx) 842 } 843 if !reflect.DeepEqual(actual, expected) { 844 t.Fatalf("bad: %v", actual) 845 } 846 } 847 848 func TestStateStore_PreparedQuery_Snapshot_Restore(t *testing.T) { 849 s := testStateStore(t) 850 851 // Set up our test environment. 852 testRegisterNode(t, s, 1, "foo") 853 testRegisterService(t, s, 2, "foo", "redis") 854 testRegisterService(t, s, 3, "foo", "mongodb") 855 856 // Create some queries. 857 queries := structs.PreparedQueries{ 858 &structs.PreparedQuery{ 859 ID: testUUID(), 860 Name: "alice", 861 Service: structs.ServiceQuery{ 862 Service: "redis", 863 }, 864 }, 865 &structs.PreparedQuery{ 866 ID: testUUID(), 867 Name: "bob-", 868 Template: structs.QueryTemplateOptions{ 869 Type: structs.QueryTemplateTypeNamePrefixMatch, 870 }, 871 Service: structs.ServiceQuery{ 872 Service: "${name.suffix}", 873 }, 874 }, 875 } 876 877 // Force the sort order of the UUIDs before we create them so the 878 // order is deterministic. 879 queries[0].ID = "a" + queries[0].ID[1:] 880 queries[1].ID = "b" + queries[1].ID[1:] 881 882 // Now create the queries. 883 for i, query := range queries { 884 if err := s.PreparedQuerySet(uint64(4+i), query); err != nil { 885 t.Fatalf("err: %s", err) 886 } 887 } 888 889 // Snapshot the queries. 890 snap := s.Snapshot() 891 defer snap.Close() 892 893 // Alter the real state store. 894 if err := s.PreparedQueryDelete(6, queries[0].ID); err != nil { 895 t.Fatalf("err: %s", err) 896 } 897 898 // Verify the snapshot. 899 if idx := snap.LastIndex(); idx != 5 { 900 t.Fatalf("bad index: %d", idx) 901 } 902 expected := structs.PreparedQueries{ 903 &structs.PreparedQuery{ 904 ID: queries[0].ID, 905 Name: "alice", 906 Service: structs.ServiceQuery{ 907 Service: "redis", 908 }, 909 RaftIndex: structs.RaftIndex{ 910 CreateIndex: 4, 911 ModifyIndex: 4, 912 }, 913 }, 914 &structs.PreparedQuery{ 915 ID: queries[1].ID, 916 Name: "bob-", 917 Template: structs.QueryTemplateOptions{ 918 Type: structs.QueryTemplateTypeNamePrefixMatch, 919 }, 920 Service: structs.ServiceQuery{ 921 Service: "${name.suffix}", 922 }, 923 RaftIndex: structs.RaftIndex{ 924 CreateIndex: 5, 925 ModifyIndex: 5, 926 }, 927 }, 928 } 929 dump, err := snap.PreparedQueries() 930 if err != nil { 931 t.Fatalf("err: %s", err) 932 } 933 if !reflect.DeepEqual(dump, expected) { 934 t.Fatalf("bad: %v", dump) 935 } 936 937 // Restore the values into a new state store. 938 func() { 939 s := testStateStore(t) 940 restore := s.Restore() 941 for _, query := range dump { 942 if err := restore.PreparedQuery(query); err != nil { 943 t.Fatalf("err: %s", err) 944 } 945 } 946 restore.Commit() 947 948 // Read the restored queries back out and verify that they 949 // match. 950 idx, actual, err := s.PreparedQueryList(nil) 951 if err != nil { 952 t.Fatalf("err: %s", err) 953 } 954 if idx != 5 { 955 t.Fatalf("bad index: %d", idx) 956 } 957 if !reflect.DeepEqual(actual, expected) { 958 t.Fatalf("bad: %v", actual) 959 } 960 961 // Make sure the second query, which is a template, was compiled 962 // and can be resolved. 963 _, query, err := s.PreparedQueryResolve("bob-backwards-is-bob", structs.QuerySource{}) 964 if err != nil { 965 t.Fatalf("err: %s", err) 966 } 967 if query == nil { 968 t.Fatalf("should have resolved the query") 969 } 970 if query.Service.Service != "backwards-is-bob" { 971 t.Fatalf("bad: %s", query.Service.Service) 972 } 973 }() 974 }