func createSourceTablet(t *testing.T, ts topo.Server, keyspace, shard string) { vshard, kr, err := topo.ValidateShardName(shard) if err != nil { t.Fatalf("ValidateShardName(%v) failed: %v", shard, err) } ctx := context.Background() tablet := &pb.Tablet{ Alias: &pb.TabletAlias{ Cell: "cell1", Uid: 100, }, Type: pb.TabletType_REPLICA, KeyRange: kr, Keyspace: keyspace, Shard: vshard, PortMap: map[string]int32{ "vt": 80, }, } if err := ts.CreateTablet(ctx, tablet); err != nil { t.Fatalf("CreateTablet failed: %v", err) } if err := topotools.UpdateTabletEndpoints(ctx, ts, tablet); err != nil { t.Fatalf("topotools.UpdateTabletEndpoints failed: %v", err) } }
// CopyTablets will create the tablets in the destination topo func CopyTablets(fromTS, toTS topo.Server) { cells, err := fromTS.GetKnownCells() if err != nil { log.Fatalf("fromTS.GetKnownCells failed: %v", err) } wg := sync.WaitGroup{} rec := concurrency.AllErrorRecorder{} for _, cell := range cells { wg.Add(1) go func(cell string) { defer wg.Done() tabletAliases, err := fromTS.GetTabletsByCell(cell) if err != nil { rec.RecordError(err) } else { for _, tabletAlias := range tabletAliases { wg.Add(1) go func(tabletAlias topo.TabletAlias) { defer wg.Done() // read the source tablet ti, err := fromTS.GetTablet(tabletAlias) if err != nil { rec.RecordError(err) return } // try to create the destination err = toTS.CreateTablet(ti.Tablet) if err == topo.ErrNodeExists { // update the destination tablet log.Warningf("tablet %v already exists, updating it", tabletAlias) err = toTS.UpdateTabletFields(ti.Alias(), func(t *topo.Tablet) error { *t = *ti.Tablet return nil }) } if err != nil { rec.RecordError(err) return } // create the replication paths // for masters only here if ti.Type == topo.TYPE_MASTER { if err = toTS.CreateReplicationPath(ti.Keyspace, ti.Shard, ti.Alias().String()); err != nil && err != topo.ErrNodeExists { rec.RecordError(err) } } }(tabletAlias) } } }(cell) } wg.Wait() if rec.HasErrors() { log.Fatalf("copyTablets failed: %v", rec.Error()) } }
func CheckPid(t *testing.T, ts topo.Server) { cell := getLocalCell(t, ts) tablet := &topo.Tablet{ Cell: cell, Uid: 1, Parent: topo.TabletAlias{}, Addr: "localhost:3333", Keyspace: "test_keyspace", Type: topo.TYPE_MASTER, State: topo.STATE_READ_WRITE, KeyRange: newKeyRange("-10"), } if err := ts.CreateTablet(tablet); err != nil { t.Fatalf("CreateTablet: %v", err) } tabletAlias := topo.TabletAlias{Cell: cell, Uid: 1} done := make(chan struct{}, 1) if err := ts.CreateTabletPidNode(tabletAlias, "contents", done); err != nil { t.Errorf("ts.CreateTabletPidNode: %v", err) } if err := ts.ValidateTabletPidNode(tabletAlias); err != nil { t.Errorf("ts.ValidateTabletPidNode: %v", err) } close(done) }
// createSourceTablet is a helper method to create the source tablet // in the given keyspace/shard. func createSourceTablet(t *testing.T, name string, ts topo.Server, keyspace, shard string) { vshard, kr, err := topo.ValidateShardName(shard) if err != nil { t.Fatalf("ValidateShardName(%v) failed: %v", shard, err) } ctx := context.Background() tablet := &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{ Cell: "cell1", Uid: 100, }, Keyspace: keyspace, Shard: vshard, Type: topodatapb.TabletType_REPLICA, KeyRange: kr, PortMap: map[string]int32{ "vt": 80, }, } if err := ts.CreateTablet(ctx, tablet); err != nil { t.Fatalf("CreateTablet failed: %v", err) } // register a tablet conn dialer that will return the instance // we want tabletconn.RegisterDialer(name, func(tablet *topodatapb.Tablet, timeout time.Duration) (tabletconn.TabletConn, error) { return &fakeTabletConn{ tablet: tablet, }, nil }) flag.Set("tablet_protocol", name) }
// CopyTablets will create the tablets in the destination topo func CopyTablets(fromTS, toTS topo.Server) { cells, err := fromTS.GetKnownCells() if err != nil { log.Fatalf("fromTS.GetKnownCells: %v", err) } wg := sync.WaitGroup{} rec := concurrency.AllErrorRecorder{} for _, cell := range cells { wg.Add(1) go func(cell string) { defer wg.Done() tabletAliases, err := fromTS.GetTabletsByCell(cell) if err != nil { rec.RecordError(fmt.Errorf("GetTabletsByCell(%v): %v", cell, err)) } else { for _, tabletAlias := range tabletAliases { wg.Add(1) go func(tabletAlias topo.TabletAlias) { defer wg.Done() // read the source tablet ti, err := fromTS.GetTablet(tabletAlias) if err != nil { rec.RecordError(fmt.Errorf("GetTablet(%v): %v", tabletAlias, err)) return } // try to create the destination err = toTS.CreateTablet(ti.Tablet) if err == topo.ErrNodeExists { // update the destination tablet log.Warningf("tablet %v already exists, updating it", tabletAlias) err = toTS.UpdateTabletFields(ti.Alias, func(t *topo.Tablet) error { *t = *ti.Tablet return nil }) } if err != nil { rec.RecordError(fmt.Errorf("CreateTablet(%v): %v", tabletAlias, err)) return } }(tabletAlias) } } }(cell) } wg.Wait() if rec.HasErrors() { log.Fatalf("copyTablets failed: %v", rec.Error()) } }
func CheckPid(t *testing.T, ts topo.Server) { cell := getLocalCell(t, ts) tablet := &topo.Tablet{ Alias: topo.TabletAlias{Cell: cell, Uid: 1}, Hostname: "localhost", Portmap: map[string]int{ "vt": 3333, }, Parent: topo.TabletAlias{}, Keyspace: "test_keyspace", Type: topo.TYPE_MASTER, State: topo.STATE_READ_WRITE, KeyRange: newKeyRange("-10"), } if err := ts.CreateTablet(tablet); err != nil { t.Fatalf("CreateTablet: %v", err) } tabletAlias := topo.TabletAlias{Cell: cell, Uid: 1} done := make(chan struct{}, 1) if err := ts.CreateTabletPidNode(tabletAlias, "contents", done); err != nil { t.Errorf("ts.CreateTabletPidNode: %v", err) } // wait for up to 30 seconds for the pid to appear timeout := 30 for { err := ts.ValidateTabletPidNode(tabletAlias) if err == nil { // exists, we're good break } timeout -= 1 if timeout == 0 { t.Fatalf("ts.ValidateTabletPidNode: %v", err) } t.Logf("Waiting for ValidateTabletPidNode to succeed %v/30", timeout) time.Sleep(time.Second) } close(done) }
// createSourceTablet is a helper method to create the source tablet // in the given keyspace/shard. func createSourceTablet(t *testing.T, name string, ts topo.Server, keyspace, shard string) { vshard, kr, err := topo.ValidateShardName(shard) if err != nil { t.Fatalf("ValidateShardName(%v) failed: %v", shard, err) } ctx := context.Background() tablet := &pbt.Tablet{ Alias: &pbt.TabletAlias{ Cell: "cell1", Uid: 100, }, Type: pbt.TabletType_REPLICA, KeyRange: kr, Keyspace: keyspace, Shard: vshard, PortMap: map[string]int32{ "vt": 80, }, } if err := ts.CreateTablet(ctx, tablet); err != nil { t.Fatalf("CreateTablet failed: %v", err) } if err := topotools.UpdateTabletEndpoints(ctx, ts, tablet); err != nil { t.Fatalf("topotools.UpdateTabletEndpoints failed: %v", err) } // register a tablet conn dialer that will return the instance // we want tabletconn.RegisterDialer(name, func(ctx context.Context, endPoint *pbt.EndPoint, k, s string, tabletType pbt.TabletType, timeout time.Duration) (tabletconn.TabletConn, error) { return &fakeTabletConn{ endPoint: endPoint, keyspace: keyspace, shard: vshard, tabletType: pbt.TabletType_REPLICA, }, nil }) flag.Lookup("tablet_protocol").Value.Set(name) }
func addTablet(ctx context.Context, t *testing.T, ts topo.Server, uid int, cell string, tabletType topodatapb.TabletType) *topo.TabletInfo { tablet := &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{Cell: cell, Uid: uint32(uid)}, Hostname: fmt.Sprintf("%vbsr%v", cell, uid), Ip: fmt.Sprintf("212.244.218.%v", uid), PortMap: map[string]int32{ "vt": 3333 + 10*int32(uid), "mysql": 3334 + 10*int32(uid), }, Keyspace: testKeyspace, Type: tabletType, Shard: testShard, } if err := ts.CreateTablet(ctx, tablet); err != nil { t.Fatalf("CreateTablet: %v", err) } ti, err := ts.GetTablet(ctx, tablet.Alias) if err != nil { t.Fatalf("GetTablet: %v", err) } return ti }
func CheckActions(t *testing.T, ts topo.Server) { cell := getLocalCell(t, ts) tablet := &topo.Tablet{ Alias: topo.TabletAlias{Cell: cell, Uid: 1}, Hostname: "localhost", IPAddr: "10.11.12.13", Portmap: map[string]int{ "vt": 3333, "mysql": 3334, }, Parent: topo.TabletAlias{}, Keyspace: "test_keyspace", Type: topo.TYPE_MASTER, State: topo.STATE_READ_WRITE, KeyRange: newKeyRange("-10"), } if err := ts.CreateTablet(tablet); err != nil { t.Fatalf("CreateTablet: %v", err) } tabletAlias := topo.TabletAlias{Cell: cell, Uid: 1} actionPath, err := ts.WriteTabletAction(tabletAlias, "contents1") if err != nil { t.Fatalf("WriteTabletAction: %v", err) } interrupted := make(chan struct{}, 1) if _, err := ts.WaitForTabletAction(actionPath, time.Second/100, interrupted); err != topo.ErrTimeout { t.Errorf("WaitForTabletAction returned %v", err) } go func() { time.Sleep(time.Second / 10) close(interrupted) }() if _, err := ts.WaitForTabletAction(actionPath, time.Second*5, interrupted); err != topo.ErrInterrupted { t.Errorf("WaitForTabletAction returned %v", err) } // wait for the result in one thread wg1 := sync.WaitGroup{} wg1.Add(1) go func() { defer wg1.Done() interrupted := make(chan struct{}, 1) var err error for i := 0; i <= 30; i++ { var result string result, err = ts.WaitForTabletAction(actionPath, time.Second, interrupted) if err == topo.ErrTimeout { // we waited for one second, didn't see the // result, try again up to 30 seconds. t.Logf("WaitForTabletAction timed out at try %v/30", i) continue } if err != nil { t.Errorf("WaitForTabletAction returned: %v", err) } if result != "contents3" { t.Errorf("WaitForTabletAction returned bad result: %v", result) } return } t.Errorf("WaitForTabletAction timed out: %v", err) }() // process the action in another thread done := make(chan struct{}, 1) wg2 := sync.WaitGroup{} wg2.Add(1) go ts.ActionEventLoop(tabletAlias, func(ap, data string) error { // the actionPath sent back to the action processor // is the exact one we have in normal cases, // but for the tee, we add extra information. if ap != actionPath && !strings.HasSuffix(ap, actionPath) { t.Errorf("Bad action path: %v", ap) } if data != "contents1" { t.Errorf("Bad data: %v", data) } ta, contents, version, err := ts.ReadTabletActionPath(ap) if err != nil { t.Errorf("Error from ReadTabletActionPath: %v", err) } if contents != data { t.Errorf("Bad contents: %v", contents) } if ta != tabletAlias { t.Errorf("Bad tablet alias: %v", ta) } if err := ts.UpdateTabletAction(ap, "contents2", version); err != nil { t.Errorf("UpdateTabletAction failed: %v", err) } if err := ts.StoreTabletActionResponse(ap, "contents3"); err != nil { t.Errorf("StoreTabletActionResponse failed: %v", err) } if err := ts.UnblockTabletAction(ap); err != nil { t.Errorf("UnblockTabletAction failed: %v", err) } wg2.Done() return nil }, done) // first wait for the processing to be done, then close the // action loop, then wait for the response to be received. wg2.Wait() close(done) wg1.Wait() // start an action, and try to purge all actions, to test // PurgeTabletActions actionPath, err = ts.WriteTabletAction(tabletAlias, "contents2") if err != nil { t.Fatalf("WriteTabletAction(contents2): %v", err) } if err := ts.PurgeTabletActions(tabletAlias, func(data string) bool { return true }); err != nil { t.Fatalf("PurgeTabletActions(contents2) failed: %v", err) } if _, _, _, err := ts.ReadTabletActionPath(actionPath); err != topo.ErrNoNode { t.Fatalf("ReadTabletActionPath(contents2) should have failed with ErrNoNode: %v", err) } }
// TestSuite runs the test suite on the given topo server and client func TestSuite(t *testing.T, ts topo.Server, client vtctlclient.VtctlClient) { ctx := context.Background() // Create a fake tablet tablet := &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{Cell: "cell1", Uid: 1}, Hostname: "localhost", Ip: "10.11.12.13", PortMap: map[string]int32{ "vt": 3333, "mysql": 3334, }, Tags: map[string]string{"tag": "value"}, Keyspace: "test_keyspace", Type: topodatapb.TabletType_MASTER, } if err := ts.CreateTablet(ctx, tablet); err != nil { t.Errorf("CreateTablet: %v", err) } // run a command that's gonna return something on the log channel stream, err := client.ExecuteVtctlCommand(ctx, []string{"ListAllTablets", "cell1"}, 30*time.Second) if err != nil { t.Fatalf("Remote error: %v", err) } got, err := stream.Recv() if err != nil { t.Fatalf("failed to get first line: %v", err) } expected := "cell1-0000000001 test_keyspace <null> master localhost:3333 localhost:3334 [tag: \"value\"]\n" if logutil.EventString(got) != expected { t.Errorf("Got unexpected log line '%v' expected '%v'", got.String(), expected) } got, err = stream.Recv() if err != io.EOF { t.Errorf("Didn't get end of log stream: %v %v", got, err) } // run a command that's gonna fail stream, err = client.ExecuteVtctlCommand(ctx, []string{"ListAllTablets", "cell2"}, 30*time.Second) if err != nil { t.Fatalf("Remote error: %v", err) } got, err = stream.Recv() expected = "node doesn't exist" if err == nil || !strings.Contains(err.Error(), expected) { t.Fatalf("Unexpected remote error, got: '%v' was expecting to find '%v'", err, expected) } // run a command that's gonna panic stream, err = client.ExecuteVtctlCommand(ctx, []string{"Panic"}, 30*time.Second) if err != nil { t.Fatalf("Remote error: %v", err) } got, err = stream.Recv() expected1 := "this command panics on purpose" expected2 := "uncaught vtctl panic" if err == nil || !strings.Contains(err.Error(), expected1) || !strings.Contains(err.Error(), expected2) { t.Fatalf("Unexpected remote error, got: '%v' was expecting to find '%v' and '%v'", err, expected1, expected2) } // and clean up the tablet if err := ts.DeleteTablet(ctx, tablet.Alias); err != nil { t.Errorf("DeleteTablet: %v", err) } }
// TestSuite runs the test suite on the given topo server and client func TestSuite(t *testing.T, ts topo.Server, client vtctlclient.VtctlClient) { ctx := context.Background() // Create a fake tablet tablet := &topo.Tablet{ Alias: topo.TabletAlias{Cell: "cell1", Uid: 1}, Hostname: "localhost", IPAddr: "10.11.12.13", Portmap: map[string]int{ "vt": 3333, "mysql": 3334, }, Tags: map[string]string{"tag": "value"}, Keyspace: "test_keyspace", Type: topo.TYPE_MASTER, } if err := ts.CreateTablet(ctx, tablet); err != nil { t.Errorf("CreateTablet: %v", err) } // run a command that's gonna return something on the log channel logs, errFunc, err := client.ExecuteVtctlCommand(ctx, []string{"ListAllTablets", "cell1"}, 30*time.Second, 10*time.Second) if err != nil { t.Fatalf("Remote error: %v", err) } count := 0 for e := range logs { expected := "cell1-0000000001 test_keyspace <null> master localhost:3333 localhost:3334 [tag: \"value\"]\n" if e.String() != expected { t.Errorf("Got unexpected log line '%v' expected '%v'", e.String(), expected) } count++ } if count != 1 { t.Errorf("Didn't get expected log line only, got %v lines", count) } if err := errFunc(); err != nil { t.Fatalf("Remote error: %v", err) } // run a command that's gonna fail logs, errFunc, err = client.ExecuteVtctlCommand(ctx, []string{"ListAllTablets", "cell2"}, 30*time.Second, 10*time.Second) if err != nil { t.Fatalf("Remote error: %v", err) } if e, ok := <-logs; ok { t.Errorf("Got unexpected line for logs: %v", e.String()) } expected := "node doesn't exist" if err := errFunc(); err == nil || !strings.Contains(err.Error(), expected) { t.Fatalf("Unexpected remote error, got: '%v' was expecting to find '%v'", err, expected) } // run a command that's gonna panic logs, errFunc, err = client.ExecuteVtctlCommand(ctx, []string{"Panic"}, 30*time.Second, 10*time.Second) if err != nil { t.Fatalf("Remote error: %v", err) } if e, ok := <-logs; ok { t.Errorf("Got unexpected line for logs: %v", e.String()) } expected1 := "this command panics on purpose" expected2 := "uncaught vtctl panic" if err := errFunc(); err == nil || !strings.Contains(err.Error(), expected1) || !strings.Contains(err.Error(), expected2) { t.Fatalf("Unexpected remote error, got: '%v' was expecting to find '%v' and '%v'", err, expected1, expected2) } }
func CheckLotsOfActions(t *testing.T, ts topo.Server) { cell := getLocalCell(t, ts) tablet := &topo.Tablet{ Alias: topo.TabletAlias{Cell: cell, Uid: 1}, Hostname: "localhost", IPAddr: "10.11.12.13", Portmap: map[string]int{ "vt": 3333, "mysql": 3334, }, Parent: topo.TabletAlias{}, Keyspace: "test_keyspace", Type: topo.TYPE_MASTER, State: topo.STATE_READ_WRITE, KeyRange: newKeyRange("-10"), } if err := ts.CreateTablet(tablet); err != nil { t.Fatalf("CreateTablet: %v", err) } nActions := 64 paths := make([]string, nActions) // write the actions in parallel, save the paths writeWG := sync.WaitGroup{} for i := 0; i < nActions; i++ { writeWG.Add(1) go func(i int) { var err error paths[i], err = ts.WriteTabletAction(tablet.Alias, fmt.Sprintf("contents %v", i)) if err != nil { t.Fatalf("WriteTabletAction(%v): %v", i, err) } t.Logf("WriteTabletAction(%v) done", i) writeWG.Done() }(i) } writeWG.Wait() // wait for all actions in parallel waitWG := sync.WaitGroup{} interrupted := make(chan struct{}, 1) for i := 0; i < nActions; i++ { waitWG.Add(1) go func(i int) { if _, err := ts.WaitForTabletAction(paths[i], time.Second*30, interrupted); err != nil { t.Fatalf("WaitForTabletAction returned %v", err) } t.Logf("WaitForTabletAction(%v) done", i) waitWG.Done() }(i) } // and unblock them all in parallel unblockWG := sync.WaitGroup{} for i := 0; i < nActions; i++ { unblockWG.Add(1) go func(i int) { defer unblockWG.Done() ta, contents, _, err := ts.ReadTabletActionPath(paths[i]) if err != nil { t.Errorf("Error from ReadTabletActionPath: %v", err) } t.Logf("ReadTabletActionPath(%v) done", i) if contents != fmt.Sprintf("contents %v", i) { t.Errorf("Bad contents: %v", contents) } if ta != tablet.Alias { t.Errorf("Bad tablet alias: %v", ta) } if err := ts.StoreTabletActionResponse(paths[i], fmt.Sprintf("contents after %v", i)); err != nil { t.Errorf("StoreTabletActionResponse failed: %v", err) } t.Logf("StoreTabletActionResponse(%v) done", i) if err := ts.UnblockTabletAction(paths[i]); err != nil { t.Errorf("UnblockTabletAction failed: %v", err) } t.Logf("UnblockTabletAction(%v) done", i) }(i) } unblockWG.Wait() waitWG.Wait() }
func CheckTablet(t *testing.T, ts topo.Server) { cell := getLocalCell(t, ts) tablet := &topo.Tablet{ Cell: cell, Uid: 1, Addr: "localhost:3333", MysqlAddr: "localhost:3334", MysqlIpAddr: "10.11.12.13:3334", Alias: topo.TabletAlias{Cell: cell, Uid: 1}, Hostname: "localhost", IPAddr: "10.11.12.13", Portmap: map[string]int{ "vt": 3333, "mysql": 3334, }, Tags: map[string]string{"tag": "value"}, Keyspace: "test_keyspace", Type: topo.TYPE_MASTER, State: topo.STATE_READ_WRITE, KeyRange: newKeyRange("-10"), BlacklistedTables: []string{"black1", "black2"}, } if err := ts.CreateTablet(tablet); err != nil { t.Errorf("CreateTablet: %v", err) } if err := ts.CreateTablet(tablet); err != topo.ErrNodeExists { t.Errorf("CreateTablet(again): %v", err) } if _, err := ts.GetTablet(topo.TabletAlias{Cell: cell, Uid: 666}); err != topo.ErrNoNode { t.Errorf("GetTablet(666): %v", err) } ti, err := ts.GetTablet(tablet.Alias) if err != nil { t.Errorf("GetTablet %v: %v", tablet.Alias, err) } if eq, err := tabletEqual(ti.Tablet, tablet); err != nil { t.Errorf("cannot compare tablets: %v", err) } else if !eq { t.Errorf("put and got tablets are not identical:\n%#v\n%#v", tablet, ti.Tablet) } if _, err := ts.GetTabletsByCell("666"); err != topo.ErrNoNode { t.Errorf("GetTabletsByCell(666): %v", err) } inCell, err := ts.GetTabletsByCell(cell) if err != nil { t.Errorf("GetTabletsByCell: %v", err) } if len(inCell) != 1 || inCell[0] != tablet.Alias { t.Errorf("GetTabletsByCell: want [%v], got %v", tablet.Alias, inCell) } ti.State = topo.STATE_READ_ONLY if err := topo.UpdateTablet(ts, ti); err != nil { t.Errorf("UpdateTablet: %v", err) } ti, err = ts.GetTablet(tablet.Alias) if err != nil { t.Errorf("GetTablet %v: %v", tablet.Alias, err) } if want := topo.STATE_READ_ONLY; ti.State != want { t.Errorf("ti.State: want %v, got %v", want, ti.State) } if err := ts.UpdateTabletFields(tablet.Alias, func(t *topo.Tablet) error { t.State = topo.STATE_READ_WRITE return nil }); err != nil { t.Errorf("UpdateTabletFields: %v", err) } ti, err = ts.GetTablet(tablet.Alias) if err != nil { t.Errorf("GetTablet %v: %v", tablet.Alias, err) } if want := topo.STATE_READ_WRITE; ti.State != want { t.Errorf("ti.State: want %v, got %v", want, ti.State) } if err := ts.DeleteTablet(tablet.Alias); err != nil { t.Errorf("DeleteTablet: %v", err) } if err := ts.DeleteTablet(tablet.Alias); err != topo.ErrNoNode { t.Errorf("DeleteTablet(again): %v", err) } if _, err := ts.GetTablet(tablet.Alias); err != topo.ErrNoNode { t.Errorf("GetTablet: expected error, tablet was deleted: %v", err) } }
func createSetup(ctx context.Context, t *testing.T) (topo.Impl, topo.Impl) { fromConn := fakezk.NewConn() fromTS := zktopo.NewServer(fromConn) toConn := fakezk.NewConn() toTS := zktopo.NewServer(toConn) for _, zkPath := range []string{"/zk/test_cell/vt", "/zk/global/vt"} { if _, err := zk.CreateRecursive(fromConn, zkPath, "", 0, zookeeper.WorldACL(zookeeper.PERM_ALL)); err != nil { t.Fatalf("cannot init fromTS: %v", err) } } // create a keyspace and a couple tablets if err := fromTS.CreateKeyspace(ctx, "test_keyspace", &topodatapb.Keyspace{}); err != nil { t.Fatalf("cannot create keyspace: %v", err) } if err := fromTS.CreateShard(ctx, "test_keyspace", "0", &topodatapb.Shard{Cells: []string{"test_cell"}}); err != nil { t.Fatalf("cannot create shard: %v", err) } tts := topo.Server{Impl: fromTS} if err := tts.CreateTablet(ctx, &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{ Cell: "test_cell", Uid: 123, }, Hostname: "masterhost", Ip: "1.2.3.4", PortMap: map[string]int32{ "vt": 8101, "gprc": 8102, "mysql": 3306, }, Keyspace: "test_keyspace", Shard: "0", Type: topodatapb.TabletType_MASTER, DbNameOverride: "", KeyRange: nil, }); err != nil { t.Fatalf("cannot create master tablet: %v", err) } if err := tts.CreateTablet(ctx, &topodatapb.Tablet{ Alias: &topodatapb.TabletAlias{ Cell: "test_cell", Uid: 234, }, Ip: "2.3.4.5", PortMap: map[string]int32{ "vt": 8101, "grpc": 8102, "mysql": 3306, }, Hostname: "slavehost", Keyspace: "test_keyspace", Shard: "0", Type: topodatapb.TabletType_REPLICA, DbNameOverride: "", KeyRange: nil, }); err != nil { t.Fatalf("cannot create slave tablet: %v", err) } os.Setenv("ZK_CLIENT_CONFIG", testfiles.Locate("topo_helpers_test_zk_client.json")) cells, err := fromTS.GetKnownCells(ctx) if err != nil { t.Fatalf("fromTS.GetKnownCells: %v", err) } log.Infof("Cells: %v", cells) return fromTS, toTS }
func CheckActions(t *testing.T, ts topo.Server) { cell := getLocalCell(t, ts) tablet := &topo.Tablet{ Cell: cell, Uid: 1, Parent: topo.TabletAlias{}, Addr: "localhost:3333", Keyspace: "test_keyspace", Type: topo.TYPE_MASTER, State: topo.STATE_READ_WRITE, KeyRange: newKeyRange("-10"), } if err := ts.CreateTablet(tablet); err != nil { t.Fatalf("CreateTablet: %v", err) } tabletAlias := topo.TabletAlias{cell, 1} actionPath, err := ts.WriteTabletAction(tabletAlias, "contents1") if err != nil { t.Fatalf("WriteTabletAction: %v", err) } interrupted := make(chan struct{}, 1) if _, err := ts.WaitForTabletAction(actionPath, time.Second/100, interrupted); err != topo.ErrTimeout { t.Errorf("WaitForTabletAction returned %v", err) } go func() { time.Sleep(time.Second / 10) close(interrupted) }() if _, err := ts.WaitForTabletAction(actionPath, time.Second*5, interrupted); err != topo.ErrInterrupted { t.Errorf("WaitForTabletAction returned %v", err) } wg := sync.WaitGroup{} // wait for the result in one thread wg.Add(1) go func() { interrupted := make(chan struct{}, 1) result, err := ts.WaitForTabletAction(actionPath, time.Second*10, interrupted) if err != nil { t.Errorf("WaitForTabletAction returned %v", err) } if result != "contents3" { t.Errorf("WaitForTabletAction returned bad result: %v", result) } wg.Done() }() // process the action in another thread done := make(chan struct{}, 1) wg.Add(1) go ts.ActionEventLoop(tabletAlias, func(ap, data string) error { // the actionPath sent back to the action processor // is the exact one we have in normal cases, // but for the tee, we add extra information. if ap != actionPath && !strings.HasSuffix(ap, actionPath) { t.Errorf("Bad action path: %v", ap) } if data != "contents1" { t.Errorf("Bad data: %v", data) } ta, contents, version, err := ts.ReadTabletActionPath(ap) if err != nil { t.Errorf("Error from ReadTabletActionPath: %v", err) } if contents != data { t.Errorf("Bad contents: %v", contents) } if ta != tabletAlias { t.Errorf("Bad tablet alias: %v", ta) } if err := ts.UpdateTabletAction(ap, "contents2", version); err != nil { t.Errorf("UpdateTabletAction failed: %v", err) } if err := ts.StoreTabletActionResponse(ap, "contents3"); err != nil { t.Errorf("StoreTabletActionResponse failed: %v", err) } if err := ts.UnblockTabletAction(ap); err != nil { t.Errorf("UnblockTabletAction failed: %v", err) } wg.Done() return nil }, done) close(done) wg.Wait() }
// CheckTablet verifies the topo server API is correct for managing tablets. func CheckTablet(ctx context.Context, t *testing.T, ts topo.Server) { cell := getLocalCell(ctx, t, ts) tablet := &topo.Tablet{ Alias: topo.TabletAlias{Cell: cell, Uid: 1}, Hostname: "localhost", IPAddr: "10.11.12.13", Portmap: map[string]int{ "vt": 3333, "mysql": 3334, }, Tags: map[string]string{"tag": "value"}, Keyspace: "test_keyspace", Type: topo.TYPE_MASTER, KeyRange: newKeyRange("-10"), } if err := ts.CreateTablet(ctx, tablet); err != nil { t.Errorf("CreateTablet: %v", err) } if err := ts.CreateTablet(ctx, tablet); err != topo.ErrNodeExists { t.Errorf("CreateTablet(again): %v", err) } if _, err := ts.GetTablet(ctx, topo.TabletAlias{Cell: cell, Uid: 666}); err != topo.ErrNoNode { t.Errorf("GetTablet(666): %v", err) } ti, err := ts.GetTablet(ctx, tablet.Alias) if err != nil { t.Errorf("GetTablet %v: %v", tablet.Alias, err) } if eq, err := tabletEqual(ti.Tablet, tablet); err != nil { t.Errorf("cannot compare tablets: %v", err) } else if !eq { t.Errorf("put and got tablets are not identical:\n%#v\n%#v", tablet, ti.Tablet) } if _, err := ts.GetTabletsByCell(ctx, "666"); err != topo.ErrNoNode { t.Errorf("GetTabletsByCell(666): %v", err) } inCell, err := ts.GetTabletsByCell(ctx, cell) if err != nil { t.Errorf("GetTabletsByCell: %v", err) } if len(inCell) != 1 || inCell[0] != tablet.Alias { t.Errorf("GetTabletsByCell: want [%v], got %v", tablet.Alias, inCell) } ti.Hostname = "remotehost" if err := topo.UpdateTablet(ctx, ts, ti); err != nil { t.Errorf("UpdateTablet: %v", err) } ti, err = ts.GetTablet(ctx, tablet.Alias) if err != nil { t.Errorf("GetTablet %v: %v", tablet.Alias, err) } if want := "remotehost"; ti.Hostname != want { t.Errorf("ti.Hostname: want %v, got %v", want, ti.Hostname) } if err := topo.UpdateTabletFields(ctx, ts, tablet.Alias, func(t *topo.Tablet) error { t.Hostname = "anotherhost" return nil }); err != nil { t.Errorf("UpdateTabletFields: %v", err) } ti, err = ts.GetTablet(ctx, tablet.Alias) if err != nil { t.Errorf("GetTablet %v: %v", tablet.Alias, err) } if want := "anotherhost"; ti.Hostname != want { t.Errorf("ti.Hostname: want %v, got %v", want, ti.Hostname) } if err := ts.DeleteTablet(ctx, tablet.Alias); err != nil { t.Errorf("DeleteTablet: %v", err) } if err := ts.DeleteTablet(ctx, tablet.Alias); err != topo.ErrNoNode { t.Errorf("DeleteTablet(again): %v", err) } if _, err := ts.GetTablet(ctx, tablet.Alias); err != topo.ErrNoNode { t.Errorf("GetTablet: expected error, tablet was deleted: %v", err) } }
func CheckTablet(t *testing.T, ts topo.Server) { cell := getLocalCell(t, ts) tablet := &topo.Tablet{ Cell: cell, Uid: 1, Parent: topo.TabletAlias{}, Addr: "localhost:3333", MysqlAddr: "localhost:3334", MysqlIpAddr: "10.11.12.13:3334", Keyspace: "test_keyspace", Type: topo.TYPE_MASTER, State: topo.STATE_READ_WRITE, KeyRange: newKeyRange("-10"), } if err := ts.CreateTablet(tablet); err != nil { t.Errorf("CreateTablet: %v", err) } if err := ts.CreateTablet(tablet); err != topo.ErrNodeExists { t.Errorf("CreateTablet(again): %v", err) } if _, err := ts.GetTablet(topo.TabletAlias{cell, 666}); err != topo.ErrNoNode { t.Errorf("GetTablet(666): %v", err) } ti, err := ts.GetTablet(tablet.Alias()) if err != nil { t.Errorf("GetTablet %v: %v", tablet.Alias(), err) } if eq, err := tabletEqual(ti.Tablet, tablet); err != nil { t.Errorf("cannot compare tablets: %v", err) } else if !eq { t.Errorf("put and got tablets are not identical:\n%#v\n%#v", tablet, ti.Tablet) } if _, err := ts.GetTabletsByCell("666"); err != topo.ErrNoNode { t.Errorf("GetTabletsByCell(666): %v", err) } inCell, err := ts.GetTabletsByCell(cell) if err != nil { t.Errorf("GetTabletsByCell: %v", err) } if len(inCell) != 1 || inCell[0] != tablet.Alias() { t.Errorf("GetTabletsByCell: want [%v], got %v", tablet.Alias(), inCell) } ti.State = topo.STATE_READ_ONLY if err := topo.UpdateTablet(ts, ti); err != nil { t.Errorf("UpdateTablet: %v", err) } ti, err = ts.GetTablet(tablet.Alias()) if err != nil { t.Errorf("GetTablet %v: %v", tablet.Alias(), err) } if want := topo.STATE_READ_ONLY; ti.State != want { t.Errorf("ti.State: want %v, got %v", want, ti.State) } if err := ts.UpdateTabletFields(tablet.Alias(), func(t *topo.Tablet) error { t.State = topo.STATE_READ_WRITE return nil }); err != nil { t.Errorf("UpdateTabletFields: %v", err) } ti, err = ts.GetTablet(tablet.Alias()) if err != nil { t.Errorf("GetTablet %v: %v", tablet.Alias(), err) } if want := topo.STATE_READ_WRITE; ti.State != want { t.Errorf("ti.State: want %v, got %v", want, ti.State) } if err := ts.DeleteTablet(tablet.Alias()); err != nil { t.Errorf("DeleteTablet: %v", err) } if err := ts.DeleteTablet(tablet.Alias()); err != topo.ErrNoNode { t.Errorf("DeleteTablet(again): %v", err) } if _, err := ts.GetTablet(tablet.Alias()); err != topo.ErrNoNode { t.Errorf("GetTablet: expected error, tablet was deleted: %v", err) } }