func (ar *ActionRepository) ApplyTabletAction(actionName string, tabletAlias topo.TabletAlias, r *http.Request) *ActionResult { result := &ActionResult{Name: actionName, Parameters: tabletAlias.String()} action, ok := ar.tabletActions[actionName] if !ok { result.error("Unknown tablet action") return result } // check the role if action.role != "" { if err := acl.CheckAccessHTTP(r, action.role); err != nil { result.error("Access denied") return result } } // run the action ar.wr.ResetActionTimeout(wrangler.DefaultActionTimeout) output, err := action.method(ar.wr, tabletAlias, r) if err != nil { result.error(err.Error()) return result } result.Output = output return result }
// RecordTabletTagAction records a new TabletTagAction // into the specified Cleaner func RecordTabletTagAction(cleaner *Cleaner, tabletAlias topo.TabletAlias, name, value string) { cleaner.Record(TabletTagActionName, tabletAlias.String(), &TabletTagAction{ TabletAlias: tabletAlias, Name: name, Value: value, }) }
// ApplyTabletAction applies the provided action to the tablet. func (ar *ActionRepository) ApplyTabletAction(ctx context.Context, actionName string, tabletAlias topo.TabletAlias, r *http.Request) *ActionResult { result := &ActionResult{Name: actionName, Parameters: tabletAlias.String()} action, ok := ar.tabletActions[actionName] if !ok { result.error("Unknown tablet action") return result } // check the role if action.role != "" { if err := acl.CheckAccessHTTP(r, action.role); err != nil { result.error("Access denied") return result } } // run the action ctx, cancel := context.WithTimeout(ctx, *actionTimeout) wr := wrangler.New(logutil.NewConsoleLogger(), ar.ts, tmclient.NewTabletManagerClient(), *lockTimeout) output, err := action.method(ctx, wr, tabletAlias, r) cancel() if err != nil { result.error(err.Error()) return result } result.Output = output return result }
// DeleteTablet implements topo.Server. func (s *Server) DeleteTablet(ctx context.Context, tabletAlias topo.TabletAlias) error { cell, err := s.getCell(tabletAlias.Cell) if err != nil { return err } // Get the keyspace and shard names for the TabletChange event. ti, tiErr := s.GetTablet(ctx, tabletAlias) _, err = cell.Delete(tabletDirPath(tabletAlias.String()), true /* recursive */) if err != nil { return convertError(err) } // Only try to log if we have the required info. if tiErr == nil { // Only copy the identity info for the tablet. The rest has been deleted. event.Dispatch(&events.TabletChange{ Tablet: topo.Tablet{ Alias: ti.Tablet.Alias, Keyspace: ti.Tablet.Keyspace, Shard: ti.Tablet.Shard, }, Status: "deleted", }) } return nil }
// FIXME(msolomon) This validate presumes the master is up and running. // Even when that isn't true, there are validation processes that might be valuable. func (wr *Wrangler) validateShard(keyspace, shard string, pingTablets bool, wg *sync.WaitGroup, results chan<- vresult) { shardInfo, err := wr.ts.GetShard(keyspace, shard) if err != nil { results <- vresult{keyspace + "/" + shard, err} return } aliases, err := topo.FindAllTabletAliasesInShard(wr.ts, keyspace, shard) if err != nil { results <- vresult{keyspace + "/" + shard, err} } tabletMap, _ := GetTabletMap(wr.ts, aliases) var masterAlias topo.TabletAlias for _, alias := range aliases { tabletInfo, ok := tabletMap[alias] if !ok { results <- vresult{alias.String(), fmt.Errorf("tablet not found in map")} continue } if tabletInfo.Parent.Uid == topo.NO_TABLET { if masterAlias.Cell != "" { results <- vresult{alias.String(), fmt.Errorf("tablet already has a master %v", masterAlias)} } else { masterAlias = alias } } } if masterAlias.Cell == "" { results <- vresult{keyspace + "/" + shard, fmt.Errorf("no master for shard")} } else if shardInfo.MasterAlias != masterAlias { results <- vresult{keyspace + "/" + shard, fmt.Errorf("master mismatch for shard: found %v, expected %v", masterAlias, shardInfo.MasterAlias)} } for _, alias := range aliases { tabletReplicationPath := masterAlias.String() if alias != masterAlias { tabletReplicationPath += "/" + alias.String() } wg.Add(1) go func(alias topo.TabletAlias) { results <- vresult{tabletReplicationPath, topo.Validate(wr.ts, alias, tabletReplicationPath)} wg.Done() }(alias) } if pingTablets { wr.validateReplication(shardInfo, tabletMap, results) wr.pingTablets(tabletMap, wg, results) } return }
// FindChangeSlaveTypeActionByTarget finds the first action for the target func FindChangeSlaveTypeActionByTarget(cleaner *Cleaner, tabletAlias topo.TabletAlias) (*ChangeSlaveTypeAction, error) { action, err := cleaner.GetActionByName(ChangeSlaveTypeActionName, tabletAlias.String()) if err != nil { return nil, err } result, ok := action.(*ChangeSlaveTypeAction) if !ok { return nil, fmt.Errorf("Action with wrong type: %v", action) } return result, nil }
// diffPermissions is a helper method to asynchronously diff a permissions func (wr *Wrangler) diffPermissions(ctx context.Context, masterPermissions *myproto.Permissions, masterAlias topo.TabletAlias, alias topo.TabletAlias, wg *sync.WaitGroup, er concurrency.ErrorRecorder) { defer wg.Done() log.Infof("Gathering permissions for %v", alias) slavePermissions, err := wr.GetPermissions(ctx, alias) if err != nil { er.RecordError(err) return } log.Infof("Diffing permissions for %v", alias) myproto.DiffPermissions(masterAlias.String(), masterPermissions, alias.String(), slavePermissions, er) }
// helper method to asynchronously diff a schema func (wr *Wrangler) diffSchema(masterSchema *mysqlctl.SchemaDefinition, masterTabletAlias, alias topo.TabletAlias, includeViews bool, wg *sync.WaitGroup, er concurrency.ErrorRecorder) { defer wg.Done() relog.Info("Gathering schema for %v", alias) slaveSchema, err := wr.GetSchema(alias, nil, includeViews) if err != nil { er.RecordError(err) return } relog.Info("Diffing schema for %v", alias) mysqlctl.DiffSchema(masterTabletAlias.String(), masterSchema, alias.String(), slaveSchema, er) }
// helper method to asynchronously diff a schema func (wr *Wrangler) diffSchema(ctx context.Context, masterSchema *myproto.SchemaDefinition, masterTabletAlias, alias topo.TabletAlias, excludeTables []string, includeViews bool, wg *sync.WaitGroup, er concurrency.ErrorRecorder) { defer wg.Done() log.Infof("Gathering schema for %v", alias) slaveSchema, err := wr.GetSchema(ctx, alias, nil, excludeTables, includeViews) if err != nil { er.RecordError(err) return } log.Infof("Diffing schema for %v", alias) myproto.DiffSchema(masterTabletAlias.String(), masterSchema, alias.String(), slaveSchema, er) }
// FIXME(alainjobart) remove after migration func BackfillAlias(zkPath string, alias *topo.TabletAlias) error { if alias.IsZero() && zkPath != "" { zkPathParts := strings.Split(zkPath, "/") if len(zkPathParts) != 6 || zkPathParts[0] != "" || zkPathParts[1] != "zk" || zkPathParts[3] != "vt" || zkPathParts[4] != "tablets" { return fmt.Errorf("Invalid tablet path: %v", zkPath) } a, err := topo.ParseTabletAliasString(zkPathParts[2] + "-" + zkPathParts[5]) if err != nil { return err } *alias = a } return nil }
func (ar *ActionRepository) ApplyTabletAction(actionName string, tabletAlias topo.TabletAlias, r *http.Request) *ActionResult { result := &ActionResult{Name: actionName, Parameters: tabletAlias.String()} action, ok := ar.tabletActions[actionName] if !ok { result.error("Unknown tablet action") return result } ar.wr.ResetActionTimeout(wrangler.DefaultActionTimeout) output, err := action(ar.wr, tabletAlias, r) if err != nil { result.error(err.Error()) return result } result.Output = output return result }
// GetTablet implements topo.Server. func (s *Server) GetTablet(ctx context.Context, tabletAlias topo.TabletAlias) (*topo.TabletInfo, error) { cell, err := s.getCell(tabletAlias.Cell) if err != nil { return nil, err } resp, err := cell.Get(tabletFilePath(tabletAlias.String()), false /* sort */, false /* recursive */) if err != nil { return nil, convertError(err) } if resp.Node == nil { return nil, ErrBadResponse } value := &topo.Tablet{} if err := json.Unmarshal([]byte(resp.Node.Value), value); err != nil { return nil, fmt.Errorf("bad tablet data (%v): %q", err, resp.Node.Value) } return topo.NewTabletInfo(value, int64(resp.Node.ModifiedIndex)), nil }
// RecordStartBlpAction records a new StartBlpAction // into the specified Cleaner func RecordStartBlpAction(cleaner *Cleaner, tabletAlias topo.TabletAlias, waitTime time.Duration) { cleaner.Record(StartBlpActionName, tabletAlias.String(), &StartBlpAction{ TabletAlias: tabletAlias, WaitTime: waitTime, }) }
// add TABLET_ALIAS to environment func configureTabletHook(hk *hook.Hook, tabletAlias topo.TabletAlias) { if hk.ExtraEnv == nil { hk.ExtraEnv = make(map[string]string, 1) } hk.ExtraEnv["TABLET_ALIAS"] = tabletAlias.String() }
// PopulateReparentJournal adds an entry into the reparent_journal table. func (agent *ActionAgent) PopulateReparentJournal(ctx context.Context, timeCreatedNS int64, actionName string, masterAlias topo.TabletAlias, pos myproto.ReplicationPosition) error { cmds := mysqlctl.CreateReparentJournal() cmds = append(cmds, mysqlctl.PopulateReparentJournal(timeCreatedNS, actionName, masterAlias.String(), pos)) return agent.MysqlDaemon.ExecuteSuperQueryList(cmds) }
func (ex ZkExplorer) GetTabletPath(alias topo.TabletAlias) string { return path.Join("/zk", alias.Cell, "vt/tablets", alias.TabletUidStr()) }
func (wr *Wrangler) applySchemaShardComplex(statusArray []*TabletStatus, shardInfo *topo.ShardInfo, preflight *myproto.SchemaChangeResult, masterTabletAlias topo.TabletAlias, change string, newParentTabletAlias topo.TabletAlias, force bool) (*myproto.SchemaChangeResult, error) { // apply the schema change to all replica / slave tablets for _, status := range statusArray { // if already applied, we skip this guy diffs := myproto.DiffSchemaToArray("after", preflight.AfterSchema, status.ti.Alias.String(), status.beforeSchema) if len(diffs) == 0 { log.Infof("Tablet %v already has the AfterSchema, skipping", status.ti.Alias) continue } // make sure the before schema matches diffs = myproto.DiffSchemaToArray("master", preflight.BeforeSchema, status.ti.Alias.String(), status.beforeSchema) if len(diffs) > 0 { if force { log.Warningf("Tablet %v has inconsistent schema, ignoring: %v", status.ti.Alias, strings.Join(diffs, "\n")) } else { return nil, fmt.Errorf("Tablet %v has inconsistent schema: %v", status.ti.Alias, strings.Join(diffs, "\n")) } } // take this guy out of the serving graph if necessary ti, err := wr.ts.GetTablet(status.ti.Alias) if err != nil { return nil, err } typeChangeRequired := ti.Tablet.IsInServingGraph() if typeChangeRequired { // note we want to update the serving graph there err = wr.changeTypeInternal(ti.Alias, topo.TYPE_SCHEMA_UPGRADE) if err != nil { return nil, err } } // apply the schema change log.Infof("Applying schema change to slave %v in complex mode", status.ti.Alias) sc := &myproto.SchemaChange{Sql: change, Force: force, AllowReplication: false, BeforeSchema: preflight.BeforeSchema, AfterSchema: preflight.AfterSchema} _, err = wr.ApplySchema(status.ti.Alias, sc) if err != nil { return nil, err } // put this guy back into the serving graph if typeChangeRequired { err = wr.changeTypeInternal(ti.Alias, ti.Tablet.Type) if err != nil { return nil, err } } } // if newParentTabletAlias is passed in, use that as the new master if !newParentTabletAlias.IsZero() { log.Infof("Reparenting with new master set to %v", newParentTabletAlias) tabletMap, err := topo.GetTabletMapForShard(wr.ts, shardInfo.Keyspace(), shardInfo.ShardName()) if err != nil { return nil, err } slaveTabletMap, masterTabletMap := sortedTabletMap(tabletMap) newMasterTablet, err := wr.ts.GetTablet(newParentTabletAlias) if err != nil { return nil, err } err = wr.reparentShardGraceful(shardInfo, slaveTabletMap, masterTabletMap, newMasterTablet /*leaveMasterReadOnly*/, false) if err != nil { return nil, err } // Here we would apply the schema change to the old // master, but after a reparent it's in Scrap state, // so no need to. When/if reparent leaves the // original master in a different state (like replica // or rdonly), then we should apply the schema there // too. log.Infof("Skipping schema change on old master %v in complex mode, it's been Scrapped", masterTabletAlias) } return &myproto.SchemaChangeResult{BeforeSchema: preflight.BeforeSchema, AfterSchema: preflight.AfterSchema}, nil }
// RecordChangeSlaveTypeAction records a new ChangeSlaveTypeAction // into the specified Cleaner func RecordChangeSlaveTypeAction(cleaner *Cleaner, tabletAlias topo.TabletAlias, tabletType topo.TabletType) { cleaner.Record(ChangeSlaveTypeActionName, tabletAlias.String(), &ChangeSlaveTypeAction{ TabletAlias: tabletAlias, TabletType: tabletType, }) }
func TabletActionPathForAlias(alias topo.TabletAlias) string { return fmt.Sprintf("/zk/%v/vt/tablets/%v/action", alias.Cell, alias.TabletUidStr()) }
// TabletPathForAlias converts a tablet alias to the zk path func TabletPathForAlias(alias topo.TabletAlias) string { return fmt.Sprintf("/zk/%v/vt/tablets/%v", alias.Cell, alias.TabletUIDStr()) }
// GetTabletPath implements vtctld Explorer. func (ex Explorer) GetTabletPath(alias topo.TabletAlias) string { return path.Join(explorerRoot, alias.Cell, tabletDirPath(alias.String())) }
func (wr *Wrangler) applySchemaShardComplex(ctx context.Context, statusArray []*tabletStatus, shardInfo *topo.ShardInfo, preflight *myproto.SchemaChangeResult, masterTabletAlias topo.TabletAlias, change string, newParentTabletAlias topo.TabletAlias, force bool, waitSlaveTimeout time.Duration) (*myproto.SchemaChangeResult, error) { // apply the schema change to all replica / slave tablets for _, status := range statusArray { // if already applied, we skip this guy diffs := myproto.DiffSchemaToArray("after", preflight.AfterSchema, status.ti.Alias.String(), status.beforeSchema) if len(diffs) == 0 { log.Infof("Tablet %v already has the AfterSchema, skipping", status.ti.Alias) continue } // make sure the before schema matches diffs = myproto.DiffSchemaToArray("master", preflight.BeforeSchema, status.ti.Alias.String(), status.beforeSchema) if len(diffs) > 0 { if force { log.Warningf("Tablet %v has inconsistent schema, ignoring: %v", status.ti.Alias, strings.Join(diffs, "\n")) } else { return nil, fmt.Errorf("Tablet %v has inconsistent schema: %v", status.ti.Alias, strings.Join(diffs, "\n")) } } // take this guy out of the serving graph if necessary ti, err := wr.ts.GetTablet(ctx, status.ti.Alias) if err != nil { return nil, err } typeChangeRequired := ti.Tablet.IsInServingGraph() if typeChangeRequired { // note we want to update the serving graph there err = wr.changeTypeInternal(ctx, ti.Alias, topo.TYPE_SCHEMA_UPGRADE) if err != nil { return nil, err } } // apply the schema change log.Infof("Applying schema change to slave %v in complex mode", status.ti.Alias) sc := &myproto.SchemaChange{Sql: change, Force: force, AllowReplication: false, BeforeSchema: preflight.BeforeSchema, AfterSchema: preflight.AfterSchema} _, err = wr.ApplySchema(ctx, status.ti.Alias, sc) if err != nil { return nil, err } // put this guy back into the serving graph if typeChangeRequired { err = wr.changeTypeInternal(ctx, ti.Alias, ti.Tablet.Type) if err != nil { return nil, err } } } // if newParentTabletAlias is passed in, use that as the new master if !newParentTabletAlias.IsZero() { log.Infof("Reparenting with new master set to %v", newParentTabletAlias) oldMasterAlias := topo.ProtoToTabletAlias(shardInfo.MasterAlias) // Create reusable Reparent event with available info ev := &events.Reparent{} if err := wr.plannedReparentShardLocked(ctx, ev, shardInfo.Keyspace(), shardInfo.ShardName(), newParentTabletAlias, waitSlaveTimeout); err != nil { return nil, err } // Here we would apply the schema change to the old // master, but we just scrap it, to be consistent // with the previous implementation of the reparent. // (this code will be refactored at some point anyway) if err := wr.Scrap(ctx, oldMasterAlias, false, false); err != nil { wr.Logger().Warningf("Scrapping old master %v from shard %v/%v failed: %v", oldMasterAlias, shardInfo.Keyspace(), shardInfo.ShardName(), err) } } return &myproto.SchemaChangeResult{BeforeSchema: preflight.BeforeSchema, AfterSchema: preflight.AfterSchema}, nil }