vitess.io/vitess@v0.16.2/go/vt/vtadmin/api_authz_test.go (about) 1 // Code generated by testutil/authztestgen. DO NOT EDIT. 2 3 /* 4 Copyright 2022 The Vitess Authors. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package vtadmin_test 20 21 import ( 22 "context" 23 "fmt" 24 "testing" 25 "time" 26 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 30 "vitess.io/vitess/go/vt/topo/topoproto" 31 "vitess.io/vitess/go/vt/vtadmin" 32 "vitess.io/vitess/go/vt/vtadmin/cluster" 33 "vitess.io/vitess/go/vt/vtadmin/rbac" 34 "vitess.io/vitess/go/vt/vtadmin/testutil" 35 "vitess.io/vitess/go/vt/vtadmin/vtctldclient/fakevtctldclient" 36 37 logutilpb "vitess.io/vitess/go/vt/proto/logutil" 38 mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl" 39 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 40 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 41 vschemapb "vitess.io/vitess/go/vt/proto/vschema" 42 vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin" 43 vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" 44 ) 45 46 func TestCreateKeyspace(t *testing.T) { 47 t.Parallel() 48 49 opts := vtadmin.Options{ 50 RBAC: &rbac.Config{ 51 Rules: []*struct { 52 Resource string 53 Actions []string 54 Subjects []string 55 Clusters []string 56 }{ 57 { 58 Resource: "Keyspace", 59 Actions: []string{"create"}, 60 Subjects: []string{"user:allowed"}, 61 Clusters: []string{"*"}, 62 }, 63 }, 64 }, 65 } 66 err := opts.RBAC.Reify() 67 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 68 69 api := vtadmin.NewAPI(testClusters(t), opts) 70 t.Cleanup(func() { 71 if err := api.Close(); err != nil { 72 t.Logf("api did not close cleanly: %s", err.Error()) 73 } 74 }) 75 76 t.Run("unauthorized actor", func(t *testing.T) { 77 t.Parallel() 78 79 actor := &rbac.Actor{Name: "other"} 80 ctx := context.Background() 81 if actor != nil { 82 ctx = rbac.NewContext(ctx, actor) 83 } 84 85 resp, err := api.CreateKeyspace(ctx, &vtadminpb.CreateKeyspaceRequest{ 86 ClusterId: "test", 87 Options: &vtctldatapb.CreateKeyspaceRequest{ 88 Name: "test", 89 }, 90 }) 91 assert.Error(t, err, "actor %+v should not be permitted to CreateKeyspace", actor) 92 assert.Nil(t, resp, "actor %+v should not be permitted to CreateKeyspace", actor) 93 }) 94 95 t.Run("authorized actor", func(t *testing.T) { 96 t.Parallel() 97 98 actor := &rbac.Actor{Name: "allowed"} 99 ctx := context.Background() 100 if actor != nil { 101 ctx = rbac.NewContext(ctx, actor) 102 } 103 104 resp, err := api.CreateKeyspace(ctx, &vtadminpb.CreateKeyspaceRequest{ 105 ClusterId: "test", 106 Options: &vtctldatapb.CreateKeyspaceRequest{ 107 Name: "test", 108 }, 109 }) 110 require.NoError(t, err) 111 assert.NotNil(t, resp, "actor %+v should be permitted to CreateKeyspace", actor) 112 }) 113 } 114 115 func TestCreateShard(t *testing.T) { 116 t.Parallel() 117 118 opts := vtadmin.Options{ 119 RBAC: &rbac.Config{ 120 Rules: []*struct { 121 Resource string 122 Actions []string 123 Subjects []string 124 Clusters []string 125 }{ 126 { 127 Resource: "Shard", 128 Actions: []string{"create"}, 129 Subjects: []string{"user:allowed"}, 130 Clusters: []string{"*"}, 131 }, 132 }, 133 }, 134 } 135 err := opts.RBAC.Reify() 136 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 137 138 api := vtadmin.NewAPI(testClusters(t), opts) 139 t.Cleanup(func() { 140 if err := api.Close(); err != nil { 141 t.Logf("api did not close cleanly: %s", err.Error()) 142 } 143 }) 144 145 t.Run("unauthorized actor", func(t *testing.T) { 146 t.Parallel() 147 148 actor := &rbac.Actor{Name: "other"} 149 ctx := context.Background() 150 if actor != nil { 151 ctx = rbac.NewContext(ctx, actor) 152 } 153 154 resp, err := api.CreateShard(ctx, &vtadminpb.CreateShardRequest{ 155 ClusterId: "test", 156 Options: &vtctldatapb.CreateShardRequest{ 157 Keyspace: "test", 158 ShardName: "-", 159 }, 160 }) 161 assert.Error(t, err, "actor %+v should not be permitted to CreateShard", actor) 162 assert.Nil(t, resp, "actor %+v should not be permitted to CreateShard", actor) 163 }) 164 165 t.Run("authorized actor", func(t *testing.T) { 166 t.Parallel() 167 168 actor := &rbac.Actor{Name: "allowed"} 169 ctx := context.Background() 170 if actor != nil { 171 ctx = rbac.NewContext(ctx, actor) 172 } 173 174 resp, err := api.CreateShard(ctx, &vtadminpb.CreateShardRequest{ 175 ClusterId: "test", 176 Options: &vtctldatapb.CreateShardRequest{ 177 Keyspace: "test", 178 ShardName: "-", 179 }, 180 }) 181 require.NoError(t, err) 182 assert.NotNil(t, resp, "actor %+v should be permitted to CreateShard", actor) 183 }) 184 } 185 186 func TestDeleteKeyspace(t *testing.T) { 187 t.Parallel() 188 189 opts := vtadmin.Options{ 190 RBAC: &rbac.Config{ 191 Rules: []*struct { 192 Resource string 193 Actions []string 194 Subjects []string 195 Clusters []string 196 }{ 197 { 198 Resource: "Keyspace", 199 Actions: []string{"delete"}, 200 Subjects: []string{"user:allowed"}, 201 Clusters: []string{"*"}, 202 }, 203 }, 204 }, 205 } 206 err := opts.RBAC.Reify() 207 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 208 209 api := vtadmin.NewAPI(testClusters(t), opts) 210 t.Cleanup(func() { 211 if err := api.Close(); err != nil { 212 t.Logf("api did not close cleanly: %s", err.Error()) 213 } 214 }) 215 216 t.Run("unauthorized actor", func(t *testing.T) { 217 t.Parallel() 218 219 actor := &rbac.Actor{Name: "other"} 220 ctx := context.Background() 221 if actor != nil { 222 ctx = rbac.NewContext(ctx, actor) 223 } 224 225 resp, err := api.DeleteKeyspace(ctx, &vtadminpb.DeleteKeyspaceRequest{ 226 ClusterId: "test", 227 Options: &vtctldatapb.DeleteKeyspaceRequest{ 228 Keyspace: "test", 229 }, 230 }) 231 assert.Error(t, err, "actor %+v should not be permitted to DeleteKeyspace", actor) 232 assert.Nil(t, resp, "actor %+v should not be permitted to DeleteKeyspace", actor) 233 }) 234 235 t.Run("authorized actor", func(t *testing.T) { 236 t.Parallel() 237 238 actor := &rbac.Actor{Name: "allowed"} 239 ctx := context.Background() 240 if actor != nil { 241 ctx = rbac.NewContext(ctx, actor) 242 } 243 244 resp, err := api.DeleteKeyspace(ctx, &vtadminpb.DeleteKeyspaceRequest{ 245 ClusterId: "test", 246 Options: &vtctldatapb.DeleteKeyspaceRequest{ 247 Keyspace: "test", 248 }, 249 }) 250 require.NoError(t, err) 251 assert.NotNil(t, resp, "actor %+v should be permitted to DeleteKeyspace", actor) 252 }) 253 } 254 255 func TestDeleteShards(t *testing.T) { 256 t.Parallel() 257 258 opts := vtadmin.Options{ 259 RBAC: &rbac.Config{ 260 Rules: []*struct { 261 Resource string 262 Actions []string 263 Subjects []string 264 Clusters []string 265 }{ 266 { 267 Resource: "Shard", 268 Actions: []string{"delete"}, 269 Subjects: []string{"user:allowed"}, 270 Clusters: []string{"*"}, 271 }, 272 }, 273 }, 274 } 275 err := opts.RBAC.Reify() 276 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 277 278 api := vtadmin.NewAPI(testClusters(t), opts) 279 t.Cleanup(func() { 280 if err := api.Close(); err != nil { 281 t.Logf("api did not close cleanly: %s", err.Error()) 282 } 283 }) 284 285 t.Run("unauthorized actor", func(t *testing.T) { 286 t.Parallel() 287 288 actor := &rbac.Actor{Name: "other"} 289 ctx := context.Background() 290 if actor != nil { 291 ctx = rbac.NewContext(ctx, actor) 292 } 293 294 resp, err := api.DeleteShards(ctx, &vtadminpb.DeleteShardsRequest{ 295 ClusterId: "test", 296 Options: &vtctldatapb.DeleteShardsRequest{ 297 Shards: []*vtctldatapb.Shard{ 298 { 299 Keyspace: "test", 300 Name: "-", 301 }, 302 }, 303 }, 304 }) 305 assert.Error(t, err, "actor %+v should not be permitted to DeleteShards", actor) 306 assert.Nil(t, resp, "actor %+v should not be permitted to DeleteShards", actor) 307 }) 308 309 t.Run("authorized actor", func(t *testing.T) { 310 t.Parallel() 311 312 actor := &rbac.Actor{Name: "allowed"} 313 ctx := context.Background() 314 if actor != nil { 315 ctx = rbac.NewContext(ctx, actor) 316 } 317 318 resp, err := api.DeleteShards(ctx, &vtadminpb.DeleteShardsRequest{ 319 ClusterId: "test", 320 Options: &vtctldatapb.DeleteShardsRequest{ 321 Shards: []*vtctldatapb.Shard{ 322 { 323 Keyspace: "test", 324 Name: "-", 325 }, 326 }, 327 }, 328 }) 329 require.NoError(t, err) 330 assert.NotNil(t, resp, "actor %+v should be permitted to DeleteShards", actor) 331 }) 332 } 333 334 func TestDeleteTablet(t *testing.T) { 335 t.Parallel() 336 337 opts := vtadmin.Options{ 338 RBAC: &rbac.Config{ 339 Rules: []*struct { 340 Resource string 341 Actions []string 342 Subjects []string 343 Clusters []string 344 }{ 345 { 346 Resource: "Tablet", 347 Actions: []string{"delete"}, 348 Subjects: []string{"user:allowed"}, 349 Clusters: []string{"*"}, 350 }, 351 }, 352 }, 353 } 354 err := opts.RBAC.Reify() 355 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 356 357 api := vtadmin.NewAPI(testClusters(t), opts) 358 t.Cleanup(func() { 359 if err := api.Close(); err != nil { 360 t.Logf("api did not close cleanly: %s", err.Error()) 361 } 362 }) 363 364 t.Run("unauthorized actor", func(t *testing.T) { 365 t.Parallel() 366 367 actor := &rbac.Actor{Name: "other"} 368 ctx := context.Background() 369 if actor != nil { 370 ctx = rbac.NewContext(ctx, actor) 371 } 372 373 resp, err := api.DeleteTablet(ctx, &vtadminpb.DeleteTabletRequest{ 374 ClusterIds: []string{"test"}, 375 Alias: &topodatapb.TabletAlias{ 376 Cell: "zone1", 377 Uid: 100, 378 }, 379 }) 380 assert.Error(t, err, "actor %+v should not be permitted to DeleteTablet", actor) 381 assert.Nil(t, resp, "actor %+v should not be permitted to DeleteTablet", actor) 382 }) 383 384 t.Run("authorized actor", func(t *testing.T) { 385 t.Parallel() 386 387 actor := &rbac.Actor{Name: "allowed"} 388 ctx := context.Background() 389 if actor != nil { 390 ctx = rbac.NewContext(ctx, actor) 391 } 392 393 resp, err := api.DeleteTablet(ctx, &vtadminpb.DeleteTabletRequest{ 394 ClusterIds: []string{"test"}, 395 Alias: &topodatapb.TabletAlias{ 396 Cell: "zone1", 397 Uid: 100, 398 }, 399 }) 400 require.NoError(t, err) 401 assert.NotNil(t, resp, "actor %+v should be permitted to DeleteTablet", actor) 402 }) 403 } 404 405 func TestEmergencyFailoverShard(t *testing.T) { 406 t.Parallel() 407 408 opts := vtadmin.Options{ 409 RBAC: &rbac.Config{ 410 Rules: []*struct { 411 Resource string 412 Actions []string 413 Subjects []string 414 Clusters []string 415 }{ 416 { 417 Resource: "Shard", 418 Actions: []string{"emergency_failover_shard"}, 419 Subjects: []string{"user:allowed"}, 420 Clusters: []string{"*"}, 421 }, 422 }, 423 }, 424 } 425 err := opts.RBAC.Reify() 426 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 427 428 api := vtadmin.NewAPI(testClusters(t), opts) 429 t.Cleanup(func() { 430 if err := api.Close(); err != nil { 431 t.Logf("api did not close cleanly: %s", err.Error()) 432 } 433 }) 434 435 t.Run("unauthorized actor", func(t *testing.T) { 436 t.Parallel() 437 438 actor := &rbac.Actor{Name: "other"} 439 ctx := context.Background() 440 if actor != nil { 441 ctx = rbac.NewContext(ctx, actor) 442 } 443 444 resp, err := api.EmergencyFailoverShard(ctx, &vtadminpb.EmergencyFailoverShardRequest{ 445 ClusterId: "test", 446 Options: &vtctldatapb.EmergencyReparentShardRequest{ 447 Keyspace: "test", 448 Shard: "-", 449 }, 450 }) 451 require.NoError(t, err) 452 assert.Nil(t, resp, "actor %+v should not be permitted to EmergencyFailoverShard", actor) 453 }) 454 455 t.Run("authorized actor", func(t *testing.T) { 456 t.Parallel() 457 458 actor := &rbac.Actor{Name: "allowed"} 459 ctx := context.Background() 460 if actor != nil { 461 ctx = rbac.NewContext(ctx, actor) 462 } 463 464 resp, err := api.EmergencyFailoverShard(ctx, &vtadminpb.EmergencyFailoverShardRequest{ 465 ClusterId: "test", 466 Options: &vtctldatapb.EmergencyReparentShardRequest{ 467 Keyspace: "test", 468 Shard: "-", 469 }, 470 }) 471 require.NoError(t, err) 472 assert.NotNil(t, resp, "actor %+v should be permitted to EmergencyFailoverShard", actor) 473 }) 474 } 475 476 func TestFindSchema(t *testing.T) { 477 t.Parallel() 478 479 opts := vtadmin.Options{ 480 RBAC: &rbac.Config{ 481 Rules: []*struct { 482 Resource string 483 Actions []string 484 Subjects []string 485 Clusters []string 486 }{ 487 { 488 Resource: "Schema", 489 Actions: []string{"get"}, 490 Subjects: []string{"user:allowed-all"}, 491 Clusters: []string{"*"}, 492 }, 493 { 494 Resource: "Schema", 495 Actions: []string{"get"}, 496 Subjects: []string{"user:allowed-other"}, 497 Clusters: []string{"other"}, 498 }, 499 }, 500 }, 501 } 502 err := opts.RBAC.Reify() 503 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 504 505 t.Run("unauthorized actor", func(t *testing.T) { 506 t.Parallel() 507 508 api := vtadmin.NewAPI(testClusters(t), opts) 509 t.Cleanup(func() { 510 if err := api.Close(); err != nil { 511 t.Logf("api did not close cleanly: %s", err.Error()) 512 } 513 }) 514 515 actor := &rbac.Actor{Name: "unauthorized"} 516 ctx := context.Background() 517 if actor != nil { 518 ctx = rbac.NewContext(ctx, actor) 519 } 520 521 resp, err := api.FindSchema(ctx, &vtadminpb.FindSchemaRequest{ 522 Table: "t1", 523 }) 524 assert.Error(t, err) 525 assert.Nil(t, resp, "actor %+v should not be permitted to FindSchema", actor) 526 }) 527 528 t.Run("partial access", func(t *testing.T) { 529 t.Parallel() 530 531 api := vtadmin.NewAPI(testClusters(t), opts) 532 t.Cleanup(func() { 533 if err := api.Close(); err != nil { 534 t.Logf("api did not close cleanly: %s", err.Error()) 535 } 536 }) 537 538 actor := &rbac.Actor{Name: "allowed-other"} 539 ctx := context.Background() 540 if actor != nil { 541 ctx = rbac.NewContext(ctx, actor) 542 } 543 544 resp, _ := api.FindSchema(ctx, &vtadminpb.FindSchemaRequest{ 545 Table: "t1", 546 }) 547 assert.NotEmpty(t, resp, "actor %+v should be permitted to FindSchema", actor) 548 }) 549 550 t.Run("full access", func(t *testing.T) { 551 t.Parallel() 552 553 api := vtadmin.NewAPI(testClusters(t), opts) 554 t.Cleanup(func() { 555 if err := api.Close(); err != nil { 556 t.Logf("api did not close cleanly: %s", err.Error()) 557 } 558 }) 559 560 actor := &rbac.Actor{Name: "allowed-all"} 561 ctx := context.Background() 562 if actor != nil { 563 ctx = rbac.NewContext(ctx, actor) 564 } 565 566 resp, err := api.FindSchema(ctx, &vtadminpb.FindSchemaRequest{ 567 Table: "t1", 568 }) 569 assert.Error(t, err) 570 assert.Contains(t, err.Error(), "multiple schemas found") 571 assert.Nil(t, resp) 572 }) 573 } 574 575 func TestGetBackups(t *testing.T) { 576 t.Parallel() 577 578 opts := vtadmin.Options{ 579 RBAC: &rbac.Config{ 580 Rules: []*struct { 581 Resource string 582 Actions []string 583 Subjects []string 584 Clusters []string 585 }{ 586 { 587 Resource: "Backup", 588 Actions: []string{"get"}, 589 Subjects: []string{"user:allowed-all"}, 590 Clusters: []string{"*"}, 591 }, 592 { 593 Resource: "Backup", 594 Actions: []string{"get"}, 595 Subjects: []string{"user:allowed-other"}, 596 Clusters: []string{"other"}, 597 }, 598 }, 599 }, 600 } 601 err := opts.RBAC.Reify() 602 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 603 604 api := vtadmin.NewAPI(testClusters(t), opts) 605 t.Cleanup(func() { 606 if err := api.Close(); err != nil { 607 t.Logf("api did not close cleanly: %s", err.Error()) 608 } 609 }) 610 611 t.Run("unauthorized actor", func(t *testing.T) { 612 t.Parallel() 613 614 actor := &rbac.Actor{Name: "unauthorized"} 615 ctx := context.Background() 616 if actor != nil { 617 ctx = rbac.NewContext(ctx, actor) 618 } 619 620 resp, err := api.GetBackups(ctx, &vtadminpb.GetBackupsRequest{}) 621 assert.NoError(t, err) 622 assert.Empty(t, resp.Backups, "actor %+v should not be permitted to GetBackups", actor) 623 }) 624 625 t.Run("partial access", func(t *testing.T) { 626 t.Parallel() 627 628 actor := &rbac.Actor{Name: "allowed-other"} 629 ctx := context.Background() 630 if actor != nil { 631 ctx = rbac.NewContext(ctx, actor) 632 } 633 634 resp, _ := api.GetBackups(ctx, &vtadminpb.GetBackupsRequest{}) 635 assert.NotEmpty(t, resp.Backups, "actor %+v should be permitted to GetBackups", actor) 636 assert.Len(t, resp.Backups, 3, "'other' actor should be able to see the 3 backups in cluster 'other'") 637 }) 638 639 t.Run("full access", func(t *testing.T) { 640 t.Parallel() 641 642 actor := &rbac.Actor{Name: "allowed-all"} 643 ctx := context.Background() 644 if actor != nil { 645 ctx = rbac.NewContext(ctx, actor) 646 } 647 648 resp, _ := api.GetBackups(ctx, &vtadminpb.GetBackupsRequest{}) 649 assert.NotEmpty(t, resp.Backups, "actor %+v should be permitted to GetBackups", actor) 650 assert.Len(t, resp.Backups, 4, "'all' actor should be able to see backups in all clusters") 651 }) 652 } 653 654 func TestGetCellInfos(t *testing.T) { 655 t.Parallel() 656 657 opts := vtadmin.Options{ 658 RBAC: &rbac.Config{ 659 Rules: []*struct { 660 Resource string 661 Actions []string 662 Subjects []string 663 Clusters []string 664 }{ 665 { 666 Resource: "CellInfo", 667 Actions: []string{"get"}, 668 Subjects: []string{"user:allowed-all"}, 669 Clusters: []string{"*"}, 670 }, 671 { 672 Resource: "CellInfo", 673 Actions: []string{"get"}, 674 Subjects: []string{"user:allowed-other"}, 675 Clusters: []string{"other"}, 676 }, 677 }, 678 }, 679 } 680 err := opts.RBAC.Reify() 681 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 682 683 api := vtadmin.NewAPI(testClusters(t), opts) 684 t.Cleanup(func() { 685 if err := api.Close(); err != nil { 686 t.Logf("api did not close cleanly: %s", err.Error()) 687 } 688 }) 689 690 t.Run("unauthorized actor", func(t *testing.T) { 691 t.Parallel() 692 693 actor := &rbac.Actor{Name: "unauthorized"} 694 ctx := context.Background() 695 if actor != nil { 696 ctx = rbac.NewContext(ctx, actor) 697 } 698 699 resp, err := api.GetCellInfos(ctx, &vtadminpb.GetCellInfosRequest{ 700 NamesOnly: true, 701 }) 702 assert.NoError(t, err) 703 assert.Empty(t, resp.CellInfos, "actor %+v should not be permitted to GetCellInfos", actor) 704 }) 705 706 t.Run("partial access", func(t *testing.T) { 707 t.Parallel() 708 709 actor := &rbac.Actor{Name: "allowed-other"} 710 ctx := context.Background() 711 if actor != nil { 712 ctx = rbac.NewContext(ctx, actor) 713 } 714 715 resp, _ := api.GetCellInfos(ctx, &vtadminpb.GetCellInfosRequest{ 716 NamesOnly: true, 717 }) 718 assert.NotEmpty(t, resp.CellInfos, "actor %+v should be permitted to GetCellInfos", actor) 719 assert.ElementsMatch(t, resp.CellInfos, []*vtadminpb.ClusterCellInfo{{Cluster: &vtadminpb.Cluster{Id: "other", Name: "other"}, Name: "other1"}}) 720 }) 721 722 t.Run("full access", func(t *testing.T) { 723 t.Parallel() 724 725 actor := &rbac.Actor{Name: "allowed-all"} 726 ctx := context.Background() 727 if actor != nil { 728 ctx = rbac.NewContext(ctx, actor) 729 } 730 731 resp, _ := api.GetCellInfos(ctx, &vtadminpb.GetCellInfosRequest{ 732 NamesOnly: true, 733 }) 734 assert.NotEmpty(t, resp.CellInfos, "actor %+v should be permitted to GetCellInfos", actor) 735 assert.ElementsMatch(t, resp.CellInfos, []*vtadminpb.ClusterCellInfo{{Cluster: &vtadminpb.Cluster{Id: "test", Name: "test"}, Name: "zone1"}, {Cluster: &vtadminpb.Cluster{Id: "other", Name: "other"}, Name: "other1"}}) 736 }) 737 } 738 739 func TestGetCellsAliases(t *testing.T) { 740 t.Parallel() 741 742 opts := vtadmin.Options{ 743 RBAC: &rbac.Config{ 744 Rules: []*struct { 745 Resource string 746 Actions []string 747 Subjects []string 748 Clusters []string 749 }{ 750 { 751 Resource: "CellsAlias", 752 Actions: []string{"get"}, 753 Subjects: []string{"user:allowed-all"}, 754 Clusters: []string{"*"}, 755 }, 756 { 757 Resource: "CellsAlias", 758 Actions: []string{"get"}, 759 Subjects: []string{"user:allowed-other"}, 760 Clusters: []string{"other"}, 761 }, 762 }, 763 }, 764 } 765 err := opts.RBAC.Reify() 766 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 767 768 api := vtadmin.NewAPI(testClusters(t), opts) 769 t.Cleanup(func() { 770 if err := api.Close(); err != nil { 771 t.Logf("api did not close cleanly: %s", err.Error()) 772 } 773 }) 774 775 t.Run("unauthorized actor", func(t *testing.T) { 776 t.Parallel() 777 778 actor := &rbac.Actor{Name: "unauthorized"} 779 ctx := context.Background() 780 if actor != nil { 781 ctx = rbac.NewContext(ctx, actor) 782 } 783 784 resp, err := api.GetCellsAliases(ctx, &vtadminpb.GetCellsAliasesRequest{}) 785 assert.NoError(t, err) 786 assert.Empty(t, resp.Aliases, "actor %+v should not be permitted to GetCellsAliases", actor) 787 }) 788 789 t.Run("partial access", func(t *testing.T) { 790 t.Parallel() 791 792 actor := &rbac.Actor{Name: "allowed-other"} 793 ctx := context.Background() 794 if actor != nil { 795 ctx = rbac.NewContext(ctx, actor) 796 } 797 798 resp, _ := api.GetCellsAliases(ctx, &vtadminpb.GetCellsAliasesRequest{}) 799 assert.NotEmpty(t, resp.Aliases, "actor %+v should be permitted to GetCellsAliases", actor) 800 assert.ElementsMatch(t, resp.Aliases, []*vtadminpb.ClusterCellsAliases{{Cluster: &vtadminpb.Cluster{Id: "other", Name: "other"}, Aliases: map[string]*topodatapb.CellsAlias{"other": {Cells: []string{"other1"}}}}}) 801 }) 802 803 t.Run("full access", func(t *testing.T) { 804 t.Parallel() 805 806 actor := &rbac.Actor{Name: "allowed-all"} 807 ctx := context.Background() 808 if actor != nil { 809 ctx = rbac.NewContext(ctx, actor) 810 } 811 812 resp, _ := api.GetCellsAliases(ctx, &vtadminpb.GetCellsAliasesRequest{}) 813 assert.NotEmpty(t, resp.Aliases, "actor %+v should be permitted to GetCellsAliases", actor) 814 assert.ElementsMatch(t, resp.Aliases, []*vtadminpb.ClusterCellsAliases{{Cluster: &vtadminpb.Cluster{Id: "test", Name: "test"}, Aliases: map[string]*topodatapb.CellsAlias{"zone": {Cells: []string{"zone1"}}}}, {Cluster: &vtadminpb.Cluster{Id: "other", Name: "other"}, Aliases: map[string]*topodatapb.CellsAlias{"other": {Cells: []string{"other1"}}}}}) 815 }) 816 } 817 818 func TestGetClusters(t *testing.T) { 819 t.Parallel() 820 821 opts := vtadmin.Options{ 822 RBAC: &rbac.Config{ 823 Rules: []*struct { 824 Resource string 825 Actions []string 826 Subjects []string 827 Clusters []string 828 }{ 829 { 830 Resource: "Cluster", 831 Actions: []string{"get"}, 832 Subjects: []string{"user:allowed"}, 833 Clusters: []string{"*"}, 834 }, 835 }, 836 }, 837 } 838 err := opts.RBAC.Reify() 839 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 840 841 api := vtadmin.NewAPI(testClusters(t), opts) 842 t.Cleanup(func() { 843 if err := api.Close(); err != nil { 844 t.Logf("api did not close cleanly: %s", err.Error()) 845 } 846 }) 847 848 t.Run("unauthenticated", func(t *testing.T) { 849 t.Parallel() 850 851 var actor *rbac.Actor 852 ctx := context.Background() 853 if actor != nil { 854 ctx = rbac.NewContext(ctx, actor) 855 } 856 857 resp, _ := api.GetClusters(ctx, &vtadminpb.GetClustersRequest{}) 858 assert.Empty(t, resp.Clusters, "actor %+v should not be permitted to GetClusters", actor) 859 }) 860 861 t.Run("unauthorized actor", func(t *testing.T) { 862 t.Parallel() 863 864 actor := &rbac.Actor{Name: "other"} 865 ctx := context.Background() 866 if actor != nil { 867 ctx = rbac.NewContext(ctx, actor) 868 } 869 870 resp, _ := api.GetClusters(ctx, &vtadminpb.GetClustersRequest{}) 871 assert.Empty(t, resp.Clusters, "actor %+v should not be permitted to GetClusters", actor) 872 }) 873 874 t.Run("authorized actor", func(t *testing.T) { 875 t.Parallel() 876 877 actor := &rbac.Actor{Name: "allowed"} 878 ctx := context.Background() 879 if actor != nil { 880 ctx = rbac.NewContext(ctx, actor) 881 } 882 883 resp, err := api.GetClusters(ctx, &vtadminpb.GetClustersRequest{}) 884 require.NoError(t, err) 885 assert.NotEmpty(t, resp.Clusters, "actor %+v should be permitted to GetClusters", actor) 886 }) 887 } 888 889 func TestGetGates(t *testing.T) { 890 t.Parallel() 891 892 opts := vtadmin.Options{ 893 RBAC: &rbac.Config{ 894 Rules: []*struct { 895 Resource string 896 Actions []string 897 Subjects []string 898 Clusters []string 899 }{ 900 { 901 Resource: "VTGate", 902 Actions: []string{"get"}, 903 Subjects: []string{"user:allowed-all"}, 904 Clusters: []string{"*"}, 905 }, 906 { 907 Resource: "VTGate", 908 Actions: []string{"get"}, 909 Subjects: []string{"user:allowed-other"}, 910 Clusters: []string{"other"}, 911 }, 912 }, 913 }, 914 } 915 err := opts.RBAC.Reify() 916 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 917 918 t.Run("unauthorized actor", func(t *testing.T) { 919 t.Parallel() 920 921 api := vtadmin.NewAPI(testClusters(t), opts) 922 t.Cleanup(func() { 923 if err := api.Close(); err != nil { 924 t.Logf("api did not close cleanly: %s", err.Error()) 925 } 926 }) 927 928 actor := &rbac.Actor{Name: "unauthorized"} 929 ctx := context.Background() 930 if actor != nil { 931 ctx = rbac.NewContext(ctx, actor) 932 } 933 934 resp, err := api.GetGates(ctx, &vtadminpb.GetGatesRequest{}) 935 assert.NoError(t, err) 936 assert.Empty(t, resp.Gates, "actor %+v should not be permitted to GetGates", actor) 937 }) 938 939 t.Run("partial access", func(t *testing.T) { 940 t.Parallel() 941 942 api := vtadmin.NewAPI(testClusters(t), opts) 943 t.Cleanup(func() { 944 if err := api.Close(); err != nil { 945 t.Logf("api did not close cleanly: %s", err.Error()) 946 } 947 }) 948 949 actor := &rbac.Actor{Name: "allowed-other"} 950 ctx := context.Background() 951 if actor != nil { 952 ctx = rbac.NewContext(ctx, actor) 953 } 954 955 resp, _ := api.GetGates(ctx, &vtadminpb.GetGatesRequest{}) 956 assert.NotEmpty(t, resp.Gates, "actor %+v should be permitted to GetGates", actor) 957 // testutil.BuildCluster creates exactly one gate per cluster 958 assert.Len(t, resp.Gates, 1, "actor %+v should only be able to see VTGate from cluster 'other'", actor) 959 }) 960 961 t.Run("full access", func(t *testing.T) { 962 t.Parallel() 963 964 api := vtadmin.NewAPI(testClusters(t), opts) 965 t.Cleanup(func() { 966 if err := api.Close(); err != nil { 967 t.Logf("api did not close cleanly: %s", err.Error()) 968 } 969 }) 970 971 actor := &rbac.Actor{Name: "allowed-all"} 972 ctx := context.Background() 973 if actor != nil { 974 ctx = rbac.NewContext(ctx, actor) 975 } 976 977 resp, _ := api.GetGates(ctx, &vtadminpb.GetGatesRequest{}) 978 assert.NotEmpty(t, resp.Gates, "actor %+v should be permitted to GetGates", actor) 979 // testutil.BuildCluster creates exactly one gate per cluster 980 assert.Len(t, resp.Gates, 2, "actor %+v should be able to see VTGate from all clusters", actor) 981 }) 982 } 983 984 func TestGetKeyspace(t *testing.T) { 985 t.Parallel() 986 987 opts := vtadmin.Options{ 988 RBAC: &rbac.Config{ 989 Rules: []*struct { 990 Resource string 991 Actions []string 992 Subjects []string 993 Clusters []string 994 }{ 995 { 996 Resource: "Keyspace", 997 Actions: []string{"get"}, 998 Subjects: []string{"user:allowed"}, 999 Clusters: []string{"*"}, 1000 }, 1001 }, 1002 }, 1003 } 1004 err := opts.RBAC.Reify() 1005 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1006 1007 api := vtadmin.NewAPI(testClusters(t), opts) 1008 t.Cleanup(func() { 1009 if err := api.Close(); err != nil { 1010 t.Logf("api did not close cleanly: %s", err.Error()) 1011 } 1012 }) 1013 1014 t.Run("unauthorized actor", func(t *testing.T) { 1015 t.Parallel() 1016 1017 actor := &rbac.Actor{Name: "other"} 1018 ctx := context.Background() 1019 if actor != nil { 1020 ctx = rbac.NewContext(ctx, actor) 1021 } 1022 1023 resp, err := api.GetKeyspace(ctx, &vtadminpb.GetKeyspaceRequest{ 1024 ClusterId: "test", 1025 Keyspace: "test", 1026 }) 1027 require.NoError(t, err) 1028 assert.Nil(t, resp, "actor %+v should not be permitted to GetKeyspace", actor) 1029 }) 1030 1031 t.Run("authorized actor", func(t *testing.T) { 1032 t.Parallel() 1033 1034 actor := &rbac.Actor{Name: "allowed"} 1035 ctx := context.Background() 1036 if actor != nil { 1037 ctx = rbac.NewContext(ctx, actor) 1038 } 1039 1040 resp, err := api.GetKeyspace(ctx, &vtadminpb.GetKeyspaceRequest{ 1041 ClusterId: "test", 1042 Keyspace: "test", 1043 }) 1044 require.NoError(t, err) 1045 assert.NotNil(t, resp, "actor %+v should be permitted to GetKeyspace", actor) 1046 }) 1047 } 1048 1049 func TestGetKeyspaces(t *testing.T) { 1050 t.Parallel() 1051 1052 opts := vtadmin.Options{ 1053 RBAC: &rbac.Config{ 1054 Rules: []*struct { 1055 Resource string 1056 Actions []string 1057 Subjects []string 1058 Clusters []string 1059 }{ 1060 { 1061 Resource: "Keyspace", 1062 Actions: []string{"get"}, 1063 Subjects: []string{"user:allowed-all"}, 1064 Clusters: []string{"*"}, 1065 }, 1066 { 1067 Resource: "Keyspace", 1068 Actions: []string{"get"}, 1069 Subjects: []string{"user:allowed-other"}, 1070 Clusters: []string{"other"}, 1071 }, 1072 }, 1073 }, 1074 } 1075 err := opts.RBAC.Reify() 1076 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1077 1078 api := vtadmin.NewAPI(testClusters(t), opts) 1079 t.Cleanup(func() { 1080 if err := api.Close(); err != nil { 1081 t.Logf("api did not close cleanly: %s", err.Error()) 1082 } 1083 }) 1084 1085 t.Run("unauthorized actor", func(t *testing.T) { 1086 t.Parallel() 1087 1088 actor := &rbac.Actor{Name: "unauthorized"} 1089 ctx := context.Background() 1090 if actor != nil { 1091 ctx = rbac.NewContext(ctx, actor) 1092 } 1093 1094 resp, err := api.GetKeyspaces(ctx, &vtadminpb.GetKeyspacesRequest{}) 1095 assert.NoError(t, err) 1096 assert.Empty(t, resp.Keyspaces, "actor %+v should not be permitted to GetKeyspaces", actor) 1097 }) 1098 1099 t.Run("partial access", func(t *testing.T) { 1100 t.Parallel() 1101 1102 actor := &rbac.Actor{Name: "allowed-other"} 1103 ctx := context.Background() 1104 if actor != nil { 1105 ctx = rbac.NewContext(ctx, actor) 1106 } 1107 1108 resp, _ := api.GetKeyspaces(ctx, &vtadminpb.GetKeyspacesRequest{}) 1109 assert.NotEmpty(t, resp.Keyspaces, "actor %+v should be permitted to GetKeyspaces", actor) 1110 ksMap := map[string][]string{} 1111 for _, ks := range resp.Keyspaces { 1112 if _, ok := ksMap[ks.Cluster.Id]; !ok { 1113 ksMap[ks.Cluster.Id] = []string{} 1114 } 1115 ksMap[ks.Cluster.Id] = append(ksMap[ks.Cluster.Id], ks.Keyspace.Name) 1116 } 1117 assert.Equal(t, ksMap, map[string][]string{"other": {"otherks"}}, "actor %+v should be permitted to GetKeyspaces", actor) 1118 }) 1119 1120 t.Run("full access", func(t *testing.T) { 1121 t.Parallel() 1122 1123 actor := &rbac.Actor{Name: "allowed-all"} 1124 ctx := context.Background() 1125 if actor != nil { 1126 ctx = rbac.NewContext(ctx, actor) 1127 } 1128 1129 resp, _ := api.GetKeyspaces(ctx, &vtadminpb.GetKeyspacesRequest{}) 1130 assert.NotEmpty(t, resp.Keyspaces, "actor %+v should be permitted to GetKeyspaces", actor) 1131 ksMap := map[string][]string{} 1132 for _, ks := range resp.Keyspaces { 1133 if _, ok := ksMap[ks.Cluster.Id]; !ok { 1134 ksMap[ks.Cluster.Id] = []string{} 1135 } 1136 ksMap[ks.Cluster.Id] = append(ksMap[ks.Cluster.Id], ks.Keyspace.Name) 1137 } 1138 assert.Equal(t, ksMap, map[string][]string{"test": {"test"}, "other": {"otherks"}}, "actor %+v should be permitted to GetKeyspaces", actor) 1139 }) 1140 } 1141 1142 func TestGetSchema(t *testing.T) { 1143 t.Parallel() 1144 1145 opts := vtadmin.Options{ 1146 RBAC: &rbac.Config{ 1147 Rules: []*struct { 1148 Resource string 1149 Actions []string 1150 Subjects []string 1151 Clusters []string 1152 }{ 1153 { 1154 Resource: "Schema", 1155 Actions: []string{"get"}, 1156 Subjects: []string{"user:allowed"}, 1157 Clusters: []string{"*"}, 1158 }, 1159 }, 1160 }, 1161 } 1162 err := opts.RBAC.Reify() 1163 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1164 1165 t.Run("unauthorized actor", func(t *testing.T) { 1166 t.Parallel() 1167 1168 api := vtadmin.NewAPI(testClusters(t), opts) 1169 t.Cleanup(func() { 1170 if err := api.Close(); err != nil { 1171 t.Logf("api did not close cleanly: %s", err.Error()) 1172 } 1173 }) 1174 1175 actor := &rbac.Actor{Name: "other"} 1176 ctx := context.Background() 1177 if actor != nil { 1178 ctx = rbac.NewContext(ctx, actor) 1179 } 1180 1181 resp, err := api.GetSchema(ctx, &vtadminpb.GetSchemaRequest{ 1182 ClusterId: "test", 1183 Keyspace: "test", 1184 Table: "t1", 1185 }) 1186 require.NoError(t, err) 1187 assert.Nil(t, resp, "actor %+v should not be permitted to GetSchema", actor) 1188 }) 1189 1190 t.Run("authorized actor", func(t *testing.T) { 1191 t.Parallel() 1192 1193 api := vtadmin.NewAPI(testClusters(t), opts) 1194 t.Cleanup(func() { 1195 if err := api.Close(); err != nil { 1196 t.Logf("api did not close cleanly: %s", err.Error()) 1197 } 1198 }) 1199 1200 actor := &rbac.Actor{Name: "allowed"} 1201 ctx := context.Background() 1202 if actor != nil { 1203 ctx = rbac.NewContext(ctx, actor) 1204 } 1205 1206 resp, err := api.GetSchema(ctx, &vtadminpb.GetSchemaRequest{ 1207 ClusterId: "test", 1208 Keyspace: "test", 1209 Table: "t1", 1210 }) 1211 require.NoError(t, err) 1212 assert.NotNil(t, resp, "actor %+v should be permitted to GetSchema", actor) 1213 }) 1214 } 1215 1216 func TestGetSchemas(t *testing.T) { 1217 t.Parallel() 1218 1219 opts := vtadmin.Options{ 1220 RBAC: &rbac.Config{ 1221 Rules: []*struct { 1222 Resource string 1223 Actions []string 1224 Subjects []string 1225 Clusters []string 1226 }{ 1227 { 1228 Resource: "Schema", 1229 Actions: []string{"get"}, 1230 Subjects: []string{"user:allowed-all"}, 1231 Clusters: []string{"*"}, 1232 }, 1233 { 1234 Resource: "Schema", 1235 Actions: []string{"get"}, 1236 Subjects: []string{"user:allowed-other"}, 1237 Clusters: []string{"other"}, 1238 }, 1239 }, 1240 }, 1241 } 1242 err := opts.RBAC.Reify() 1243 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1244 1245 t.Run("unauthorized actor", func(t *testing.T) { 1246 t.Parallel() 1247 1248 api := vtadmin.NewAPI(testClusters(t), opts) 1249 t.Cleanup(func() { 1250 if err := api.Close(); err != nil { 1251 t.Logf("api did not close cleanly: %s", err.Error()) 1252 } 1253 }) 1254 1255 actor := &rbac.Actor{Name: "unauthorized"} 1256 ctx := context.Background() 1257 if actor != nil { 1258 ctx = rbac.NewContext(ctx, actor) 1259 } 1260 1261 resp, err := api.GetSchemas(ctx, &vtadminpb.GetSchemasRequest{}) 1262 assert.NoError(t, err) 1263 assert.Empty(t, resp.Schemas, "actor %+v should not be permitted to GetSchemas", actor) 1264 }) 1265 1266 t.Run("partial access", func(t *testing.T) { 1267 t.Parallel() 1268 1269 api := vtadmin.NewAPI(testClusters(t), opts) 1270 t.Cleanup(func() { 1271 if err := api.Close(); err != nil { 1272 t.Logf("api did not close cleanly: %s", err.Error()) 1273 } 1274 }) 1275 1276 actor := &rbac.Actor{Name: "allowed-other"} 1277 ctx := context.Background() 1278 if actor != nil { 1279 ctx = rbac.NewContext(ctx, actor) 1280 } 1281 1282 resp, _ := api.GetSchemas(ctx, &vtadminpb.GetSchemasRequest{}) 1283 assert.NotEmpty(t, resp.Schemas, "actor %+v should be permitted to GetSchemas", actor) 1284 schemaMap := map[string][]string{} 1285 for _, schema := range resp.Schemas { 1286 if _, ok := schemaMap[schema.Cluster.Id]; !ok { 1287 schemaMap[schema.Cluster.Id] = []string{} 1288 } 1289 schemaMap[schema.Cluster.Id] = append(schemaMap[schema.Cluster.Id], schema.Keyspace) 1290 } 1291 assert.Equal(t, schemaMap, map[string][]string{"other": {"otherks"}}, "actor %+v should be permitted to GetSchemas", actor) 1292 }) 1293 1294 t.Run("full access", func(t *testing.T) { 1295 t.Parallel() 1296 1297 api := vtadmin.NewAPI(testClusters(t), opts) 1298 t.Cleanup(func() { 1299 if err := api.Close(); err != nil { 1300 t.Logf("api did not close cleanly: %s", err.Error()) 1301 } 1302 }) 1303 1304 actor := &rbac.Actor{Name: "allowed-all"} 1305 ctx := context.Background() 1306 if actor != nil { 1307 ctx = rbac.NewContext(ctx, actor) 1308 } 1309 1310 resp, _ := api.GetSchemas(ctx, &vtadminpb.GetSchemasRequest{}) 1311 assert.NotEmpty(t, resp.Schemas, "actor %+v should be permitted to GetSchemas", actor) 1312 schemaMap := map[string][]string{} 1313 for _, schema := range resp.Schemas { 1314 if _, ok := schemaMap[schema.Cluster.Id]; !ok { 1315 schemaMap[schema.Cluster.Id] = []string{} 1316 } 1317 schemaMap[schema.Cluster.Id] = append(schemaMap[schema.Cluster.Id], schema.Keyspace) 1318 } 1319 assert.Equal(t, schemaMap, map[string][]string{"test": {"test"}, "other": {"otherks"}}, "actor %+v should be permitted to GetSchemas", actor) 1320 }) 1321 } 1322 1323 func TestGetShardReplicationPositions(t *testing.T) { 1324 t.Parallel() 1325 1326 opts := vtadmin.Options{ 1327 RBAC: &rbac.Config{ 1328 Rules: []*struct { 1329 Resource string 1330 Actions []string 1331 Subjects []string 1332 Clusters []string 1333 }{ 1334 { 1335 Resource: "ShardReplicationPosition", 1336 Actions: []string{"get"}, 1337 Subjects: []string{"user:allowed-all"}, 1338 Clusters: []string{"*"}, 1339 }, 1340 { 1341 Resource: "ShardReplicationPosition", 1342 Actions: []string{"get"}, 1343 Subjects: []string{"user:allowed-other"}, 1344 Clusters: []string{"other"}, 1345 }, 1346 }, 1347 }, 1348 } 1349 err := opts.RBAC.Reify() 1350 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1351 1352 api := vtadmin.NewAPI(testClusters(t), opts) 1353 t.Cleanup(func() { 1354 if err := api.Close(); err != nil { 1355 t.Logf("api did not close cleanly: %s", err.Error()) 1356 } 1357 }) 1358 1359 t.Run("unauthorized actor", func(t *testing.T) { 1360 t.Parallel() 1361 1362 actor := &rbac.Actor{Name: "unauthorized"} 1363 ctx := context.Background() 1364 if actor != nil { 1365 ctx = rbac.NewContext(ctx, actor) 1366 } 1367 1368 resp, err := api.GetShardReplicationPositions(ctx, &vtadminpb.GetShardReplicationPositionsRequest{}) 1369 assert.NoError(t, err) 1370 assert.Empty(t, resp.ReplicationPositions, "actor %+v should not be permitted to GetShardReplicationPositions", actor) 1371 }) 1372 1373 t.Run("partial access", func(t *testing.T) { 1374 t.Parallel() 1375 1376 actor := &rbac.Actor{Name: "allowed-other"} 1377 ctx := context.Background() 1378 if actor != nil { 1379 ctx = rbac.NewContext(ctx, actor) 1380 } 1381 1382 resp, _ := api.GetShardReplicationPositions(ctx, &vtadminpb.GetShardReplicationPositionsRequest{}) 1383 assert.NotEmpty(t, resp.ReplicationPositions, "actor %+v should be permitted to GetShardReplicationPositions", actor) 1384 posMap := map[string][]string{} 1385 for _, pos := range resp.ReplicationPositions { 1386 if _, ok := posMap[pos.Cluster.Id]; !ok { 1387 posMap[pos.Cluster.Id] = []string{} 1388 } 1389 posMap[pos.Cluster.Id] = append(posMap[pos.Cluster.Id], fmt.Sprintf("%s/%s", pos.Keyspace, pos.Shard)) 1390 } 1391 assert.Equal(t, posMap, map[string][]string{"other": {"otherks/-"}}, "actor %+v should be permitted to GetShardReplicationPositions", actor) 1392 }) 1393 1394 t.Run("full access", func(t *testing.T) { 1395 t.Parallel() 1396 1397 actor := &rbac.Actor{Name: "allowed-all"} 1398 ctx := context.Background() 1399 if actor != nil { 1400 ctx = rbac.NewContext(ctx, actor) 1401 } 1402 1403 resp, _ := api.GetShardReplicationPositions(ctx, &vtadminpb.GetShardReplicationPositionsRequest{}) 1404 assert.NotEmpty(t, resp.ReplicationPositions, "actor %+v should be permitted to GetShardReplicationPositions", actor) 1405 posMap := map[string][]string{} 1406 for _, pos := range resp.ReplicationPositions { 1407 if _, ok := posMap[pos.Cluster.Id]; !ok { 1408 posMap[pos.Cluster.Id] = []string{} 1409 } 1410 posMap[pos.Cluster.Id] = append(posMap[pos.Cluster.Id], fmt.Sprintf("%s/%s", pos.Keyspace, pos.Shard)) 1411 } 1412 assert.Equal(t, posMap, map[string][]string{"test": {"test/-"}, "other": {"otherks/-"}}, "actor %+v should be permitted to GetShardReplicationPositions", actor) 1413 }) 1414 } 1415 1416 func TestGetSrvVSchema(t *testing.T) { 1417 t.Parallel() 1418 1419 opts := vtadmin.Options{ 1420 RBAC: &rbac.Config{ 1421 Rules: []*struct { 1422 Resource string 1423 Actions []string 1424 Subjects []string 1425 Clusters []string 1426 }{ 1427 { 1428 Resource: "SrvVSchema", 1429 Actions: []string{"get"}, 1430 Subjects: []string{"user:allowed"}, 1431 Clusters: []string{"*"}, 1432 }, 1433 }, 1434 }, 1435 } 1436 err := opts.RBAC.Reify() 1437 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1438 1439 api := vtadmin.NewAPI(testClusters(t), opts) 1440 t.Cleanup(func() { 1441 if err := api.Close(); err != nil { 1442 t.Logf("api did not close cleanly: %s", err.Error()) 1443 } 1444 }) 1445 1446 t.Run("unauthorized actor", func(t *testing.T) { 1447 t.Parallel() 1448 1449 actor := &rbac.Actor{Name: "other"} 1450 ctx := context.Background() 1451 if actor != nil { 1452 ctx = rbac.NewContext(ctx, actor) 1453 } 1454 1455 resp, err := api.GetSrvVSchema(ctx, &vtadminpb.GetSrvVSchemaRequest{ 1456 ClusterId: "test", 1457 Cell: "zone1", 1458 }) 1459 require.NoError(t, err) 1460 assert.Nil(t, resp, "actor %+v should not be permitted to GetSrvVSchema", actor) 1461 }) 1462 1463 t.Run("authorized actor", func(t *testing.T) { 1464 t.Parallel() 1465 1466 actor := &rbac.Actor{Name: "allowed"} 1467 ctx := context.Background() 1468 if actor != nil { 1469 ctx = rbac.NewContext(ctx, actor) 1470 } 1471 1472 resp, err := api.GetSrvVSchema(ctx, &vtadminpb.GetSrvVSchemaRequest{ 1473 ClusterId: "test", 1474 Cell: "zone1", 1475 }) 1476 require.NoError(t, err) 1477 assert.NotNil(t, resp, "actor %+v should be permitted to GetSrvVSchema", actor) 1478 }) 1479 } 1480 1481 func TestGetSrvVSchemas(t *testing.T) { 1482 t.Parallel() 1483 1484 opts := vtadmin.Options{ 1485 RBAC: &rbac.Config{ 1486 Rules: []*struct { 1487 Resource string 1488 Actions []string 1489 Subjects []string 1490 Clusters []string 1491 }{ 1492 { 1493 Resource: "SrvVSchema", 1494 Actions: []string{"get"}, 1495 Subjects: []string{"user:allowed-all"}, 1496 Clusters: []string{"*"}, 1497 }, 1498 { 1499 Resource: "SrvVSchema", 1500 Actions: []string{"get"}, 1501 Subjects: []string{"user:allowed-other"}, 1502 Clusters: []string{"other"}, 1503 }, 1504 }, 1505 }, 1506 } 1507 err := opts.RBAC.Reify() 1508 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1509 1510 api := vtadmin.NewAPI(testClusters(t), opts) 1511 t.Cleanup(func() { 1512 if err := api.Close(); err != nil { 1513 t.Logf("api did not close cleanly: %s", err.Error()) 1514 } 1515 }) 1516 1517 t.Run("unauthorized actor", func(t *testing.T) { 1518 t.Parallel() 1519 1520 actor := &rbac.Actor{Name: "unauthorized"} 1521 ctx := context.Background() 1522 if actor != nil { 1523 ctx = rbac.NewContext(ctx, actor) 1524 } 1525 1526 resp, err := api.GetSrvVSchemas(ctx, &vtadminpb.GetSrvVSchemasRequest{}) 1527 require.NoError(t, err) 1528 assert.Nil(t, resp.SrvVSchemas, "actor %+v should not be permitted to GetSrvVSchemas", actor) 1529 }) 1530 1531 t.Run("partial access", func(t *testing.T) { 1532 t.Parallel() 1533 1534 actor := &rbac.Actor{Name: "allowed-other"} 1535 ctx := context.Background() 1536 if actor != nil { 1537 ctx = rbac.NewContext(ctx, actor) 1538 } 1539 1540 resp, _ := api.GetSrvVSchemas(ctx, &vtadminpb.GetSrvVSchemasRequest{}) 1541 assert.NotEmpty(t, resp.SrvVSchemas, "actor %+v should be permitted to GetSrvVSchemas", actor) 1542 clusterCells := map[string][]string{} 1543 for _, svs := range resp.SrvVSchemas { 1544 if _, ok := clusterCells[svs.Cluster.Id]; !ok { 1545 clusterCells[svs.Cluster.Id] = []string{} 1546 } 1547 clusterCells[svs.Cluster.Id] = append(clusterCells[svs.Cluster.Id], svs.Cell) 1548 } 1549 assert.Equal(t, clusterCells, map[string][]string{"other": {"other1"}}, "actor %+v should be permitted to GetSrvVSchemas", actor) 1550 }) 1551 1552 t.Run("full access", func(t *testing.T) { 1553 t.Parallel() 1554 1555 actor := &rbac.Actor{Name: "allowed-all"} 1556 ctx := context.Background() 1557 if actor != nil { 1558 ctx = rbac.NewContext(ctx, actor) 1559 } 1560 1561 resp, _ := api.GetSrvVSchemas(ctx, &vtadminpb.GetSrvVSchemasRequest{}) 1562 assert.NotEmpty(t, resp.SrvVSchemas, "actor %+v should be permitted to GetSrvVSchemas", actor) 1563 clusterCells := map[string][]string{} 1564 for _, svs := range resp.SrvVSchemas { 1565 if _, ok := clusterCells[svs.Cluster.Id]; !ok { 1566 clusterCells[svs.Cluster.Id] = []string{} 1567 } 1568 clusterCells[svs.Cluster.Id] = append(clusterCells[svs.Cluster.Id], svs.Cell) 1569 } 1570 assert.Equal(t, clusterCells, map[string][]string{"test": {"zone1"}, "other": {"other1"}}, "actor %+v should be permitted to GetSrvVSchemas", actor) 1571 }) 1572 } 1573 1574 func TestGetTablet(t *testing.T) { 1575 t.Parallel() 1576 1577 opts := vtadmin.Options{ 1578 RBAC: &rbac.Config{ 1579 Rules: []*struct { 1580 Resource string 1581 Actions []string 1582 Subjects []string 1583 Clusters []string 1584 }{ 1585 { 1586 Resource: "Tablet", 1587 Actions: []string{"get"}, 1588 Subjects: []string{"user:allowed"}, 1589 Clusters: []string{"*"}, 1590 }, 1591 }, 1592 }, 1593 } 1594 err := opts.RBAC.Reify() 1595 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1596 1597 api := vtadmin.NewAPI(testClusters(t), opts) 1598 t.Cleanup(func() { 1599 if err := api.Close(); err != nil { 1600 t.Logf("api did not close cleanly: %s", err.Error()) 1601 } 1602 }) 1603 1604 t.Run("unauthorized actor", func(t *testing.T) { 1605 t.Parallel() 1606 1607 actor := &rbac.Actor{Name: "other"} 1608 ctx := context.Background() 1609 if actor != nil { 1610 ctx = rbac.NewContext(ctx, actor) 1611 } 1612 1613 resp, err := api.GetTablet(ctx, &vtadminpb.GetTabletRequest{ 1614 Alias: &topodatapb.TabletAlias{ 1615 Cell: "zone1", 1616 Uid: 100, 1617 }, 1618 }) 1619 assert.Error(t, err, "actor %+v should not be permitted to GetTablet", actor) 1620 assert.Nil(t, resp, "actor %+v should not be permitted to GetTablet", actor) 1621 }) 1622 1623 t.Run("authorized actor", func(t *testing.T) { 1624 t.Parallel() 1625 1626 actor := &rbac.Actor{Name: "allowed"} 1627 ctx := context.Background() 1628 if actor != nil { 1629 ctx = rbac.NewContext(ctx, actor) 1630 } 1631 1632 resp, err := api.GetTablet(ctx, &vtadminpb.GetTabletRequest{ 1633 Alias: &topodatapb.TabletAlias{ 1634 Cell: "zone1", 1635 Uid: 100, 1636 }, 1637 }) 1638 require.NoError(t, err) 1639 assert.NotNil(t, resp, "actor %+v should be permitted to GetTablet", actor) 1640 }) 1641 } 1642 1643 func TestGetTablets(t *testing.T) { 1644 t.Parallel() 1645 1646 opts := vtadmin.Options{ 1647 RBAC: &rbac.Config{ 1648 Rules: []*struct { 1649 Resource string 1650 Actions []string 1651 Subjects []string 1652 Clusters []string 1653 }{ 1654 { 1655 Resource: "Tablet", 1656 Actions: []string{"get"}, 1657 Subjects: []string{"user:allowed-all"}, 1658 Clusters: []string{"*"}, 1659 }, 1660 { 1661 Resource: "Tablet", 1662 Actions: []string{"get"}, 1663 Subjects: []string{"user:allowed-other"}, 1664 Clusters: []string{"other"}, 1665 }, 1666 }, 1667 }, 1668 } 1669 err := opts.RBAC.Reify() 1670 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1671 1672 api := vtadmin.NewAPI(testClusters(t), opts) 1673 t.Cleanup(func() { 1674 if err := api.Close(); err != nil { 1675 t.Logf("api did not close cleanly: %s", err.Error()) 1676 } 1677 }) 1678 1679 t.Run("unauthorized actor", func(t *testing.T) { 1680 t.Parallel() 1681 1682 actor := &rbac.Actor{Name: "unauthorized"} 1683 ctx := context.Background() 1684 if actor != nil { 1685 ctx = rbac.NewContext(ctx, actor) 1686 } 1687 1688 resp, err := api.GetTablets(ctx, &vtadminpb.GetTabletsRequest{}) 1689 require.NoError(t, err) 1690 assert.Nil(t, resp.Tablets, "actor %+v should not be permitted to GetTablets", actor) 1691 }) 1692 1693 t.Run("partial access", func(t *testing.T) { 1694 t.Parallel() 1695 1696 actor := &rbac.Actor{Name: "allowed-other"} 1697 ctx := context.Background() 1698 if actor != nil { 1699 ctx = rbac.NewContext(ctx, actor) 1700 } 1701 1702 resp, _ := api.GetTablets(ctx, &vtadminpb.GetTabletsRequest{}) 1703 assert.NotEmpty(t, resp.Tablets, "actor %+v should be permitted to GetTablets", actor) 1704 clusterAliases := map[string][]string{} 1705 for _, tablet := range resp.Tablets { 1706 if _, ok := clusterAliases[tablet.Cluster.Id]; !ok { 1707 clusterAliases[tablet.Cluster.Id] = []string{} 1708 } 1709 clusterAliases[tablet.Cluster.Id] = append(clusterAliases[tablet.Cluster.Id], topoproto.TabletAliasString(tablet.Tablet.Alias)) 1710 } 1711 assert.Equal(t, clusterAliases, map[string][]string{"other": {"other1-0000000100"}}, "actor %+v should be permitted to GetTablets", actor) 1712 }) 1713 1714 t.Run("full access", func(t *testing.T) { 1715 t.Parallel() 1716 1717 actor := &rbac.Actor{Name: "allowed-all"} 1718 ctx := context.Background() 1719 if actor != nil { 1720 ctx = rbac.NewContext(ctx, actor) 1721 } 1722 1723 resp, _ := api.GetTablets(ctx, &vtadminpb.GetTabletsRequest{}) 1724 assert.NotEmpty(t, resp.Tablets, "actor %+v should be permitted to GetTablets", actor) 1725 clusterAliases := map[string][]string{} 1726 for _, tablet := range resp.Tablets { 1727 if _, ok := clusterAliases[tablet.Cluster.Id]; !ok { 1728 clusterAliases[tablet.Cluster.Id] = []string{} 1729 } 1730 clusterAliases[tablet.Cluster.Id] = append(clusterAliases[tablet.Cluster.Id], topoproto.TabletAliasString(tablet.Tablet.Alias)) 1731 } 1732 assert.Equal(t, clusterAliases, map[string][]string{"test": {"zone1-0000000100"}, "other": {"other1-0000000100"}}, "actor %+v should be permitted to GetTablets", actor) 1733 }) 1734 } 1735 1736 func TestGetVSchema(t *testing.T) { 1737 t.Parallel() 1738 1739 opts := vtadmin.Options{ 1740 RBAC: &rbac.Config{ 1741 Rules: []*struct { 1742 Resource string 1743 Actions []string 1744 Subjects []string 1745 Clusters []string 1746 }{ 1747 { 1748 Resource: "VSchema", 1749 Actions: []string{"get"}, 1750 Subjects: []string{"user:allowed"}, 1751 Clusters: []string{"*"}, 1752 }, 1753 }, 1754 }, 1755 } 1756 err := opts.RBAC.Reify() 1757 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1758 1759 api := vtadmin.NewAPI(testClusters(t), opts) 1760 t.Cleanup(func() { 1761 if err := api.Close(); err != nil { 1762 t.Logf("api did not close cleanly: %s", err.Error()) 1763 } 1764 }) 1765 1766 t.Run("unauthorized actor", func(t *testing.T) { 1767 t.Parallel() 1768 1769 actor := &rbac.Actor{Name: "other"} 1770 ctx := context.Background() 1771 if actor != nil { 1772 ctx = rbac.NewContext(ctx, actor) 1773 } 1774 1775 resp, err := api.GetVSchema(ctx, &vtadminpb.GetVSchemaRequest{ 1776 ClusterId: "test", 1777 Keyspace: "test", 1778 }) 1779 require.NoError(t, err) 1780 assert.Nil(t, resp, "actor %+v should not be permitted to GetVSchema", actor) 1781 }) 1782 1783 t.Run("authorized actor", func(t *testing.T) { 1784 t.Parallel() 1785 1786 actor := &rbac.Actor{Name: "allowed"} 1787 ctx := context.Background() 1788 if actor != nil { 1789 ctx = rbac.NewContext(ctx, actor) 1790 } 1791 1792 resp, err := api.GetVSchema(ctx, &vtadminpb.GetVSchemaRequest{ 1793 ClusterId: "test", 1794 Keyspace: "test", 1795 }) 1796 require.NoError(t, err) 1797 assert.NotNil(t, resp, "actor %+v should be permitted to GetVSchema", actor) 1798 }) 1799 } 1800 1801 func TestGetVSchemas(t *testing.T) { 1802 t.Parallel() 1803 1804 opts := vtadmin.Options{ 1805 RBAC: &rbac.Config{ 1806 Rules: []*struct { 1807 Resource string 1808 Actions []string 1809 Subjects []string 1810 Clusters []string 1811 }{ 1812 { 1813 Resource: "VSchema", 1814 Actions: []string{"get"}, 1815 Subjects: []string{"user:allowed-all"}, 1816 Clusters: []string{"*"}, 1817 }, 1818 { 1819 Resource: "VSchema", 1820 Actions: []string{"get"}, 1821 Subjects: []string{"user:allowed-other"}, 1822 Clusters: []string{"other"}, 1823 }, 1824 }, 1825 }, 1826 } 1827 err := opts.RBAC.Reify() 1828 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1829 1830 api := vtadmin.NewAPI(testClusters(t), opts) 1831 t.Cleanup(func() { 1832 if err := api.Close(); err != nil { 1833 t.Logf("api did not close cleanly: %s", err.Error()) 1834 } 1835 }) 1836 1837 t.Run("unauthorized actor", func(t *testing.T) { 1838 t.Parallel() 1839 1840 actor := &rbac.Actor{Name: "unauthorized"} 1841 ctx := context.Background() 1842 if actor != nil { 1843 ctx = rbac.NewContext(ctx, actor) 1844 } 1845 1846 resp, err := api.GetVSchemas(ctx, &vtadminpb.GetVSchemasRequest{}) 1847 require.NoError(t, err) 1848 assert.Nil(t, resp.VSchemas, "actor %+v should not be permitted to GetVSchemas", actor) 1849 }) 1850 1851 t.Run("partial access", func(t *testing.T) { 1852 t.Parallel() 1853 1854 actor := &rbac.Actor{Name: "allowed-other"} 1855 ctx := context.Background() 1856 if actor != nil { 1857 ctx = rbac.NewContext(ctx, actor) 1858 } 1859 1860 resp, _ := api.GetVSchemas(ctx, &vtadminpb.GetVSchemasRequest{}) 1861 assert.NotEmpty(t, resp.VSchemas, "actor %+v should be permitted to GetVSchemas", actor) 1862 clusterKeyspaces := map[string][]string{} 1863 for _, vs := range resp.VSchemas { 1864 if _, ok := clusterKeyspaces[vs.Cluster.Id]; !ok { 1865 clusterKeyspaces[vs.Cluster.Id] = []string{} 1866 } 1867 clusterKeyspaces[vs.Cluster.Id] = append(clusterKeyspaces[vs.Cluster.Id], vs.Name) 1868 } 1869 assert.Equal(t, clusterKeyspaces, map[string][]string{"other": {"otherks"}}, "actor %+v should be permitted to GetVSchemas", actor) 1870 }) 1871 1872 t.Run("full access", func(t *testing.T) { 1873 t.Parallel() 1874 1875 actor := &rbac.Actor{Name: "allowed-all"} 1876 ctx := context.Background() 1877 if actor != nil { 1878 ctx = rbac.NewContext(ctx, actor) 1879 } 1880 1881 resp, _ := api.GetVSchemas(ctx, &vtadminpb.GetVSchemasRequest{}) 1882 assert.NotEmpty(t, resp.VSchemas, "actor %+v should be permitted to GetVSchemas", actor) 1883 clusterKeyspaces := map[string][]string{} 1884 for _, vs := range resp.VSchemas { 1885 if _, ok := clusterKeyspaces[vs.Cluster.Id]; !ok { 1886 clusterKeyspaces[vs.Cluster.Id] = []string{} 1887 } 1888 clusterKeyspaces[vs.Cluster.Id] = append(clusterKeyspaces[vs.Cluster.Id], vs.Name) 1889 } 1890 assert.Equal(t, clusterKeyspaces, map[string][]string{"test": {"test"}, "other": {"otherks"}}, "actor %+v should be permitted to GetVSchemas", actor) 1891 }) 1892 } 1893 1894 func TestGetVtctlds(t *testing.T) { 1895 t.Parallel() 1896 1897 opts := vtadmin.Options{ 1898 RBAC: &rbac.Config{ 1899 Rules: []*struct { 1900 Resource string 1901 Actions []string 1902 Subjects []string 1903 Clusters []string 1904 }{ 1905 { 1906 Resource: "Vtctld", 1907 Actions: []string{"get"}, 1908 Subjects: []string{"user:allowed-all"}, 1909 Clusters: []string{"*"}, 1910 }, 1911 { 1912 Resource: "Vtctld", 1913 Actions: []string{"get"}, 1914 Subjects: []string{"user:allowed-other"}, 1915 Clusters: []string{"other"}, 1916 }, 1917 }, 1918 }, 1919 } 1920 err := opts.RBAC.Reify() 1921 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 1922 1923 t.Run("unauthorized actor", func(t *testing.T) { 1924 t.Parallel() 1925 1926 api := vtadmin.NewAPI(testClusters(t), opts) 1927 t.Cleanup(func() { 1928 if err := api.Close(); err != nil { 1929 t.Logf("api did not close cleanly: %s", err.Error()) 1930 } 1931 }) 1932 1933 actor := &rbac.Actor{Name: "unauthorized"} 1934 ctx := context.Background() 1935 if actor != nil { 1936 ctx = rbac.NewContext(ctx, actor) 1937 } 1938 1939 resp, err := api.GetVtctlds(ctx, &vtadminpb.GetVtctldsRequest{}) 1940 assert.NoError(t, err) 1941 assert.Empty(t, resp.Vtctlds, "actor %+v should not be permitted to GetVtctlds", actor) 1942 }) 1943 1944 t.Run("partial access", func(t *testing.T) { 1945 t.Parallel() 1946 1947 api := vtadmin.NewAPI(testClusters(t), opts) 1948 t.Cleanup(func() { 1949 if err := api.Close(); err != nil { 1950 t.Logf("api did not close cleanly: %s", err.Error()) 1951 } 1952 }) 1953 1954 actor := &rbac.Actor{Name: "allowed-other"} 1955 ctx := context.Background() 1956 if actor != nil { 1957 ctx = rbac.NewContext(ctx, actor) 1958 } 1959 1960 resp, _ := api.GetVtctlds(ctx, &vtadminpb.GetVtctldsRequest{}) 1961 assert.NotEmpty(t, resp.Vtctlds, "actor %+v should be permitted to GetVtctlds", actor) 1962 // testutil.BuildCluster creates exactly one gate per cluster 1963 assert.Len(t, resp.Vtctlds, 1, "actor %+v should only be able to see Vtctld from cluster 'other'", actor) 1964 }) 1965 1966 t.Run("full access", func(t *testing.T) { 1967 t.Parallel() 1968 1969 api := vtadmin.NewAPI(testClusters(t), opts) 1970 t.Cleanup(func() { 1971 if err := api.Close(); err != nil { 1972 t.Logf("api did not close cleanly: %s", err.Error()) 1973 } 1974 }) 1975 1976 actor := &rbac.Actor{Name: "allowed-all"} 1977 ctx := context.Background() 1978 if actor != nil { 1979 ctx = rbac.NewContext(ctx, actor) 1980 } 1981 1982 resp, _ := api.GetVtctlds(ctx, &vtadminpb.GetVtctldsRequest{}) 1983 assert.NotEmpty(t, resp.Vtctlds, "actor %+v should be permitted to GetVtctlds", actor) 1984 // testutil.BuildCluster creates exactly one gate per cluster 1985 assert.Len(t, resp.Vtctlds, 2, "actor %+v should be able to see Vtctld from all clusters", actor) 1986 }) 1987 } 1988 1989 func TestGetWorkflow(t *testing.T) { 1990 t.Parallel() 1991 1992 opts := vtadmin.Options{ 1993 RBAC: &rbac.Config{ 1994 Rules: []*struct { 1995 Resource string 1996 Actions []string 1997 Subjects []string 1998 Clusters []string 1999 }{ 2000 { 2001 Resource: "Workflow", 2002 Actions: []string{"get"}, 2003 Subjects: []string{"user:allowed"}, 2004 Clusters: []string{"*"}, 2005 }, 2006 }, 2007 }, 2008 } 2009 err := opts.RBAC.Reify() 2010 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2011 2012 api := vtadmin.NewAPI(testClusters(t), opts) 2013 t.Cleanup(func() { 2014 if err := api.Close(); err != nil { 2015 t.Logf("api did not close cleanly: %s", err.Error()) 2016 } 2017 }) 2018 2019 t.Run("unauthorized actor", func(t *testing.T) { 2020 t.Parallel() 2021 2022 actor := &rbac.Actor{Name: "other"} 2023 ctx := context.Background() 2024 if actor != nil { 2025 ctx = rbac.NewContext(ctx, actor) 2026 } 2027 2028 resp, err := api.GetWorkflow(ctx, &vtadminpb.GetWorkflowRequest{ 2029 ClusterId: "test", 2030 Keyspace: "test", 2031 Name: "testworkflow", 2032 }) 2033 require.NoError(t, err) 2034 assert.Nil(t, resp, "actor %+v should not be permitted to GetWorkflow", actor) 2035 }) 2036 2037 t.Run("authorized actor", func(t *testing.T) { 2038 t.Parallel() 2039 2040 actor := &rbac.Actor{Name: "allowed"} 2041 ctx := context.Background() 2042 if actor != nil { 2043 ctx = rbac.NewContext(ctx, actor) 2044 } 2045 2046 resp, err := api.GetWorkflow(ctx, &vtadminpb.GetWorkflowRequest{ 2047 ClusterId: "test", 2048 Keyspace: "test", 2049 Name: "testworkflow", 2050 }) 2051 require.NoError(t, err) 2052 assert.NotNil(t, resp, "actor %+v should be permitted to GetWorkflow", actor) 2053 }) 2054 } 2055 2056 func TestGetWorkflows(t *testing.T) { 2057 t.Parallel() 2058 2059 opts := vtadmin.Options{ 2060 RBAC: &rbac.Config{ 2061 Rules: []*struct { 2062 Resource string 2063 Actions []string 2064 Subjects []string 2065 Clusters []string 2066 }{ 2067 { 2068 Resource: "Workflow", 2069 Actions: []string{"get"}, 2070 Subjects: []string{"user:allowed-all"}, 2071 Clusters: []string{"*"}, 2072 }, 2073 { 2074 Resource: "Workflow", 2075 Actions: []string{"get"}, 2076 Subjects: []string{"user:allowed-other"}, 2077 Clusters: []string{"other"}, 2078 }, 2079 }, 2080 }, 2081 } 2082 err := opts.RBAC.Reify() 2083 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2084 2085 api := vtadmin.NewAPI(testClusters(t), opts) 2086 t.Cleanup(func() { 2087 if err := api.Close(); err != nil { 2088 t.Logf("api did not close cleanly: %s", err.Error()) 2089 } 2090 }) 2091 2092 t.Run("unauthorized actor", func(t *testing.T) { 2093 t.Parallel() 2094 2095 actor := &rbac.Actor{Name: "unauthorized"} 2096 ctx := context.Background() 2097 if actor != nil { 2098 ctx = rbac.NewContext(ctx, actor) 2099 } 2100 2101 resp, err := api.GetWorkflows(ctx, &vtadminpb.GetWorkflowsRequest{}) 2102 require.NoError(t, err) 2103 assert.Empty(t, resp.WorkflowsByCluster, "actor %+v should not be permitted to GetWorkflows", actor) 2104 }) 2105 2106 t.Run("partial access", func(t *testing.T) { 2107 t.Parallel() 2108 2109 actor := &rbac.Actor{Name: "allowed-other"} 2110 ctx := context.Background() 2111 if actor != nil { 2112 ctx = rbac.NewContext(ctx, actor) 2113 } 2114 2115 resp, _ := api.GetWorkflows(ctx, &vtadminpb.GetWorkflowsRequest{}) 2116 assert.NotEmpty(t, resp.WorkflowsByCluster, "actor %+v should be permitted to GetWorkflows", actor) 2117 assert.Equal(t, resp.WorkflowsByCluster, map[string]*vtadminpb.ClusterWorkflows{"other": {Workflows: []*vtadminpb.Workflow{{Cluster: &vtadminpb.Cluster{Id: "other", Name: "other"}, Keyspace: "otherks", Workflow: &vtctldatapb.Workflow{Name: "otherks_workflow"}}}}}, "actor %+v should be permitted to GetWorkflows", actor) 2118 }) 2119 2120 t.Run("full access", func(t *testing.T) { 2121 t.Parallel() 2122 2123 actor := &rbac.Actor{Name: "allowed-all"} 2124 ctx := context.Background() 2125 if actor != nil { 2126 ctx = rbac.NewContext(ctx, actor) 2127 } 2128 2129 resp, _ := api.GetWorkflows(ctx, &vtadminpb.GetWorkflowsRequest{}) 2130 assert.NotEmpty(t, resp.WorkflowsByCluster, "actor %+v should be permitted to GetWorkflows", actor) 2131 assert.Equal(t, resp.WorkflowsByCluster, map[string]*vtadminpb.ClusterWorkflows{"test": {Workflows: []*vtadminpb.Workflow{{Cluster: &vtadminpb.Cluster{Id: "test", Name: "test"}, Keyspace: "test", Workflow: &vtctldatapb.Workflow{Name: "testworkflow"}}}}, "other": {Workflows: []*vtadminpb.Workflow{{Cluster: &vtadminpb.Cluster{Id: "other", Name: "other"}, Keyspace: "otherks", Workflow: &vtctldatapb.Workflow{Name: "otherks_workflow"}}}}}, "actor %+v should be permitted to GetWorkflows", actor) 2132 }) 2133 } 2134 2135 func TestPingTablet(t *testing.T) { 2136 t.Parallel() 2137 2138 opts := vtadmin.Options{ 2139 RBAC: &rbac.Config{ 2140 Rules: []*struct { 2141 Resource string 2142 Actions []string 2143 Subjects []string 2144 Clusters []string 2145 }{ 2146 { 2147 Resource: "Tablet", 2148 Actions: []string{"ping"}, 2149 Subjects: []string{"user:allowed"}, 2150 Clusters: []string{"*"}, 2151 }, 2152 }, 2153 }, 2154 } 2155 err := opts.RBAC.Reify() 2156 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2157 2158 api := vtadmin.NewAPI(testClusters(t), opts) 2159 t.Cleanup(func() { 2160 if err := api.Close(); err != nil { 2161 t.Logf("api did not close cleanly: %s", err.Error()) 2162 } 2163 }) 2164 2165 t.Run("unauthorized actor", func(t *testing.T) { 2166 t.Parallel() 2167 2168 actor := &rbac.Actor{Name: "other"} 2169 ctx := context.Background() 2170 if actor != nil { 2171 ctx = rbac.NewContext(ctx, actor) 2172 } 2173 2174 resp, err := api.PingTablet(ctx, &vtadminpb.PingTabletRequest{ 2175 Alias: &topodatapb.TabletAlias{ 2176 Cell: "zone1", 2177 Uid: 100, 2178 }, 2179 }) 2180 assert.Error(t, err, "actor %+v should not be permitted to PingTablet", actor) 2181 assert.Nil(t, resp, "actor %+v should not be permitted to PingTablet", actor) 2182 }) 2183 2184 t.Run("authorized actor", func(t *testing.T) { 2185 t.Parallel() 2186 2187 actor := &rbac.Actor{Name: "allowed"} 2188 ctx := context.Background() 2189 if actor != nil { 2190 ctx = rbac.NewContext(ctx, actor) 2191 } 2192 2193 resp, err := api.PingTablet(ctx, &vtadminpb.PingTabletRequest{ 2194 Alias: &topodatapb.TabletAlias{ 2195 Cell: "zone1", 2196 Uid: 100, 2197 }, 2198 }) 2199 require.NoError(t, err) 2200 assert.NotNil(t, resp, "actor %+v should be permitted to PingTablet", actor) 2201 }) 2202 } 2203 2204 func TestPlannedFailoverShard(t *testing.T) { 2205 t.Parallel() 2206 2207 opts := vtadmin.Options{ 2208 RBAC: &rbac.Config{ 2209 Rules: []*struct { 2210 Resource string 2211 Actions []string 2212 Subjects []string 2213 Clusters []string 2214 }{ 2215 { 2216 Resource: "Shard", 2217 Actions: []string{"planned_failover_shard"}, 2218 Subjects: []string{"user:allowed"}, 2219 Clusters: []string{"*"}, 2220 }, 2221 }, 2222 }, 2223 } 2224 err := opts.RBAC.Reify() 2225 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2226 2227 api := vtadmin.NewAPI(testClusters(t), opts) 2228 t.Cleanup(func() { 2229 if err := api.Close(); err != nil { 2230 t.Logf("api did not close cleanly: %s", err.Error()) 2231 } 2232 }) 2233 2234 t.Run("unauthorized actor", func(t *testing.T) { 2235 t.Parallel() 2236 2237 actor := &rbac.Actor{Name: "other"} 2238 ctx := context.Background() 2239 if actor != nil { 2240 ctx = rbac.NewContext(ctx, actor) 2241 } 2242 2243 resp, err := api.PlannedFailoverShard(ctx, &vtadminpb.PlannedFailoverShardRequest{ 2244 ClusterId: "test", 2245 Options: &vtctldatapb.PlannedReparentShardRequest{ 2246 Keyspace: "test", 2247 Shard: "-", 2248 }, 2249 }) 2250 require.NoError(t, err) 2251 assert.Nil(t, resp, "actor %+v should not be permitted to PlannedFailoverShard", actor) 2252 }) 2253 2254 t.Run("authorized actor", func(t *testing.T) { 2255 t.Parallel() 2256 2257 actor := &rbac.Actor{Name: "allowed"} 2258 ctx := context.Background() 2259 if actor != nil { 2260 ctx = rbac.NewContext(ctx, actor) 2261 } 2262 2263 resp, err := api.PlannedFailoverShard(ctx, &vtadminpb.PlannedFailoverShardRequest{ 2264 ClusterId: "test", 2265 Options: &vtctldatapb.PlannedReparentShardRequest{ 2266 Keyspace: "test", 2267 Shard: "-", 2268 }, 2269 }) 2270 require.NoError(t, err) 2271 assert.NotNil(t, resp, "actor %+v should be permitted to PlannedFailoverShard", actor) 2272 }) 2273 } 2274 2275 func TestRefreshState(t *testing.T) { 2276 t.Parallel() 2277 2278 opts := vtadmin.Options{ 2279 RBAC: &rbac.Config{ 2280 Rules: []*struct { 2281 Resource string 2282 Actions []string 2283 Subjects []string 2284 Clusters []string 2285 }{ 2286 { 2287 Resource: "Tablet", 2288 Actions: []string{"put"}, 2289 Subjects: []string{"user:allowed"}, 2290 Clusters: []string{"*"}, 2291 }, 2292 }, 2293 }, 2294 } 2295 err := opts.RBAC.Reify() 2296 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2297 2298 api := vtadmin.NewAPI(testClusters(t), opts) 2299 t.Cleanup(func() { 2300 if err := api.Close(); err != nil { 2301 t.Logf("api did not close cleanly: %s", err.Error()) 2302 } 2303 }) 2304 2305 t.Run("unauthorized actor", func(t *testing.T) { 2306 t.Parallel() 2307 2308 actor := &rbac.Actor{Name: "other"} 2309 ctx := context.Background() 2310 if actor != nil { 2311 ctx = rbac.NewContext(ctx, actor) 2312 } 2313 2314 resp, err := api.RefreshState(ctx, &vtadminpb.RefreshStateRequest{ 2315 Alias: &topodatapb.TabletAlias{ 2316 Cell: "zone1", 2317 Uid: 100, 2318 }, 2319 }) 2320 assert.Error(t, err, "actor %+v should not be permitted to RefreshState", actor) 2321 assert.Nil(t, resp, "actor %+v should not be permitted to RefreshState", actor) 2322 }) 2323 2324 t.Run("authorized actor", func(t *testing.T) { 2325 t.Parallel() 2326 2327 actor := &rbac.Actor{Name: "allowed"} 2328 ctx := context.Background() 2329 if actor != nil { 2330 ctx = rbac.NewContext(ctx, actor) 2331 } 2332 2333 resp, err := api.RefreshState(ctx, &vtadminpb.RefreshStateRequest{ 2334 Alias: &topodatapb.TabletAlias{ 2335 Cell: "zone1", 2336 Uid: 100, 2337 }, 2338 }) 2339 require.NoError(t, err) 2340 assert.NotNil(t, resp, "actor %+v should be permitted to RefreshState", actor) 2341 }) 2342 } 2343 2344 func TestRefreshTabletReplicationSource(t *testing.T) { 2345 t.Parallel() 2346 2347 opts := vtadmin.Options{ 2348 RBAC: &rbac.Config{ 2349 Rules: []*struct { 2350 Resource string 2351 Actions []string 2352 Subjects []string 2353 Clusters []string 2354 }{ 2355 { 2356 Resource: "Tablet", 2357 Actions: []string{"refresh_tablet_replication_source"}, 2358 Subjects: []string{"user:allowed"}, 2359 Clusters: []string{"*"}, 2360 }, 2361 }, 2362 }, 2363 } 2364 err := opts.RBAC.Reify() 2365 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2366 2367 api := vtadmin.NewAPI(testClusters(t), opts) 2368 t.Cleanup(func() { 2369 if err := api.Close(); err != nil { 2370 t.Logf("api did not close cleanly: %s", err.Error()) 2371 } 2372 }) 2373 2374 t.Run("unauthorized actor", func(t *testing.T) { 2375 t.Parallel() 2376 2377 actor := &rbac.Actor{Name: "other"} 2378 ctx := context.Background() 2379 if actor != nil { 2380 ctx = rbac.NewContext(ctx, actor) 2381 } 2382 2383 resp, err := api.RefreshTabletReplicationSource(ctx, &vtadminpb.RefreshTabletReplicationSourceRequest{ 2384 Alias: &topodatapb.TabletAlias{ 2385 Cell: "zone1", 2386 Uid: 100, 2387 }, 2388 }) 2389 assert.Error(t, err, "actor %+v should not be permitted to RefreshTabletReplicationSource", actor) 2390 assert.Nil(t, resp, "actor %+v should not be permitted to RefreshTabletReplicationSource", actor) 2391 }) 2392 2393 t.Run("authorized actor", func(t *testing.T) { 2394 t.Parallel() 2395 2396 actor := &rbac.Actor{Name: "allowed"} 2397 ctx := context.Background() 2398 if actor != nil { 2399 ctx = rbac.NewContext(ctx, actor) 2400 } 2401 2402 resp, err := api.RefreshTabletReplicationSource(ctx, &vtadminpb.RefreshTabletReplicationSourceRequest{ 2403 Alias: &topodatapb.TabletAlias{ 2404 Cell: "zone1", 2405 Uid: 100, 2406 }, 2407 }) 2408 require.NoError(t, err) 2409 assert.NotNil(t, resp, "actor %+v should be permitted to RefreshTabletReplicationSource", actor) 2410 }) 2411 } 2412 2413 func TestReloadSchemas(t *testing.T) { 2414 t.Parallel() 2415 2416 opts := vtadmin.Options{ 2417 RBAC: &rbac.Config{ 2418 Rules: []*struct { 2419 Resource string 2420 Actions []string 2421 Subjects []string 2422 Clusters []string 2423 }{ 2424 { 2425 Resource: "Schema", 2426 Actions: []string{"reload"}, 2427 Subjects: []string{"user:allowed-all"}, 2428 Clusters: []string{"*"}, 2429 }, 2430 { 2431 Resource: "Schema", 2432 Actions: []string{"reload"}, 2433 Subjects: []string{"user:allowed-other"}, 2434 Clusters: []string{"other"}, 2435 }, 2436 }, 2437 }, 2438 } 2439 err := opts.RBAC.Reify() 2440 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2441 2442 api := vtadmin.NewAPI(testClusters(t), opts) 2443 t.Cleanup(func() { 2444 if err := api.Close(); err != nil { 2445 t.Logf("api did not close cleanly: %s", err.Error()) 2446 } 2447 }) 2448 2449 t.Run("unauthorized actor", func(t *testing.T) { 2450 t.Parallel() 2451 2452 actor := &rbac.Actor{Name: "unauthorized"} 2453 ctx := context.Background() 2454 if actor != nil { 2455 ctx = rbac.NewContext(ctx, actor) 2456 } 2457 2458 resp, err := api.ReloadSchemas(ctx, &vtadminpb.ReloadSchemasRequest{ 2459 Keyspaces: []string{ 2460 "test", 2461 "otherks", 2462 }, 2463 }) 2464 require.NoError(t, err) 2465 assert.Empty(t, resp.KeyspaceResults, "actor %+v should not be permitted to ReloadSchemas", actor) 2466 }) 2467 2468 t.Run("partial access", func(t *testing.T) { 2469 t.Parallel() 2470 2471 actor := &rbac.Actor{Name: "allowed-other"} 2472 ctx := context.Background() 2473 if actor != nil { 2474 ctx = rbac.NewContext(ctx, actor) 2475 } 2476 2477 resp, _ := api.ReloadSchemas(ctx, &vtadminpb.ReloadSchemasRequest{ 2478 Keyspaces: []string{ 2479 "test", 2480 "otherks", 2481 }, 2482 }) 2483 assert.NotEmpty(t, resp.KeyspaceResults, "actor %+v should be permitted to ReloadSchemas", actor) 2484 }) 2485 2486 t.Run("full access", func(t *testing.T) { 2487 t.Parallel() 2488 2489 actor := &rbac.Actor{Name: "allowed-all"} 2490 ctx := context.Background() 2491 if actor != nil { 2492 ctx = rbac.NewContext(ctx, actor) 2493 } 2494 2495 resp, _ := api.ReloadSchemas(ctx, &vtadminpb.ReloadSchemasRequest{ 2496 Keyspaces: []string{ 2497 "test", 2498 "otherks", 2499 }, 2500 }) 2501 assert.NotEmpty(t, resp.KeyspaceResults, "actor %+v should be permitted to ReloadSchemas", actor) 2502 }) 2503 } 2504 2505 func TestRunHealthCheck(t *testing.T) { 2506 t.Parallel() 2507 2508 opts := vtadmin.Options{ 2509 RBAC: &rbac.Config{ 2510 Rules: []*struct { 2511 Resource string 2512 Actions []string 2513 Subjects []string 2514 Clusters []string 2515 }{ 2516 { 2517 Resource: "Tablet", 2518 Actions: []string{"get"}, 2519 Subjects: []string{"user:allowed"}, 2520 Clusters: []string{"*"}, 2521 }, 2522 }, 2523 }, 2524 } 2525 err := opts.RBAC.Reify() 2526 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2527 2528 api := vtadmin.NewAPI(testClusters(t), opts) 2529 t.Cleanup(func() { 2530 if err := api.Close(); err != nil { 2531 t.Logf("api did not close cleanly: %s", err.Error()) 2532 } 2533 }) 2534 2535 t.Run("unauthorized actor", func(t *testing.T) { 2536 t.Parallel() 2537 2538 actor := &rbac.Actor{Name: "other"} 2539 ctx := context.Background() 2540 if actor != nil { 2541 ctx = rbac.NewContext(ctx, actor) 2542 } 2543 2544 resp, err := api.RunHealthCheck(ctx, &vtadminpb.RunHealthCheckRequest{ 2545 Alias: &topodatapb.TabletAlias{ 2546 Cell: "zone1", 2547 Uid: 100, 2548 }, 2549 }) 2550 assert.Error(t, err, "actor %+v should not be permitted to RunHealthCheck", actor) 2551 assert.Nil(t, resp, "actor %+v should not be permitted to RunHealthCheck", actor) 2552 }) 2553 2554 t.Run("authorized actor", func(t *testing.T) { 2555 t.Parallel() 2556 2557 actor := &rbac.Actor{Name: "allowed"} 2558 ctx := context.Background() 2559 if actor != nil { 2560 ctx = rbac.NewContext(ctx, actor) 2561 } 2562 2563 resp, err := api.RunHealthCheck(ctx, &vtadminpb.RunHealthCheckRequest{ 2564 Alias: &topodatapb.TabletAlias{ 2565 Cell: "zone1", 2566 Uid: 100, 2567 }, 2568 }) 2569 require.NoError(t, err) 2570 assert.NotNil(t, resp, "actor %+v should be permitted to RunHealthCheck", actor) 2571 }) 2572 } 2573 2574 func TestSetReadOnly(t *testing.T) { 2575 t.Parallel() 2576 2577 opts := vtadmin.Options{ 2578 RBAC: &rbac.Config{ 2579 Rules: []*struct { 2580 Resource string 2581 Actions []string 2582 Subjects []string 2583 Clusters []string 2584 }{ 2585 { 2586 Resource: "Tablet", 2587 Actions: []string{"manage_tablet_writability"}, 2588 Subjects: []string{"user:allowed"}, 2589 Clusters: []string{"*"}, 2590 }, 2591 }, 2592 }, 2593 } 2594 err := opts.RBAC.Reify() 2595 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2596 2597 api := vtadmin.NewAPI(testClusters(t), opts) 2598 t.Cleanup(func() { 2599 if err := api.Close(); err != nil { 2600 t.Logf("api did not close cleanly: %s", err.Error()) 2601 } 2602 }) 2603 2604 t.Run("unauthorized actor", func(t *testing.T) { 2605 t.Parallel() 2606 2607 actor := &rbac.Actor{Name: "other"} 2608 ctx := context.Background() 2609 if actor != nil { 2610 ctx = rbac.NewContext(ctx, actor) 2611 } 2612 2613 resp, err := api.SetReadOnly(ctx, &vtadminpb.SetReadOnlyRequest{ 2614 Alias: &topodatapb.TabletAlias{ 2615 Cell: "zone1", 2616 Uid: 100, 2617 }, 2618 }) 2619 assert.Error(t, err, "actor %+v should not be permitted to SetReadOnly", actor) 2620 assert.Nil(t, resp, "actor %+v should not be permitted to SetReadOnly", actor) 2621 }) 2622 2623 t.Run("authorized actor", func(t *testing.T) { 2624 t.Parallel() 2625 2626 actor := &rbac.Actor{Name: "allowed"} 2627 ctx := context.Background() 2628 if actor != nil { 2629 ctx = rbac.NewContext(ctx, actor) 2630 } 2631 2632 resp, err := api.SetReadOnly(ctx, &vtadminpb.SetReadOnlyRequest{ 2633 Alias: &topodatapb.TabletAlias{ 2634 Cell: "zone1", 2635 Uid: 100, 2636 }, 2637 }) 2638 require.NoError(t, err) 2639 assert.NotNil(t, resp, "actor %+v should be permitted to SetReadOnly", actor) 2640 }) 2641 } 2642 2643 func TestSetReadWrite(t *testing.T) { 2644 t.Parallel() 2645 2646 opts := vtadmin.Options{ 2647 RBAC: &rbac.Config{ 2648 Rules: []*struct { 2649 Resource string 2650 Actions []string 2651 Subjects []string 2652 Clusters []string 2653 }{ 2654 { 2655 Resource: "Tablet", 2656 Actions: []string{"manage_tablet_writability"}, 2657 Subjects: []string{"user:allowed"}, 2658 Clusters: []string{"*"}, 2659 }, 2660 }, 2661 }, 2662 } 2663 err := opts.RBAC.Reify() 2664 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2665 2666 api := vtadmin.NewAPI(testClusters(t), opts) 2667 t.Cleanup(func() { 2668 if err := api.Close(); err != nil { 2669 t.Logf("api did not close cleanly: %s", err.Error()) 2670 } 2671 }) 2672 2673 t.Run("unauthorized actor", func(t *testing.T) { 2674 t.Parallel() 2675 2676 actor := &rbac.Actor{Name: "other"} 2677 ctx := context.Background() 2678 if actor != nil { 2679 ctx = rbac.NewContext(ctx, actor) 2680 } 2681 2682 resp, err := api.SetReadWrite(ctx, &vtadminpb.SetReadWriteRequest{ 2683 Alias: &topodatapb.TabletAlias{ 2684 Cell: "zone1", 2685 Uid: 100, 2686 }, 2687 }) 2688 assert.Error(t, err, "actor %+v should not be permitted to SetReadWrite", actor) 2689 assert.Nil(t, resp, "actor %+v should not be permitted to SetReadWrite", actor) 2690 }) 2691 2692 t.Run("authorized actor", func(t *testing.T) { 2693 t.Parallel() 2694 2695 actor := &rbac.Actor{Name: "allowed"} 2696 ctx := context.Background() 2697 if actor != nil { 2698 ctx = rbac.NewContext(ctx, actor) 2699 } 2700 2701 resp, err := api.SetReadWrite(ctx, &vtadminpb.SetReadWriteRequest{ 2702 Alias: &topodatapb.TabletAlias{ 2703 Cell: "zone1", 2704 Uid: 100, 2705 }, 2706 }) 2707 require.NoError(t, err) 2708 assert.NotNil(t, resp, "actor %+v should be permitted to SetReadWrite", actor) 2709 }) 2710 } 2711 2712 func TestStartReplication(t *testing.T) { 2713 t.Parallel() 2714 2715 opts := vtadmin.Options{ 2716 RBAC: &rbac.Config{ 2717 Rules: []*struct { 2718 Resource string 2719 Actions []string 2720 Subjects []string 2721 Clusters []string 2722 }{ 2723 { 2724 Resource: "Tablet", 2725 Actions: []string{"manage_tablet_replication"}, 2726 Subjects: []string{"user:allowed"}, 2727 Clusters: []string{"*"}, 2728 }, 2729 }, 2730 }, 2731 } 2732 err := opts.RBAC.Reify() 2733 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2734 2735 api := vtadmin.NewAPI(testClusters(t), opts) 2736 t.Cleanup(func() { 2737 if err := api.Close(); err != nil { 2738 t.Logf("api did not close cleanly: %s", err.Error()) 2739 } 2740 }) 2741 2742 t.Run("unauthorized actor", func(t *testing.T) { 2743 t.Parallel() 2744 2745 actor := &rbac.Actor{Name: "other"} 2746 ctx := context.Background() 2747 if actor != nil { 2748 ctx = rbac.NewContext(ctx, actor) 2749 } 2750 2751 resp, err := api.StartReplication(ctx, &vtadminpb.StartReplicationRequest{ 2752 Alias: &topodatapb.TabletAlias{ 2753 Cell: "zone1", 2754 Uid: 100, 2755 }, 2756 }) 2757 assert.Error(t, err, "actor %+v should not be permitted to StartReplication", actor) 2758 assert.Nil(t, resp, "actor %+v should not be permitted to StartReplication", actor) 2759 }) 2760 2761 t.Run("authorized actor", func(t *testing.T) { 2762 t.Parallel() 2763 2764 actor := &rbac.Actor{Name: "allowed"} 2765 ctx := context.Background() 2766 if actor != nil { 2767 ctx = rbac.NewContext(ctx, actor) 2768 } 2769 2770 resp, err := api.StartReplication(ctx, &vtadminpb.StartReplicationRequest{ 2771 Alias: &topodatapb.TabletAlias{ 2772 Cell: "zone1", 2773 Uid: 100, 2774 }, 2775 }) 2776 require.NoError(t, err) 2777 assert.NotNil(t, resp, "actor %+v should be permitted to StartReplication", actor) 2778 }) 2779 } 2780 2781 func TestStopReplication(t *testing.T) { 2782 t.Parallel() 2783 2784 opts := vtadmin.Options{ 2785 RBAC: &rbac.Config{ 2786 Rules: []*struct { 2787 Resource string 2788 Actions []string 2789 Subjects []string 2790 Clusters []string 2791 }{ 2792 { 2793 Resource: "Tablet", 2794 Actions: []string{"manage_tablet_replication"}, 2795 Subjects: []string{"user:allowed"}, 2796 Clusters: []string{"*"}, 2797 }, 2798 }, 2799 }, 2800 } 2801 err := opts.RBAC.Reify() 2802 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2803 2804 api := vtadmin.NewAPI(testClusters(t), opts) 2805 t.Cleanup(func() { 2806 if err := api.Close(); err != nil { 2807 t.Logf("api did not close cleanly: %s", err.Error()) 2808 } 2809 }) 2810 2811 t.Run("unauthorized actor", func(t *testing.T) { 2812 t.Parallel() 2813 2814 actor := &rbac.Actor{Name: "other"} 2815 ctx := context.Background() 2816 if actor != nil { 2817 ctx = rbac.NewContext(ctx, actor) 2818 } 2819 2820 resp, err := api.StopReplication(ctx, &vtadminpb.StopReplicationRequest{ 2821 Alias: &topodatapb.TabletAlias{ 2822 Cell: "zone1", 2823 Uid: 100, 2824 }, 2825 }) 2826 assert.Error(t, err, "actor %+v should not be permitted to StopReplication", actor) 2827 assert.Nil(t, resp, "actor %+v should not be permitted to StopReplication", actor) 2828 }) 2829 2830 t.Run("authorized actor", func(t *testing.T) { 2831 t.Parallel() 2832 2833 actor := &rbac.Actor{Name: "allowed"} 2834 ctx := context.Background() 2835 if actor != nil { 2836 ctx = rbac.NewContext(ctx, actor) 2837 } 2838 2839 resp, err := api.StopReplication(ctx, &vtadminpb.StopReplicationRequest{ 2840 Alias: &topodatapb.TabletAlias{ 2841 Cell: "zone1", 2842 Uid: 100, 2843 }, 2844 }) 2845 require.NoError(t, err) 2846 assert.NotNil(t, resp, "actor %+v should be permitted to StopReplication", actor) 2847 }) 2848 } 2849 2850 func TestTabletExternallyPromoted(t *testing.T) { 2851 t.Parallel() 2852 2853 opts := vtadmin.Options{ 2854 RBAC: &rbac.Config{ 2855 Rules: []*struct { 2856 Resource string 2857 Actions []string 2858 Subjects []string 2859 Clusters []string 2860 }{ 2861 { 2862 Resource: "Shard", 2863 Actions: []string{"tablet_externally_promoted"}, 2864 Subjects: []string{"user:allowed"}, 2865 Clusters: []string{"*"}, 2866 }, 2867 }, 2868 }, 2869 } 2870 err := opts.RBAC.Reify() 2871 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2872 2873 api := vtadmin.NewAPI(testClusters(t), opts) 2874 t.Cleanup(func() { 2875 if err := api.Close(); err != nil { 2876 t.Logf("api did not close cleanly: %s", err.Error()) 2877 } 2878 }) 2879 2880 t.Run("unauthorized actor", func(t *testing.T) { 2881 t.Parallel() 2882 2883 actor := &rbac.Actor{Name: "other"} 2884 ctx := context.Background() 2885 if actor != nil { 2886 ctx = rbac.NewContext(ctx, actor) 2887 } 2888 2889 resp, err := api.TabletExternallyPromoted(ctx, &vtadminpb.TabletExternallyPromotedRequest{ 2890 Alias: &topodatapb.TabletAlias{ 2891 Cell: "zone1", 2892 Uid: 100, 2893 }, 2894 }) 2895 assert.Error(t, err, "actor %+v should not be permitted to TabletExternallyPromoted", actor) 2896 assert.Nil(t, resp, "actor %+v should not be permitted to TabletExternallyPromoted", actor) 2897 }) 2898 2899 t.Run("authorized actor", func(t *testing.T) { 2900 t.Parallel() 2901 2902 actor := &rbac.Actor{Name: "allowed"} 2903 ctx := context.Background() 2904 if actor != nil { 2905 ctx = rbac.NewContext(ctx, actor) 2906 } 2907 2908 resp, err := api.TabletExternallyPromoted(ctx, &vtadminpb.TabletExternallyPromotedRequest{ 2909 Alias: &topodatapb.TabletAlias{ 2910 Cell: "zone1", 2911 Uid: 100, 2912 }, 2913 }) 2914 require.NoError(t, err) 2915 assert.NotNil(t, resp, "actor %+v should be permitted to TabletExternallyPromoted", actor) 2916 }) 2917 } 2918 2919 func TestVTExplain(t *testing.T) { 2920 t.Parallel() 2921 2922 opts := vtadmin.Options{ 2923 RBAC: &rbac.Config{ 2924 Rules: []*struct { 2925 Resource string 2926 Actions []string 2927 Subjects []string 2928 Clusters []string 2929 }{ 2930 { 2931 Resource: "VTExplain", 2932 Actions: []string{"get"}, 2933 Subjects: []string{"user:allowed"}, 2934 Clusters: []string{"*"}, 2935 }, 2936 }, 2937 }, 2938 } 2939 err := opts.RBAC.Reify() 2940 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 2941 2942 api := vtadmin.NewAPI(testClusters(t), opts) 2943 t.Cleanup(func() { 2944 if err := api.Close(); err != nil { 2945 t.Logf("api did not close cleanly: %s", err.Error()) 2946 } 2947 }) 2948 2949 t.Run("unauthorized actor", func(t *testing.T) { 2950 t.Parallel() 2951 2952 actor := &rbac.Actor{Name: "other"} 2953 ctx := context.Background() 2954 if actor != nil { 2955 ctx = rbac.NewContext(ctx, actor) 2956 } 2957 2958 resp, err := api.VTExplain(ctx, &vtadminpb.VTExplainRequest{ 2959 Cluster: "test", 2960 Keyspace: "test", 2961 Sql: "select id from t1;"}) 2962 require.NoError(t, err) 2963 assert.Nil(t, resp, "actor %+v should not be permitted to VTExplain", actor) 2964 }) 2965 2966 t.Run("authorized actor", func(t *testing.T) { 2967 t.Parallel() 2968 2969 actor := &rbac.Actor{Name: "allowed"} 2970 ctx := context.Background() 2971 if actor != nil { 2972 ctx = rbac.NewContext(ctx, actor) 2973 } 2974 2975 resp, err := api.VTExplain(ctx, &vtadminpb.VTExplainRequest{ 2976 Cluster: "test", 2977 Keyspace: "test", 2978 Sql: "select id from t1;"}) 2979 require.NoError(t, err) 2980 assert.NotNil(t, resp, "actor %+v should be permitted to VTExplain", actor) 2981 }) 2982 } 2983 2984 func TestValidateKeyspace(t *testing.T) { 2985 t.Parallel() 2986 2987 opts := vtadmin.Options{ 2988 RBAC: &rbac.Config{ 2989 Rules: []*struct { 2990 Resource string 2991 Actions []string 2992 Subjects []string 2993 Clusters []string 2994 }{ 2995 { 2996 Resource: "Keyspace", 2997 Actions: []string{"put"}, 2998 Subjects: []string{"user:allowed"}, 2999 Clusters: []string{"*"}, 3000 }, 3001 }, 3002 }, 3003 } 3004 err := opts.RBAC.Reify() 3005 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 3006 3007 api := vtadmin.NewAPI(testClusters(t), opts) 3008 t.Cleanup(func() { 3009 if err := api.Close(); err != nil { 3010 t.Logf("api did not close cleanly: %s", err.Error()) 3011 } 3012 }) 3013 3014 t.Run("unauthorized actor", func(t *testing.T) { 3015 t.Parallel() 3016 3017 actor := &rbac.Actor{Name: "other"} 3018 ctx := context.Background() 3019 if actor != nil { 3020 ctx = rbac.NewContext(ctx, actor) 3021 } 3022 3023 resp, err := api.ValidateKeyspace(ctx, &vtadminpb.ValidateKeyspaceRequest{ 3024 ClusterId: "test", 3025 Keyspace: "test", 3026 }) 3027 require.NoError(t, err) 3028 assert.Nil(t, resp, "actor %+v should not be permitted to ValidateKeyspace", actor) 3029 }) 3030 3031 t.Run("authorized actor", func(t *testing.T) { 3032 t.Parallel() 3033 3034 actor := &rbac.Actor{Name: "allowed"} 3035 ctx := context.Background() 3036 if actor != nil { 3037 ctx = rbac.NewContext(ctx, actor) 3038 } 3039 3040 resp, err := api.ValidateKeyspace(ctx, &vtadminpb.ValidateKeyspaceRequest{ 3041 ClusterId: "test", 3042 Keyspace: "test", 3043 }) 3044 require.NoError(t, err) 3045 assert.NotNil(t, resp, "actor %+v should be permitted to ValidateKeyspace", actor) 3046 }) 3047 } 3048 3049 func TestValidateSchemaKeyspace(t *testing.T) { 3050 t.Parallel() 3051 3052 opts := vtadmin.Options{ 3053 RBAC: &rbac.Config{ 3054 Rules: []*struct { 3055 Resource string 3056 Actions []string 3057 Subjects []string 3058 Clusters []string 3059 }{ 3060 { 3061 Resource: "Keyspace", 3062 Actions: []string{"put"}, 3063 Subjects: []string{"user:allowed"}, 3064 Clusters: []string{"*"}, 3065 }, 3066 }, 3067 }, 3068 } 3069 err := opts.RBAC.Reify() 3070 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 3071 3072 api := vtadmin.NewAPI(testClusters(t), opts) 3073 t.Cleanup(func() { 3074 if err := api.Close(); err != nil { 3075 t.Logf("api did not close cleanly: %s", err.Error()) 3076 } 3077 }) 3078 3079 t.Run("unauthorized actor", func(t *testing.T) { 3080 t.Parallel() 3081 3082 actor := &rbac.Actor{Name: "other"} 3083 ctx := context.Background() 3084 if actor != nil { 3085 ctx = rbac.NewContext(ctx, actor) 3086 } 3087 3088 resp, err := api.ValidateSchemaKeyspace(ctx, &vtadminpb.ValidateSchemaKeyspaceRequest{ 3089 ClusterId: "test", 3090 Keyspace: "test", 3091 }) 3092 require.NoError(t, err) 3093 assert.Nil(t, resp, "actor %+v should not be permitted to ValidateSchemaKeyspace", actor) 3094 }) 3095 3096 t.Run("authorized actor", func(t *testing.T) { 3097 t.Parallel() 3098 3099 actor := &rbac.Actor{Name: "allowed"} 3100 ctx := context.Background() 3101 if actor != nil { 3102 ctx = rbac.NewContext(ctx, actor) 3103 } 3104 3105 resp, err := api.ValidateSchemaKeyspace(ctx, &vtadminpb.ValidateSchemaKeyspaceRequest{ 3106 ClusterId: "test", 3107 Keyspace: "test", 3108 }) 3109 require.NoError(t, err) 3110 assert.NotNil(t, resp, "actor %+v should be permitted to ValidateSchemaKeyspace", actor) 3111 }) 3112 } 3113 3114 func TestValidateVersionKeyspace(t *testing.T) { 3115 t.Parallel() 3116 3117 opts := vtadmin.Options{ 3118 RBAC: &rbac.Config{ 3119 Rules: []*struct { 3120 Resource string 3121 Actions []string 3122 Subjects []string 3123 Clusters []string 3124 }{ 3125 { 3126 Resource: "Keyspace", 3127 Actions: []string{"put"}, 3128 Subjects: []string{"user:allowed"}, 3129 Clusters: []string{"*"}, 3130 }, 3131 }, 3132 }, 3133 } 3134 err := opts.RBAC.Reify() 3135 require.NoError(t, err, "failed to reify authorization rules: %+v", opts.RBAC.Rules) 3136 3137 api := vtadmin.NewAPI(testClusters(t), opts) 3138 t.Cleanup(func() { 3139 if err := api.Close(); err != nil { 3140 t.Logf("api did not close cleanly: %s", err.Error()) 3141 } 3142 }) 3143 3144 t.Run("unauthorized actor", func(t *testing.T) { 3145 t.Parallel() 3146 3147 actor := &rbac.Actor{Name: "other"} 3148 ctx := context.Background() 3149 if actor != nil { 3150 ctx = rbac.NewContext(ctx, actor) 3151 } 3152 3153 resp, err := api.ValidateVersionKeyspace(ctx, &vtadminpb.ValidateVersionKeyspaceRequest{ 3154 ClusterId: "test", 3155 Keyspace: "test", 3156 }) 3157 require.NoError(t, err) 3158 assert.Nil(t, resp, "actor %+v should not be permitted to ValidateVersionKeyspace", actor) 3159 }) 3160 3161 t.Run("authorized actor", func(t *testing.T) { 3162 t.Parallel() 3163 3164 actor := &rbac.Actor{Name: "allowed"} 3165 ctx := context.Background() 3166 if actor != nil { 3167 ctx = rbac.NewContext(ctx, actor) 3168 } 3169 3170 resp, err := api.ValidateVersionKeyspace(ctx, &vtadminpb.ValidateVersionKeyspaceRequest{ 3171 ClusterId: "test", 3172 Keyspace: "test", 3173 }) 3174 require.NoError(t, err) 3175 assert.NotNil(t, resp, "actor %+v should be permitted to ValidateVersionKeyspace", actor) 3176 }) 3177 } 3178 3179 func testClusters(t testing.TB) []*cluster.Cluster { 3180 configs := []testutil.TestClusterConfig{ 3181 { 3182 Cluster: &vtadminpb.Cluster{ 3183 Id: "test", 3184 Name: "test", 3185 }, 3186 VtctldClient: &fakevtctldclient.VtctldClient{ 3187 DeleteShardsResults: map[string]error{ 3188 "test/-": nil, 3189 }, 3190 DeleteTabletsResults: map[string]error{ 3191 "zone1-0000000100": nil, 3192 }, 3193 EmergencyReparentShardResults: map[string]struct { 3194 Response *vtctldatapb.EmergencyReparentShardResponse 3195 Error error 3196 }{ 3197 "test/-": { 3198 Response: &vtctldatapb.EmergencyReparentShardResponse{}, 3199 }, 3200 }, 3201 FindAllShardsInKeyspaceResults: map[string]struct { 3202 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 3203 Error error 3204 }{ 3205 "test": { 3206 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 3207 Shards: map[string]*vtctldatapb.Shard{ 3208 "-": { 3209 Keyspace: "test", 3210 Name: "-", 3211 Shard: &topodatapb.Shard{ 3212 KeyRange: &topodatapb.KeyRange{}, 3213 IsPrimaryServing: true, 3214 }, 3215 }, 3216 }, 3217 }, 3218 }, 3219 }, 3220 GetBackupsResults: map[string]struct { 3221 Response *vtctldatapb.GetBackupsResponse 3222 Error error 3223 }{ 3224 "test/-": { 3225 Response: &vtctldatapb.GetBackupsResponse{ 3226 Backups: []*mysqlctlpb.BackupInfo{ 3227 {}, 3228 }, 3229 }, 3230 }, 3231 }, 3232 GetCellInfoNamesResults: &struct { 3233 Response *vtctldatapb.GetCellInfoNamesResponse 3234 Error error 3235 }{ 3236 Response: &vtctldatapb.GetCellInfoNamesResponse{ 3237 Names: []string{"zone1"}, 3238 }, 3239 }, 3240 GetCellsAliasesResults: &struct { 3241 Response *vtctldatapb.GetCellsAliasesResponse 3242 Error error 3243 }{ 3244 Response: &vtctldatapb.GetCellsAliasesResponse{ 3245 Aliases: map[string]*topodatapb.CellsAlias{ 3246 "zone": { 3247 Cells: []string{"zone1"}}, 3248 }, 3249 }, 3250 }, 3251 GetKeyspaceResults: map[string]struct { 3252 Response *vtctldatapb.GetKeyspaceResponse 3253 Error error 3254 }{ 3255 "test": { 3256 Response: &vtctldatapb.GetKeyspaceResponse{ 3257 Keyspace: &vtctldatapb.Keyspace{ 3258 Name: "test", 3259 Keyspace: &topodatapb.Keyspace{}, 3260 }, 3261 }, 3262 }, 3263 }, 3264 GetKeyspacesResults: &struct { 3265 Keyspaces []*vtctldatapb.Keyspace 3266 Error error 3267 }{ 3268 Keyspaces: []*vtctldatapb.Keyspace{ 3269 { 3270 Name: "test", 3271 Keyspace: &topodatapb.Keyspace{}, 3272 }, 3273 }, 3274 }, 3275 GetSchemaResults: map[string]struct { 3276 Response *vtctldatapb.GetSchemaResponse 3277 Error error 3278 }{ 3279 "zone1-0000000100": { 3280 Response: &vtctldatapb.GetSchemaResponse{ 3281 Schema: &tabletmanagerdatapb.SchemaDefinition{ 3282 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 3283 {Name: "t1", Schema: "create table t1 (id int(11) not null primary key);"}, 3284 {Name: "t2"}, 3285 }, 3286 }, 3287 }, 3288 }, 3289 }, 3290 GetSrvVSchemaResults: map[string]struct { 3291 Response *vtctldatapb.GetSrvVSchemaResponse 3292 Error error 3293 }{ 3294 "zone1": { 3295 Response: &vtctldatapb.GetSrvVSchemaResponse{ 3296 SrvVSchema: &vschemapb.SrvVSchema{ 3297 Keyspaces: map[string]*vschemapb.Keyspace{ 3298 "test": { 3299 Sharded: true, 3300 Vindexes: map[string]*vschemapb.Vindex{ 3301 "id": { 3302 Type: "hash", 3303 }, 3304 }, 3305 Tables: map[string]*vschemapb.Table{ 3306 "t1": { 3307 ColumnVindexes: []*vschemapb.ColumnVindex{ 3308 { 3309 Name: "id", 3310 Column: "id", 3311 }, 3312 }, 3313 }, 3314 }, 3315 }, 3316 }, 3317 }, 3318 }, 3319 }, 3320 }, 3321 GetVSchemaResults: map[string]struct { 3322 Response *vtctldatapb.GetVSchemaResponse 3323 Error error 3324 }{ 3325 "test": { 3326 Response: &vtctldatapb.GetVSchemaResponse{ 3327 VSchema: &vschemapb.Keyspace{}, 3328 }, 3329 }, 3330 }, 3331 GetWorkflowsResults: map[string]struct { 3332 Response *vtctldatapb.GetWorkflowsResponse 3333 Error error 3334 }{ 3335 "test": { 3336 Response: &vtctldatapb.GetWorkflowsResponse{ 3337 Workflows: []*vtctldatapb.Workflow{ 3338 { 3339 Name: "testworkflow", 3340 }, 3341 }, 3342 }}, 3343 }, 3344 PingTabletResults: map[string]error{ 3345 "zone1-0000000100": nil, 3346 }, 3347 PlannedReparentShardResults: map[string]struct { 3348 Response *vtctldatapb.PlannedReparentShardResponse 3349 Error error 3350 }{ 3351 "test/-": { 3352 Response: &vtctldatapb.PlannedReparentShardResponse{}, 3353 }, 3354 }, 3355 RefreshStateResults: map[string]error{ 3356 "zone1-0000000100": nil, 3357 }, 3358 ReloadSchemaKeyspaceResults: map[string]struct { 3359 Response *vtctldatapb.ReloadSchemaKeyspaceResponse 3360 Error error 3361 }{ 3362 "test": { 3363 Response: &vtctldatapb.ReloadSchemaKeyspaceResponse{ 3364 Events: []*logutilpb.Event{{}, {}, {}}}, 3365 }, 3366 }, 3367 ReparentTabletResults: map[string]struct { 3368 Response *vtctldatapb.ReparentTabletResponse 3369 Error error 3370 }{ 3371 "zone1-0000000100": { 3372 Response: &vtctldatapb.ReparentTabletResponse{}, 3373 }, 3374 }, 3375 RunHealthCheckResults: map[string]error{ 3376 "zone1-0000000100": nil, 3377 }, 3378 SetWritableResults: map[string]error{ 3379 "zone1-0000000100": nil, 3380 }, 3381 ShardReplicationPositionsResults: map[string]struct { 3382 Response *vtctldatapb.ShardReplicationPositionsResponse 3383 Error error 3384 }{ 3385 "test/-": { 3386 Response: &vtctldatapb.ShardReplicationPositionsResponse{}, 3387 }, 3388 }, 3389 StartReplicationResults: map[string]error{ 3390 "zone1-0000000100": nil, 3391 }, 3392 StopReplicationResults: map[string]error{ 3393 "zone1-0000000100": nil, 3394 }, 3395 TabletExternallyReparentedResults: map[string]struct { 3396 Response *vtctldatapb.TabletExternallyReparentedResponse 3397 Error error 3398 }{ 3399 "zone1-0000000100": { 3400 Response: &vtctldatapb.TabletExternallyReparentedResponse{}, 3401 }, 3402 }, 3403 ValidateKeyspaceResults: map[string]struct { 3404 Response *vtctldatapb.ValidateKeyspaceResponse 3405 Error error 3406 }{ 3407 "test": { 3408 Response: &vtctldatapb.ValidateKeyspaceResponse{}, 3409 }, 3410 }, 3411 ValidateSchemaKeyspaceResults: map[string]struct { 3412 Response *vtctldatapb.ValidateSchemaKeyspaceResponse 3413 Error error 3414 }{ 3415 "test": { 3416 Response: &vtctldatapb.ValidateSchemaKeyspaceResponse{}, 3417 }, 3418 }, 3419 ValidateVersionKeyspaceResults: map[string]struct { 3420 Response *vtctldatapb.ValidateVersionKeyspaceResponse 3421 Error error 3422 }{ 3423 "test": { 3424 Response: &vtctldatapb.ValidateVersionKeyspaceResponse{}, 3425 }, 3426 }, 3427 }, 3428 Tablets: []*vtadminpb.Tablet{ 3429 { 3430 Cluster: &vtadminpb.Cluster{Id: "test", Name: "test"}, 3431 Tablet: &topodatapb.Tablet{ 3432 Alias: &topodatapb.TabletAlias{ 3433 Cell: "zone1", 3434 Uid: 100, 3435 }, 3436 Keyspace: "test", 3437 Type: topodatapb.TabletType_REPLICA, 3438 }, 3439 State: vtadminpb.Tablet_SERVING, 3440 }, 3441 }, 3442 Config: &cluster.Config{ 3443 TopoReadPoolConfig: &cluster.RPCPoolConfig{ 3444 Size: 100, 3445 WaitTimeout: time.Millisecond * 50, 3446 }, 3447 }, 3448 }, { 3449 Cluster: &vtadminpb.Cluster{ 3450 Id: "other", 3451 Name: "other", 3452 }, 3453 VtctldClient: &fakevtctldclient.VtctldClient{ 3454 FindAllShardsInKeyspaceResults: map[string]struct { 3455 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 3456 Error error 3457 }{ 3458 "otherks": { 3459 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 3460 Shards: map[string]*vtctldatapb.Shard{ 3461 "-": { 3462 Keyspace: "otherks", 3463 Name: "-", 3464 Shard: &topodatapb.Shard{}, 3465 }, 3466 }, 3467 }, 3468 }, 3469 }, 3470 GetBackupsResults: map[string]struct { 3471 Response *vtctldatapb.GetBackupsResponse 3472 Error error 3473 }{ 3474 "otherks/-": { 3475 Response: &vtctldatapb.GetBackupsResponse{ 3476 Backups: []*mysqlctlpb.BackupInfo{ 3477 {}, {}, {}, 3478 }, 3479 }, 3480 }, 3481 }, 3482 GetCellInfoNamesResults: &struct { 3483 Response *vtctldatapb.GetCellInfoNamesResponse 3484 Error error 3485 }{ 3486 Response: &vtctldatapb.GetCellInfoNamesResponse{ 3487 Names: []string{"other1"}, 3488 }, 3489 }, 3490 GetCellsAliasesResults: &struct { 3491 Response *vtctldatapb.GetCellsAliasesResponse 3492 Error error 3493 }{ 3494 Response: &vtctldatapb.GetCellsAliasesResponse{ 3495 Aliases: map[string]*topodatapb.CellsAlias{ 3496 "other": { 3497 Cells: []string{"other1"}}, 3498 }, 3499 }, 3500 }, 3501 GetKeyspacesResults: &struct { 3502 Keyspaces []*vtctldatapb.Keyspace 3503 Error error 3504 }{ 3505 Keyspaces: []*vtctldatapb.Keyspace{ 3506 { 3507 Name: "otherks", 3508 Keyspace: &topodatapb.Keyspace{}, 3509 }, 3510 }, 3511 }, 3512 GetSchemaResults: map[string]struct { 3513 Response *vtctldatapb.GetSchemaResponse 3514 Error error 3515 }{ 3516 "other1-0000000100": { 3517 Response: &vtctldatapb.GetSchemaResponse{ 3518 Schema: &tabletmanagerdatapb.SchemaDefinition{ 3519 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 3520 {Name: "t1"}, 3521 }, 3522 }, 3523 }, 3524 }, 3525 }, 3526 GetSrvVSchemaResults: map[string]struct { 3527 Response *vtctldatapb.GetSrvVSchemaResponse 3528 Error error 3529 }{ 3530 "other1": { 3531 Response: &vtctldatapb.GetSrvVSchemaResponse{ 3532 SrvVSchema: &vschemapb.SrvVSchema{}, 3533 }, 3534 }, 3535 }, 3536 GetVSchemaResults: map[string]struct { 3537 Response *vtctldatapb.GetVSchemaResponse 3538 Error error 3539 }{ 3540 "otherks": { 3541 Response: &vtctldatapb.GetVSchemaResponse{ 3542 VSchema: &vschemapb.Keyspace{}, 3543 }, 3544 }, 3545 }, 3546 GetWorkflowsResults: map[string]struct { 3547 Response *vtctldatapb.GetWorkflowsResponse 3548 Error error 3549 }{ 3550 "otherks": { 3551 Response: &vtctldatapb.GetWorkflowsResponse{ 3552 Workflows: []*vtctldatapb.Workflow{ 3553 { 3554 Name: "otherks_workflow", 3555 }, 3556 }, 3557 }}, 3558 }, 3559 ShardReplicationPositionsResults: map[string]struct { 3560 Response *vtctldatapb.ShardReplicationPositionsResponse 3561 Error error 3562 }{ 3563 "otherks/-": { 3564 Response: &vtctldatapb.ShardReplicationPositionsResponse{}, 3565 }, 3566 }, 3567 ReloadSchemaKeyspaceResults: map[string]struct { 3568 Response *vtctldatapb.ReloadSchemaKeyspaceResponse 3569 Error error 3570 }{ 3571 "otherks": { 3572 Response: &vtctldatapb.ReloadSchemaKeyspaceResponse{ 3573 Events: []*logutilpb.Event{{}}}, 3574 }, 3575 }, 3576 }, 3577 Tablets: []*vtadminpb.Tablet{ 3578 { 3579 Cluster: &vtadminpb.Cluster{Id: "other", Name: "other"}, 3580 Tablet: &topodatapb.Tablet{ 3581 Alias: &topodatapb.TabletAlias{ 3582 Cell: "other1", 3583 Uid: 100, 3584 }, 3585 Keyspace: "otherks", 3586 Type: topodatapb.TabletType_UNKNOWN, 3587 }, 3588 State: vtadminpb.Tablet_SERVING, 3589 }, 3590 }, 3591 Config: &cluster.Config{ 3592 TopoReadPoolConfig: &cluster.RPCPoolConfig{ 3593 Size: 100, 3594 WaitTimeout: time.Millisecond * 50, 3595 }, 3596 }, 3597 }, 3598 } 3599 3600 return testutil.BuildClusters(t, configs...) 3601 }