// A non-nil return signals that event processing should stop. func (agent *ActionAgent) dispatchAction(actionPath, data string) error { agent.actionMutex.Lock() defer agent.actionMutex.Unlock() log.Infof("action dispatch %v", actionPath) actionNode, err := actionnode.ActionNodeFromJson(data, actionPath) if err != nil { log.Errorf("action decode failed: %v %v", actionPath, err) return nil } cmd := []string{ agent.vtActionBinFile, "-action", actionNode.Action, "-action-node", actionPath, "-action-guid", actionNode.ActionGuid, } cmd = append(cmd, logutil.GetSubprocessFlags()...) cmd = append(cmd, topo.GetSubprocessFlags()...) cmd = append(cmd, dbconfigs.GetSubprocessFlags()...) cmd = append(cmd, mysqlctl.GetSubprocessFlags()...) log.Infof("action launch %v", cmd) vtActionCmd := exec.Command(cmd[0], cmd[1:]...) stdOut, vtActionErr := vtActionCmd.CombinedOutput() if vtActionErr != nil { log.Errorf("agent action failed: %v %v\n%s", actionPath, vtActionErr, stdOut) // If the action failed, preserve single execution path semantics. return vtActionErr } log.Infof("Agent action completed %v %s", actionPath, stdOut) agent.afterAction(actionPath, actionNode.Action == actionnode.TABLET_ACTION_APPLY_SCHEMA) return nil }
// A non-nil return signals that event processing should stop. func (agent *ActionAgent) dispatchAction(actionPath, data string) error { log.Infof("action dispatch %v", actionPath) actionNode, err := ActionNodeFromJson(data, actionPath) if err != nil { log.Errorf("action decode failed: %v %v", actionPath, err) return nil } cmd := []string{ agent.vtActionBinFile, "-action", actionNode.Action, "-action-node", actionPath, "-action-guid", actionNode.ActionGuid, "-mycnf-file", agent.MycnfFile, } cmd = append(cmd, logutil.GetSubprocessFlags()...) cmd = append(cmd, topo.GetSubprocessFlags()...) if agent.DbConfigsFile != "" { cmd = append(cmd, "-db-configs-file", agent.DbConfigsFile) } if agent.DbCredentialsFile != "" { cmd = append(cmd, "-db-credentials-file", agent.DbCredentialsFile) } log.Infof("action launch %v", cmd) vtActionCmd := exec.Command(cmd[0], cmd[1:]...) stdOut, vtActionErr := vtActionCmd.CombinedOutput() if vtActionErr != nil { log.Errorf("agent action failed: %v %v\n%s", actionPath, vtActionErr, stdOut) // If the action failed, preserve single execution path semantics. return vtActionErr } log.Infof("agent action completed %v %s", actionPath, stdOut) // Save the old tablet so callbacks can have a better idea of the precise // nature of the transition. oldTablet := agent.Tablet().Tablet // Actions should have side effects on the tablet, so reload the data. if err := agent.readTablet(); err != nil { log.Warningf("failed rereading tablet after action - services may be inconsistent: %v %v", actionPath, err) } else { agent.runChangeCallbacks(oldTablet, actionPath) } // Maybe invalidate the schema. // This adds a dependency between tabletmanager and tabletserver, // so it's not ideal. But I (alainjobart) think it's better // to have up to date schema in vtocc. if actionNode.Action == TABLET_ACTION_APPLY_SCHEMA { tabletserver.ReloadSchema() } return nil }