// standardHttp starts serving standard HTTP (api/web) requests, to be used by normal clients func standardHttp(discovery bool) { m := martini.Classic() switch strings.ToLower(config.Config.AuthenticationMethod) { case "basic": { if config.Config.HTTPAuthUser == "" { // Still allowed; may be disallowed in future versions log.Warning("AuthenticationMethod is configured as 'basic' but HTTPAuthUser undefined. Running without authentication.") } m.Use(auth.Basic(config.Config.HTTPAuthUser, config.Config.HTTPAuthPassword)) } case "multi": { if config.Config.HTTPAuthUser == "" { // Still allowed; may be disallowed in future versions log.Fatal("AuthenticationMethod is configured as 'multi' but HTTPAuthUser undefined") } m.Use(auth.BasicFunc(func(username, password string) bool { if username == "readonly" { // Will be treated as "read-only" return true } return auth.SecureCompare(username, config.Config.HTTPAuthUser) && auth.SecureCompare(password, config.Config.HTTPAuthPassword) })) } default: { // We inject a dummy User object because we have function signatures with User argument in api.go m.Map(auth.User("")) } } m.Use(gzip.All()) // Render html templates from templates directory m.Use(render.Renderer(render.Options{ Directory: "resources", Layout: "templates/layout", HTMLContentType: "text/html", })) m.Use(martini.Static("resources/public")) inst.SetMaintenanceOwner(logic.ThisHostname) log.Info("Starting HTTP") if discovery { go logic.ContinuousDiscovery() } inst.ReadClusterAliases() http.API.RegisterRequests(m) http.Web.RegisterRequests(m) // Serve if err := nethttp.ListenAndServe(config.Config.ListenAddress, m); err != nil { log.Fatale(err) } }
// ForceRead reads configuration from given file name or bails out if it fails func ForceRead(file_name string) *Configuration { _, err := read(file_name) if err != nil { log.Fatal("Cannot read config file:", file_name, err) } return Config }
// Common code to deduce the instance's instanceKey if not defined. func deduceInstanceKeyIfNeeded(instance string, instanceKey *inst.InstanceKey) *inst.InstanceKey { if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } return instanceKey }
func getInstanceKey(instanceKey *inst.InstanceKey) *inst.InstanceKey { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance key") } return instanceKey }
// Common code to deduce the instance's instanceKey if not defined. func deduceInstanceKeyIfNeeded(instance string, instanceKey *inst.InstanceKey, allowFuzzyMatch bool) *inst.InstanceKey { if allowFuzzyMatch { instanceKey = inst.ReadFuzzyInstanceKeyIfPossible(instanceKey) } if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } return instanceKey }
// read reads configuration from given file, or silently skips if the file does not exist. // If the file does exist, then it is expected to be in valid JSON format or the function bails out. func read(file_name string) (*Configuration, error) { file, err := os.Open(file_name) if err == nil { decoder := json.NewDecoder(file) err := decoder.Decode(Config) if err == nil { log.Infof("Read config: %s", file_name) } else { log.Fatal("Cannot read config file:", file_name, err) } } return Config, err }
// read reads configuration from given file, or silently skips if the file does not exist. // If the file does exist, then it is expected to be in valid JSON format or the function bails out. func read(file_name string) (*Configuration, error) { file, err := os.Open(file_name) if err == nil { decoder := json.NewDecoder(file) err := decoder.Decode(Config) if err == nil { log.Infof("Read config: %s", file_name) } else { log.Fatal("Cannot read config file:", file_name, err) } if Config.MySQLOrchestratorCredentialsConfigFile != "" { mySQLConfig := struct { Client struct { User string Password string } }{} err := gcfg.ReadFileInto(&mySQLConfig, Config.MySQLOrchestratorCredentialsConfigFile) if err != nil { log.Fatalf("Failed to parse gcfg data from file: %+v", err) } else { log.Debugf("Parsed orchestrator credentials from %s", Config.MySQLOrchestratorCredentialsConfigFile) Config.MySQLOrchestratorUser = mySQLConfig.Client.User Config.MySQLOrchestratorPassword = mySQLConfig.Client.Password } } if Config.MySQLTopologyCredentialsConfigFile != "" { mySQLConfig := struct { Client struct { User string Password string } }{} err := gcfg.ReadFileInto(&mySQLConfig, Config.MySQLTopologyCredentialsConfigFile) if err != nil { log.Fatalf("Failed to parse gcfg data from file: %+v", err) } else { log.Debugf("Parsed topology credentials from %s", Config.MySQLTopologyCredentialsConfigFile) Config.MySQLTopologyUser = mySQLConfig.Client.User Config.MySQLTopologyPassword = mySQLConfig.Client.Password } } } return Config, err }
// main is the application's entry point. It will either spawn a CLI or HTTP itnerfaces. func main() { configFile := flag.String("config", "", "config file name") verbose := flag.Bool("verbose", false, "verbose") debug := flag.Bool("debug", false, "debug mode (very verbose)") stack := flag.Bool("stack", false, "add stack trace upon error") flag.Parse() log.SetLevel(log.ERROR) if *verbose { log.SetLevel(log.INFO) } if *debug { log.SetLevel(log.DEBUG) } if *stack { log.SetPrintStackTrace(*stack) } log.Info("starting") if len(*configFile) > 0 { config.ForceRead(*configFile) } else { config.Read("/etc/orchestrator-agent.conf.json", "conf/orchestrator-agent.conf.json", "orchestrator-agent.conf.json") } if len(config.Config.AgentsServer) == 0 { log.Fatal("AgentsServer unconfigured. Please set to the HTTP address orchestrator serves agents (port is by default 3001)") } log.Debugf("Process token: %s", agent.ProcessToken.Hash) go acceptSignal() app.Http() }
// Cli initiates a command line interface, executing requested command. func Cli(command string, strict bool, instance string, destination string, owner string, reason string, duration string, pattern string, clusterAlias string, pool string, hostnameFlag string) { if instance != "" && !strings.Contains(instance, ":") { instance = fmt.Sprintf("%s:%d", instance, config.Config.DefaultInstancePort) } instanceKey, err := inst.ParseInstanceKey(instance) if err != nil { instanceKey = nil } rawInstanceKey, err := inst.NewRawInstanceKey(instance) if err != nil { rawInstanceKey = nil } if destination != "" && !strings.Contains(destination, ":") { destination = fmt.Sprintf("%s:%d", destination, config.Config.DefaultInstancePort) } destinationKey, err := inst.ParseInstanceKey(destination) if err != nil { destinationKey = nil } if hostname, err := os.Hostname(); err == nil { thisInstanceKey = &inst.InstanceKey{Hostname: hostname, Port: int(config.Config.DefaultInstancePort)} } postponedFunctionsContainer := inst.NewPostponedFunctionsContainer() if len(owner) == 0 { // get os username as owner usr, err := user.Current() if err != nil { log.Fatale(err) } owner = usr.Username } inst.SetMaintenanceOwner(owner) skipDatabaseCommands := false switch command { case "reset-internal-db-deployment": skipDatabaseCommands = true case "help": skipDatabaseCommands = true } if !skipDatabaseCommands { process.ContinuousRegistration(string(process.OrchestratorExecutionCliMode), command) } // begin commands switch command { // smart mode case registerCliCommand("relocate", "Smart relocation", `Relocate a slave beneath another instance`), registerCliCommand("relocate-below", "Smart relocation", `Synonym to 'relocate', will be deprecated`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } _, err := inst.RelocateBelow(instanceKey, destinationKey) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), destinationKey.DisplayString())) } case registerCliCommand("relocate-slaves", "Smart relocation", `Relocates all or part of the slaves of a given instance under another instance`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } slaves, _, err, errs := inst.RelocateSlaves(instanceKey, destinationKey, pattern) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range slaves { fmt.Println(slave.Key.DisplayString()) } } } case registerCliCommand("regroup-slaves", "Smart relocation", `Given an instance, pick one of its slave and make it local master of its siblings`): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } lostSlaves, equalSlaves, aheadSlaves, promotedSlave, err := inst.RegroupSlaves(instanceKey, false, func(candidateSlave *inst.Instance) { fmt.Println(candidateSlave.Key.DisplayString()) }, postponedFunctionsContainer) postponedFunctionsContainer.InvokePostponed() if promotedSlave == nil { log.Fatalf("Could not regroup slaves of %+v; error: %+v", *instanceKey, err) } fmt.Println(fmt.Sprintf("%s lost: %d, trivial: %d, pseudo-gtid: %d", promotedSlave.Key.DisplayString(), len(lostSlaves), len(equalSlaves), len(aheadSlaves))) if err != nil { log.Fatale(err) } } // General replication commands // move, binlog file:pos case registerCliCommand("move-up", "Classic file:pos relocation", `Move a slave one level up the topology`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) instance, err := inst.MoveUp(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), instance.MasterKey.DisplayString())) } case registerCliCommand("move-up-slaves", "Classic file:pos relocation", `Moves slaves of the given instance one level up the topology`): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } movedSlaves, _, err, errs := inst.MoveUpSlaves(instanceKey, pattern) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range movedSlaves { fmt.Println(slave.Key.DisplayString()) } } } case registerCliCommand("move-below", "Classic file:pos relocation", `Moves a slave beneath its sibling. Both slaves must be actively replicating from same master.`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) if destinationKey == nil { log.Fatal("Cannot deduce destination/sibling:", destination) } _, err := inst.MoveBelow(instanceKey, destinationKey) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), destinationKey.DisplayString())) } case registerCliCommand("move-equivalent", "Classic file:pos relocation", `Moves a slave beneath another server, based on previously recorded "equivalence coordinates"`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } _, err := inst.MoveEquivalent(instanceKey, destinationKey) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), destinationKey.DisplayString())) } case registerCliCommand("repoint", "Classic file:pos relocation", `Make the given instance replicate from another instance without changing the binglog coordinates. Use with care`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) // destinationKey can be null, in which case the instance repoints to its existing master instance, err := inst.Repoint(instanceKey, destinationKey, inst.GTIDHintNeutral) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), instance.MasterKey.DisplayString())) } case registerCliCommand("repoint-slaves", "Classic file:pos relocation", `Repoint all slaves of given instance to replicate back from the instance. Use with care`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) repointedSlaves, err, errs := inst.RepointSlavesTo(instanceKey, pattern, destinationKey) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range repointedSlaves { fmt.Println(fmt.Sprintf("%s<%s", slave.Key.DisplayString(), instanceKey.DisplayString())) } } } case registerCliCommand("enslave-siblings", "Classic file:pos relocation", `Turn all siblings of a slave into its sub-slaves.`): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, _, err := inst.EnslaveSiblings(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("enslave-master", "Classic file:pos relocation", `Turn an instance into a master of its own master; essentially switch the two.`): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.EnslaveMaster(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("make-co-master", "Classic file:pos relocation", `Create a master-master replication. Given instance is a slave which replicates directly from a master.`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.MakeCoMaster(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("get-candidate-slave", "Classic file:pos relocation", `Information command suggesting the most up-to-date slave of a given instance that is good for promotion`): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } instance, _, _, _, err := inst.GetCandidateSlave(instanceKey, false) if err != nil { log.Fatale(err) } else { fmt.Println(instance.Key.DisplayString()) } } case registerCliCommand("regroup-slaves-bls", "Binlog server relocation", `Regroup Binlog Server slaves of a given instance`): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, promotedBinlogServer, err := inst.RegroupSlavesBinlogServers(instanceKey, false) if promotedBinlogServer == nil { log.Fatalf("Could not regroup binlog server slaves of %+v; error: %+v", *instanceKey, err) } fmt.Println(promotedBinlogServer.Key.DisplayString()) if err != nil { log.Fatale(err) } } // move, GTID case registerCliCommand("move-gtid", "GTID relocation", `Move a slave beneath another instance.`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } _, err := inst.MoveBelowGTID(instanceKey, destinationKey) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), destinationKey.DisplayString())) } case registerCliCommand("move-slaves-gtid", "GTID relocation", `Moves all slaves of a given instance under another (destination) instance using GTID`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } movedSlaves, _, err, errs := inst.MoveSlavesGTID(instanceKey, destinationKey, pattern) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range movedSlaves { fmt.Println(slave.Key.DisplayString()) } } } case registerCliCommand("regroup-slaves-gtid", "GTID relocation", `Given an instance, pick one of its slave and make it local master of its siblings, using GTID.`): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } lostSlaves, movedSlaves, promotedSlave, err := inst.RegroupSlavesGTID(instanceKey, false, func(candidateSlave *inst.Instance) { fmt.Println(candidateSlave.Key.DisplayString()) }) if promotedSlave == nil { log.Fatalf("Could not regroup slaves of %+v; error: %+v", *instanceKey, err) } fmt.Println(fmt.Sprintf("%s lost: %d, moved: %d", promotedSlave.Key.DisplayString(), len(lostSlaves), len(movedSlaves))) if err != nil { log.Fatale(err) } } // Pseudo-GTID case registerCliCommand("match", "Pseudo-GTID relocation", `Matches a slave beneath another (destination) instance using Pseudo-GTID`), registerCliCommand("match-below", "Pseudo-GTID relocation", `Synonym to 'match', will be deprecated`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } _, _, err := inst.MatchBelow(instanceKey, destinationKey, true) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), destinationKey.DisplayString())) } case registerCliCommand("match-up", "Pseudo-GTID relocation", `Transport the slave one level up the hierarchy, making it child of its grandparent, using Pseudo-GTID`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) instance, _, err := inst.MatchUp(instanceKey, true) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), instance.MasterKey.DisplayString())) } case registerCliCommand("rematch", "Pseudo-GTID relocation", `Reconnect a slave onto its master, via PSeudo-GTID.`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) instance, _, err := inst.RematchSlave(instanceKey, true) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), instance.MasterKey.DisplayString())) } case registerCliCommand("match-slaves", "Pseudo-GTID relocation", `Matches all slaves of a given instance under another (destination) instance using Pseudo-GTID`), registerCliCommand("multi-match-slaves", "Pseudo-GTID relocation", `Synonym to 'match-slaves', will be deprecated`): { // Move all slaves of "instance" beneath "destination" if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } matchedSlaves, _, err, errs := inst.MultiMatchSlaves(instanceKey, destinationKey, pattern) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range matchedSlaves { fmt.Println(slave.Key.DisplayString()) } } } case registerCliCommand("match-up-slaves", "Pseudo-GTID relocation", `Matches slaves of the given instance one level up the topology, making them siblings of given instance, using Pseudo-GTID`): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } matchedSlaves, _, err, errs := inst.MatchUpSlaves(instanceKey, pattern) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range matchedSlaves { fmt.Println(slave.Key.DisplayString()) } } } case registerCliCommand("regroup-slaves-pgtid", "Pseudo-GTID relocation", `Given an instance, pick one of its slave and make it local master of its siblings, using Pseudo-GTID.`): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } lostSlaves, equalSlaves, aheadSlaves, promotedSlave, err := inst.RegroupSlavesPseudoGTID(instanceKey, false, func(candidateSlave *inst.Instance) { fmt.Println(candidateSlave.Key.DisplayString()) }, postponedFunctionsContainer) postponedFunctionsContainer.InvokePostponed() if promotedSlave == nil { log.Fatalf("Could not regroup slaves of %+v; error: %+v", *instanceKey, err) } fmt.Println(fmt.Sprintf("%s lost: %d, trivial: %d, pseudo-gtid: %d", promotedSlave.Key.DisplayString(), len(lostSlaves), len(equalSlaves), len(aheadSlaves))) if err != nil { log.Fatale(err) } } // General replication commands case registerCliCommand("enable-gtid", "Replication, general", `If possible, turn on GTID replication`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.EnableGTID(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("disable-gtid", "Replication, general", `Turn off GTID replication, back to file:pos replication`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.DisableGTID(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("reset-master-gtid-remove-own-uuid", "Replication, general", `Reset master on instance, remove GTID entries generated by instance`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.ResetMasterGTIDOperation(instanceKey, true, "") if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("skip-query", "Replication, general", `Skip a single statement on a slave; either when running with GTID or without`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.SkipQuery(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("stop-slave", "Replication, general", `Issue a STOP SLAVE on an instance`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.StopSlave(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("start-slave", "Replication, general", `Issue a START SLAVE on an instance`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.StartSlave(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("restart-slave", "Replication, general", `STOP and START SLAVE on an instance`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.RestartSlave(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("reset-slave", "Replication, general", `Issues a RESET SLAVE command; use with care`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.ResetSlaveOperation(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("detach-slave", "Replication, general", `Stops replication and modifies binlog position into an impossible, yet reversible, value.`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.DetachSlaveOperation(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("reattach-slave", "Replication, general", `Undo a detach-slave operation`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.ReattachSlaveOperation(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("detach-slave-master-host", "Replication, general", `Stops replication and modifies Master_Host into an impossible, yet reversible, value.`): { if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.DetachSlaveMasterHost(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("reattach-slave-master-host", "Replication, general", `Undo a detach-slave-master-host operation`): { if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.ReattachSlaveMasterHost(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("master-pos-wait", "Replication, general", `Wait until slave reaches given replication coordinates (--binlog=file:pos)`): { if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatalf("Unresolved instance") } instance, err := inst.ReadTopologyInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } var binlogCoordinates *inst.BinlogCoordinates if binlogCoordinates, err = inst.ParseBinlogCoordinates(*config.RuntimeCLIFlags.BinlogFile); err != nil { log.Fatalf("Expecing --binlog argument as file:pos") } _, err = inst.MasterPosWait(instanceKey, binlogCoordinates) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } // Pool case registerCliCommand("set-read-only", "Instance", `Turn an instance read-only, via SET GLOBAL read_only := 1`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.SetReadOnly(instanceKey, true) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("set-writeable", "Instance", `Turn an instance writeable, via SET GLOBAL read_only := 0`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) _, err := inst.SetReadOnly(instanceKey, false) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } // Binary log operations case registerCliCommand("flush-binary-logs", "Binary logs", `Flush binary logs on an instance`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) var err error if *config.RuntimeCLIFlags.BinlogFile == "" { _, err = inst.FlushBinaryLogs(instanceKey, 1) } else { _, err = inst.FlushBinaryLogsTo(instanceKey, *config.RuntimeCLIFlags.BinlogFile) } if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("purge-binary-logs", "Binary logs", `Purge binary logs of an instance`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) var err error if *config.RuntimeCLIFlags.BinlogFile == "" { log.Fatal("expecting --binlog value") } _, err = inst.PurgeBinaryLogsTo(instanceKey, *config.RuntimeCLIFlags.BinlogFile) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("last-pseudo-gtid", "Binary logs", `Find latest Pseudo-GTID entry in instance's binary logs`): { if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatalf("Unresolved instance") } instance, err := inst.ReadTopologyInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } coordinates, text, err := inst.FindLastPseudoGTIDEntry(instance, instance.RelaylogCoordinates, nil, strict, nil) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%+v:%s", *coordinates, text)) } case registerCliCommand("find-binlog-entry", "Binary logs", `Get binlog file:pos of entry given by --pattern (exact full match, not a regular expression) in a given instance`): { if pattern == "" { log.Fatal("No pattern given") } if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatalf("Unresolved instance") } instance, err := inst.ReadTopologyInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } coordinates, err := inst.SearchEntryInInstanceBinlogs(instance, pattern, false) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%+v", *coordinates)) } case registerCliCommand("correlate-binlog-pos", "Binary logs", `Given an instance (-i) and binlog coordinates (--binlog=file:pos), find the correlated coordinates in another instance (-d)`): { if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatalf("Unresolved instance") } instance, err := inst.ReadTopologyInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } if !instance.LogBinEnabled { log.Fatalf("Instance does not have binary logs: %+v", *instanceKey) } if destinationKey == nil { log.Fatal("Cannot deduce target instance:", destination) } otherInstance, err := inst.ReadTopologyInstance(destinationKey) if err != nil { log.Fatale(err) } if otherInstance == nil { log.Fatalf("Instance not found: %+v", *destinationKey) } var binlogCoordinates *inst.BinlogCoordinates if *config.RuntimeCLIFlags.BinlogFile == "" { binlogCoordinates = &instance.SelfBinlogCoordinates } else { if binlogCoordinates, err = inst.ParseBinlogCoordinates(*config.RuntimeCLIFlags.BinlogFile); err != nil { log.Fatalf("Expecing --binlog argument as file:pos") } } coordinates, _, err := inst.CorrelateBinlogCoordinates(instance, binlogCoordinates, otherInstance) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%+v", *coordinates)) } // Pool case registerCliCommand("submit-pool-instances", "Pools", `Submit a pool name with a list of instances in that pool`): { if pool == "" { log.Fatal("Please submit --pool") } err := inst.ApplyPoolInstances(pool, instance) if err != nil { log.Fatale(err) } } case registerCliCommand("cluster-pool-instances", "Pools", `List all pools and their associated instances`): { clusterPoolInstances, err := inst.ReadAllClusterPoolInstances() if err != nil { log.Fatale(err) } for _, clusterPoolInstance := range clusterPoolInstances { fmt.Println(fmt.Sprintf("%s\t%s\t%s\t%s:%d", clusterPoolInstance.ClusterName, clusterPoolInstance.ClusterAlias, clusterPoolInstance.Pool, clusterPoolInstance.Hostname, clusterPoolInstance.Port)) } } // Information case registerCliCommand("find", "Information", `Find instances whose hostname matches given regex pattern`): { if pattern == "" { log.Fatal("No pattern given") } instances, err := inst.FindInstances(pattern) if err != nil { log.Fatale(err) } else { for _, instance := range instances { fmt.Println(instance.Key.DisplayString()) } } } case registerCliCommand("clusters", "Information", `List all clusters known to orchestrator`): { clusters, err := inst.ReadClusters() if err != nil { log.Fatale(err) } else { fmt.Println(strings.Join(clusters, "\n")) } } case registerCliCommand("topology", "Information", `Show an ascii-graph of a replication topology, given a member of that topology`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) output, err := inst.ASCIITopology(instanceKey, pattern) if err != nil { log.Fatale(err) } fmt.Println(output) } case registerCliCommand("which-instance", "Information", `Output the fully-qualified hostname:port representation of the given instance, or error if unknown`): { if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatalf("Unable to get master: unresolved instance") } instance, _, err := inst.ReadInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } fmt.Println(instance.Key.DisplayString()) } case registerCliCommand("which-cluster", "Information", `Output the name of the cluster an instance belongs to, or error if unknown to orchestrator`): { clusterName := getClusterName(clusterAlias, instanceKey) fmt.Println(clusterName) } case registerCliCommand("which-cluster-instances", "Information", `Output the list of instances participating in same cluster as given instance`): { clusterName := getClusterName(clusterAlias, instanceKey) instances, err := inst.ReadClusterInstances(clusterName) if err != nil { log.Fatale(err) } for _, clusterInstance := range instances { fmt.Println(clusterInstance.Key.DisplayString()) } } case registerCliCommand("which-cluster-osc-slaves", "Information", `Output a list of slaves in same cluster as given instance, that could serve as a pt-online-schema-change operation control slaves`): { clusterName := getClusterName(clusterAlias, instanceKey) instances, err := inst.GetClusterOSCSlaves(clusterName) if err != nil { log.Fatale(err) } for _, clusterInstance := range instances { fmt.Println(clusterInstance.Key.DisplayString()) } } case registerCliCommand("which-master", "Information", `Output the fully-qualified hostname:port representation of a given instance's master`): { if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatalf("Unable to get master: unresolved instance") } instance, _, err := inst.ReadInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } fmt.Println(instance.MasterKey.DisplayString()) } case registerCliCommand("which-slaves", "Information", `Output the fully-qualified hostname:port list of slaves of a given instance`): { if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatalf("Unable to get slaves: unresolved instance") } slaves, err := inst.ReadSlaveInstances(instanceKey) if err != nil { log.Fatale(err) } for _, slave := range slaves { fmt.Println(slave.Key.DisplayString()) } } case registerCliCommand("instance-status", "Information", `Output short status on a given instance`): { if instanceKey == nil { instanceKey = assignThisInstanceKey() } if instanceKey == nil { log.Fatalf("Unable to get status: unresolved instance") } instance, _, err := inst.ReadInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } fmt.Println(instance.HumanReadableDescription()) } case registerCliCommand("get-cluster-heuristic-lag", "Information", `For a given cluster (indicated by an instance or alias), output a heuristic "representative" lag of that cluster`): { clusterName := getClusterName(clusterAlias, instanceKey) lag, err := inst.GetClusterHeuristicLag(clusterName) if err != nil { log.Fatale(err) } fmt.Println(lag) } // Instance management case registerCliCommand("discover", "Instance management", `Lookup an instance, investigate it`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) instance, err := inst.ReadTopologyInstance(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instance.Key.DisplayString()) } case registerCliCommand("forget", "Instance management", `Forget about an instance's existence`): { if rawInstanceKey == nil { rawInstanceKey = assignThisInstanceKey() } if rawInstanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } err := inst.ForgetInstance(rawInstanceKey) if err != nil { log.Fatale(err) } fmt.Println(rawInstanceKey.DisplayString()) } case registerCliCommand("begin-maintenance", "Instance management", `Request a maintenance lock on an instance`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) if reason == "" { log.Fatal("--reason option required") } var durationSeconds int = 0 if duration != "" { durationSeconds, err = util.SimpleTimeToSeconds(duration) if err != nil { log.Fatale(err) } if durationSeconds < 0 { log.Fatalf("Duration value must be non-negative. Given value: %d", durationSeconds) } } maintenanceKey, err := inst.BeginBoundedMaintenance(instanceKey, inst.GetMaintenanceOwner(), reason, uint(durationSeconds)) if err == nil { log.Infof("Maintenance key: %+v", maintenanceKey) log.Infof("Maintenance duration: %d seconds", durationSeconds) } if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("end-maintenance", "Instance management", `Remove maintenance lock from an instance`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) err := inst.EndMaintenanceByInstanceKey(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("begin-downtime", "Instance management", `Mark an instance as downtimed`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) if reason == "" { log.Fatal("--reason option required") } var durationSeconds int = 0 if duration != "" { durationSeconds, err = util.SimpleTimeToSeconds(duration) if err != nil { log.Fatale(err) } if durationSeconds < 0 { log.Fatalf("Duration value must be non-negative. Given value: %d", durationSeconds) } } err := inst.BeginDowntime(instanceKey, inst.GetMaintenanceOwner(), reason, uint(durationSeconds)) if err == nil { log.Infof("Downtime duration: %d seconds", durationSeconds) } else { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("end-downtime", "Instance management", `Indicate an instance is no longer downtimed`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) err := inst.EndDowntime(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } // Recovery & analysis case registerCliCommand("recover", "Recovery", `Do auto-recovery given a dead instance`), registerCliCommand("recover-lite", "Recovery", `Do auto-recovery given a dead instance. Orchestrator chooses the best course of actionwithout executing external processes`): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } recoveryAttempted, promotedInstanceKey, err := logic.CheckAndRecover(instanceKey, destinationKey, (command == "recover-lite")) if err != nil { log.Fatale(err) } if recoveryAttempted { if promotedInstanceKey == nil { log.Fatalf("Recovery attempted yet no slave promoted") } fmt.Println(promotedInstanceKey.DisplayString()) } } case registerCliCommand("replication-analysis", "Recovery", `Request an analysis of potential crash incidents in all known topologies`): { analysis, err := inst.GetReplicationAnalysis("", false, false) if err != nil { log.Fatale(err) } for _, entry := range analysis { fmt.Println(fmt.Sprintf("%s (cluster %s): %s", entry.AnalyzedInstanceKey.DisplayString(), entry.ClusterDetails.ClusterName, entry.Analysis)) } } case registerCliCommand("ack-cluster-recoveries", "Recovery", `Acknowledge recoveries for a given cluster; this unblocks pending future recoveries`): { if reason == "" { log.Fatal("--reason option required (comment your ack)") } clusterName := getClusterName(clusterAlias, instanceKey) countRecoveries, err := logic.AcknowledgeClusterRecoveries(clusterName, inst.GetMaintenanceOwner(), reason) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%d recoveries acknowldged", countRecoveries)) } case registerCliCommand("ack-instance-recoveries", "Recovery", `Acknowledge recoveries for a given instance; this unblocks pending future recoveries`): { if reason == "" { log.Fatal("--reason option required (comment your ack)") } instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) countRecoveries, err := logic.AcknowledgeInstanceRecoveries(instanceKey, inst.GetMaintenanceOwner(), reason) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%d recoveries acknowldged", countRecoveries)) } // Instance meta case registerCliCommand("register-candidate", "Instance, meta", `Indicate that a specific instance is a preferred candidate for master promotion`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) err := inst.RegisterCandidateInstance(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("register-hostname-unresolve", "Instance, meta", `Assigns the given instance a virtual (aka "unresolved") name`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) err := inst.RegisterHostnameUnresolve(instanceKey, hostnameFlag) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case registerCliCommand("deregister-hostname-unresolve", "Instance, meta", `Explicitly deregister/dosassociate a hostname with an "unresolved" name`): { instanceKey = deduceInstanceKeyIfNeeded(instance, instanceKey) err := inst.DeregisterHostnameUnresolve(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } // meta case registerCliCommand("snapshot-topologies", "Meta", `Take a snapshot of existing topologies.`): { err := inst.SnapshotTopologies() if err != nil { log.Fatale(err) } } case registerCliCommand("continuous", "Meta", `Enter continuous mode, and actively poll for instances, diagnose problems, do maintenance`): { logic.ContinuousDiscovery() } case registerCliCommand("resolve", "Meta", `Resolve given hostname`): { if rawInstanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if conn, err := net.Dial("tcp", rawInstanceKey.DisplayString()); err == nil { log.Debugf("tcp test is good; got connection %+v", conn) conn.Close() } else { log.Fatale(err) } if cname, err := inst.GetCNAME(rawInstanceKey.Hostname); err == nil { log.Debugf("GetCNAME() %+v, %+v", cname, err) rawInstanceKey.Hostname = cname fmt.Println(rawInstanceKey.DisplayString()) } else { log.Fatale(err) } } case registerCliCommand("reset-hostname-resolve-cache", "Meta", `Clear the hostname resolve cache`): { err := inst.ResetHostnameResolveCache() if err != nil { log.Fatale(err) } fmt.Println("hostname resolve cache cleared") } case registerCliCommand("reset-internal-db-deployment", "Meta, internal", `Clear internal db deployment history, use if somehow corrupted internal deployment history`): { config.Config.SkipOrchestratorDatabaseUpdate = true db.ResetInternalDeployment() fmt.Println("Internal db deployment history reset. Next orchestrator execution will rebuild internal db structure (no data will be lost)") } // Help case "help": { fmt.Fprintf(os.Stderr, availableCommandsUsage()) } default: log.Fatalf("Unknown command: \"%s\". %s", command, availableCommandsUsage()) } }
// Cli initiates a command line interface, executing requested command. func Cli(command string, strict bool, instance string, destination string, owner string, reason string, duration string, pattern string, clusterAlias string, pool string, hostnameFlag string) { if instance != "" && !strings.Contains(instance, ":") { instance = fmt.Sprintf("%s:%d", instance, config.Config.DefaultInstancePort) } instanceKey, err := inst.ParseInstanceKey(instance) if err != nil { instanceKey = nil } rawInstanceKey, err := inst.NewRawInstanceKey(instance) if err != nil { rawInstanceKey = nil } if destination != "" && !strings.Contains(destination, ":") { destination = fmt.Sprintf("%s:%d", destination, config.Config.DefaultInstancePort) } destinationKey, err := inst.ParseInstanceKey(destination) if err != nil { destinationKey = nil } if hostname, err := os.Hostname(); err == nil { thisInstanceKey = &inst.InstanceKey{Hostname: hostname, Port: int(config.Config.DefaultInstancePort)} } if len(owner) == 0 { // get os username as owner usr, err := user.Current() if err != nil { log.Fatale(err) } owner = usr.Username } inst.SetMaintenanceOwner(owner) // begin commands switch command { // Instance meta case cliCommand("discover"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } instance, err := inst.ReadTopologyInstance(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instance.Key.DisplayString()) } case cliCommand("forget"): { if rawInstanceKey == nil { rawInstanceKey = thisInstanceKey } if rawInstanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } err := inst.ForgetInstance(rawInstanceKey) if err != nil { log.Fatale(err) } fmt.Println(rawInstanceKey.DisplayString()) } case cliCommand("resolve"): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if conn, err := net.Dial("tcp", instanceKey.DisplayString()); err == nil { conn.Close() } else { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("register-hostname-unresolve"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } err := inst.RegisterHostnameUnresolve(instanceKey, hostnameFlag) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("deregister-hostname-unresolve"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } err := inst.DeregisterHostnameUnresolve(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("register-candidate"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } err := inst.RegisterCandidateInstance(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } // Instance case cliCommand("begin-maintenance"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if reason == "" { log.Fatal("--reason option required") } var durationSeconds int = 0 if duration != "" { durationSeconds, err = util.SimpleTimeToSeconds(duration) if err != nil { log.Fatale(err) } if durationSeconds < 0 { log.Fatalf("Duration value must be non-negative. Given value: %d", durationSeconds) } } maintenanceKey, err := inst.BeginBoundedMaintenance(instanceKey, inst.GetMaintenanceOwner(), reason, uint(durationSeconds)) if err == nil { log.Infof("Maintenance key: %+v", maintenanceKey) log.Infof("Maintenance duration: %d seconds", durationSeconds) } if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("end-maintenance"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } err := inst.EndMaintenanceByInstanceKey(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("begin-downtime"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if reason == "" { log.Fatal("--reason option required") } var durationSeconds int = 0 if duration != "" { durationSeconds, err = util.SimpleTimeToSeconds(duration) if err != nil { log.Fatale(err) } if durationSeconds < 0 { log.Fatalf("Duration value must be non-negative. Given value: %d", durationSeconds) } } err := inst.BeginDowntime(instanceKey, inst.GetMaintenanceOwner(), reason, uint(durationSeconds)) if err == nil { log.Infof("Downtime duration: %d seconds", durationSeconds) } else { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("end-downtime"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } err := inst.EndDowntime(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("set-read-only"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.SetReadOnly(instanceKey, true) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("set-writeable"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.SetReadOnly(instanceKey, false) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("flush-binary-logs"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } var err error if *config.RuntimeCLIFlags.BinlogFile == "" { err = inst.FlushBinaryLogs(instanceKey, 1) } else { _, err = inst.FlushBinaryLogsTo(instanceKey, *config.RuntimeCLIFlags.BinlogFile) } if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("last-pseudo-gtid"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatalf("Unresolved instance") } instance, err := inst.ReadTopologyInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } coordinates, text, err := inst.FindLastPseudoGTIDEntry(instance, instance.RelaylogCoordinates, strict, nil) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%+v:%s", *coordinates, text)) } // replication case cliCommand("stop-slave"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.StopSlave(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("start-slave"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.StartSlave(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("restart-slave"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.RestartSlave(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("reset-slave"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.ResetSlaveOperation(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("detach-slave"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.DetachSlaveOperation(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("reattach-slave"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.ReattachSlaveOperation(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("enable-gtid"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.EnableGTID(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("disable-gtid"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.DisableGTID(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("skip-query"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.SkipQuery(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } // move case cliCommand("relocate"), cliCommand("relocate-below"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } _, err := inst.RelocateBelow(instanceKey, destinationKey) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), destinationKey.DisplayString())) } case cliCommand("relocate-slaves"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } slaves, _, err, errs := inst.RelocateSlaves(instanceKey, destinationKey, pattern) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range slaves { fmt.Println(slave.Key.DisplayString()) } } } case cliCommand("move-up"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } instance, err := inst.MoveUp(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), instance.MasterKey.DisplayString())) } case cliCommand("move-up-slaves"): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } movedSlaves, _, err, errs := inst.MoveUpSlaves(instanceKey, pattern) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range movedSlaves { fmt.Println(slave.Key.DisplayString()) } } } case cliCommand("move-below"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if destinationKey == nil { log.Fatal("Cannot deduce sibling:", destination) } _, err := inst.MoveBelow(instanceKey, destinationKey) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), destinationKey.DisplayString())) } case cliCommand("move-equivalent"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if destinationKey == nil { log.Fatal("Cannot deduce sibling:", destination) } _, err := inst.MoveEquivalent(instanceKey, destinationKey) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), destinationKey.DisplayString())) } case cliCommand("move-gtid"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if destinationKey == nil { log.Fatal("Cannot deduce sibling:", destination) } _, err := inst.MoveBelowGTID(instanceKey, destinationKey) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), destinationKey.DisplayString())) } case cliCommand("move-slaves-gtid"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } movedSlaves, _, err, errs := inst.MoveSlavesGTID(instanceKey, destinationKey, pattern) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range movedSlaves { fmt.Println(slave.Key.DisplayString()) } } } case cliCommand("repoint"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } // destinationKey can be null, in which case the instance repoints to its existing master instance, err := inst.Repoint(instanceKey, destinationKey, inst.GTIDHintNeutral) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), instance.MasterKey.DisplayString())) } case cliCommand("repoint-slaves"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } repointedSlaves, err, errs := inst.RepointSlavesTo(instanceKey, pattern, destinationKey) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range repointedSlaves { fmt.Println(fmt.Sprintf("%s<%s", slave.Key.DisplayString(), instanceKey.DisplayString())) } } } case cliCommand("enslave-siblings"): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, _, err := inst.EnslaveSiblings(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("enslave-master"): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.EnslaveMaster(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } case cliCommand("make-co-master"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } _, err := inst.MakeCoMaster(instanceKey) if err != nil { log.Fatale(err) } fmt.Println(instanceKey.DisplayString()) } // Pseudo-GTID case cliCommand("match"), cliCommand("match-below"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } _, _, err := inst.MatchBelow(instanceKey, destinationKey, true) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), destinationKey.DisplayString())) } case cliCommand("match-up"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } instance, _, err := inst.MatchUp(instanceKey, true) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), instance.MasterKey.DisplayString())) } case cliCommand("rematch"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } instance, _, err := inst.RematchSlave(instanceKey, true) if err != nil { log.Fatale(err) } fmt.Println(fmt.Sprintf("%s<%s", instanceKey.DisplayString(), instance.MasterKey.DisplayString())) } case cliCommand("get-candidate-slave"): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } instance, _, _, _, err := inst.GetCandidateSlave(instanceKey, false) if err != nil { log.Fatale(err) } else { fmt.Println(instance.Key.DisplayString()) } } case cliCommand("match-slaves"), cliCommand("multi-match-slaves"): { // Move all slaves of "instance" beneath "destination" if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } if destinationKey == nil { log.Fatal("Cannot deduce destination:", destination) } matchedSlaves, _, err, errs := inst.MultiMatchSlaves(instanceKey, destinationKey, pattern) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range matchedSlaves { fmt.Println(slave.Key.DisplayString()) } } } case cliCommand("match-up-slaves"): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } matchedSlaves, _, err, errs := inst.MatchUpSlaves(instanceKey, pattern) if err != nil { log.Fatale(err) } else { for _, e := range errs { log.Errore(e) } for _, slave := range matchedSlaves { fmt.Println(slave.Key.DisplayString()) } } } case cliCommand("regroup-slaves"): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } lostSlaves, equalSlaves, aheadSlaves, promotedSlave, err := inst.RegroupSlaves(instanceKey, false, func(candidateSlave *inst.Instance) { fmt.Println(candidateSlave.Key.DisplayString()) }) if promotedSlave == nil { log.Fatalf("Could not regroup slaves of %+v; error: %+v", *instanceKey, err) } fmt.Println(fmt.Sprintf("%s lost: %d, trivial: %d, pseudo-gtid: %d", promotedSlave.Key.DisplayString(), len(lostSlaves), len(equalSlaves), len(aheadSlaves))) if err != nil { log.Fatale(err) } } case cliCommand("regroup-slaves-gtid"): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } lostSlaves, movedSlaves, promotedSlave, err := inst.RegroupSlavesGTID(instanceKey, false, func(candidateSlave *inst.Instance) { fmt.Println(candidateSlave.Key.DisplayString()) }) if promotedSlave == nil { log.Fatalf("Could not regroup slaves of %+v; error: %+v", *instanceKey, err) } fmt.Println(fmt.Sprintf("%s lost: %d, moved: %d", promotedSlave.Key.DisplayString(), len(lostSlaves), len(movedSlaves))) if err != nil { log.Fatale(err) } } case cliCommand("regroup-slaves-bls"): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } promotedBinlogServer, err := inst.RegroupSlavesBinlogServers(instanceKey, false, nil) if promotedBinlogServer == nil { log.Fatalf("Could not regroup binlog server slaves of %+v; error: %+v", *instanceKey, err) } fmt.Println(promotedBinlogServer.Key.DisplayString()) if err != nil { log.Fatale(err) } } // cluster case cliCommand("clusters"): { clusters, err := inst.ReadClusters() if err != nil { log.Fatale(err) } else { fmt.Println(strings.Join(clusters, "\n")) } } case cliCommand("find"): { if pattern == "" { log.Fatal("No pattern given") } instances, err := inst.FindInstances(pattern) if err != nil { log.Fatale(err) } else { for _, instance := range instances { fmt.Println(instance.Key.DisplayString()) } } } case cliCommand("topology"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } output, err := inst.ASCIITopology(instanceKey, pattern) if err != nil { log.Fatale(err) } fmt.Println(output) } case cliCommand("which-instance"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatalf("Unable to get master: unresolved instance") } instance, _, err := inst.ReadInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } fmt.Println(instance.Key.DisplayString()) } case cliCommand("which-master"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatalf("Unable to get master: unresolved instance") } instance, _, err := inst.ReadInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } fmt.Println(instance.MasterKey.DisplayString()) } case cliCommand("which-cluster"): { clusterName := getClusterName(clusterAlias, instanceKey) fmt.Println(clusterName) } case cliCommand("which-cluster-instances"): { clusterName := getClusterName(clusterAlias, instanceKey) instances, err := inst.ReadClusterInstances(clusterName) if err != nil { log.Fatale(err) } for _, clusterInstance := range instances { fmt.Println(clusterInstance.Key.DisplayString()) } } case cliCommand("which-cluster-osc-slaves"): { clusterName := getClusterName(clusterAlias, instanceKey) instances, err := inst.GetClusterOSCSlaves(clusterName) if err != nil { log.Fatale(err) } for _, clusterInstance := range instances { fmt.Println(clusterInstance.Key.DisplayString()) } } case cliCommand("which-slaves"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatalf("Unable to get slaves: unresolved instance") } slaves, err := inst.ReadSlaveInstances(instanceKey) if err != nil { log.Fatale(err) } for _, slave := range slaves { fmt.Println(slave.Key.DisplayString()) } } case cliCommand("instance-status"): { if instanceKey == nil { instanceKey = thisInstanceKey } if instanceKey == nil { log.Fatalf("Unable to get status: unresolved instance") } instance, _, err := inst.ReadInstance(instanceKey) if err != nil { log.Fatale(err) } if instance == nil { log.Fatalf("Instance not found: %+v", *instanceKey) } fmt.Println(instance.HumanReadableDescription()) } case cliCommand("get-cluster-heuristic-lag"): { clusterName := getClusterName(clusterAlias, instanceKey) lag, err := inst.GetClusterHeuristicLag(clusterName) if err != nil { log.Fatale(err) } fmt.Println(lag) } // meta case cliCommand("snapshot-topologies"): { err := inst.SnapshotTopologies() if err != nil { log.Fatale(err) } } case cliCommand("continuous"): { logic.ContinuousDiscovery() } case cliCommand("reset-hostname-resolve-cache"): { err := inst.ResetHostnameResolveCache() if err != nil { log.Fatale(err) } fmt.Println("hostname resolve cache cleared") } // Recovery & analysis case cliCommand("recover"), cliCommand("recover-lite"): { if instanceKey == nil { log.Fatal("Cannot deduce instance:", instance) } actionTaken, promotedInstance, err := logic.CheckAndRecover(instanceKey, destinationKey, true, (command == "recover-lite")) if err != nil { log.Fatale(err) } if actionTaken { fmt.Println(promotedInstance.Key.DisplayString()) } } case cliCommand("replication-analysis"): { analysis, err := inst.GetReplicationAnalysis(false) if err != nil { log.Fatale(err) } for _, entry := range analysis { fmt.Println(fmt.Sprintf("%s (cluster %s): %s", entry.AnalyzedInstanceKey.DisplayString(), entry.ClusterDetails.ClusterName, entry.Analysis)) } } // pool case cliCommand("submit-pool-instances"): { if pool == "" { log.Fatal("Please submit --pool") } err := inst.ApplyPoolInstances(pool, instance) if err != nil { log.Fatale(err) } } case cliCommand("cluster-pool-instances"): { clusterPoolInstances, err := inst.ReadAllClusterPoolInstances() if err != nil { log.Fatale(err) } for _, clusterPoolInstance := range clusterPoolInstances { fmt.Println(fmt.Sprintf("%s\t%s\t%s\t%s:%d", clusterPoolInstance.ClusterName, clusterPoolInstance.ClusterAlias, clusterPoolInstance.Pool, clusterPoolInstance.Hostname, clusterPoolInstance.Port)) } } default: log.Fatalf("Unknown command: \"%s\". Available commands (-c):\n\t%v", command, strings.Join(knownCommands, "\n\t")) } }
// standardHttp starts serving HTTP or HTTPS (api/web) requests, to be used by normal clients func standardHttp(discovery bool) { m := martini.Classic() switch strings.ToLower(config.Config.AuthenticationMethod) { case "basic": { if config.Config.HTTPAuthUser == "" { // Still allowed; may be disallowed in future versions log.Warning("AuthenticationMethod is configured as 'basic' but HTTPAuthUser undefined. Running without authentication.") } m.Use(auth.Basic(config.Config.HTTPAuthUser, config.Config.HTTPAuthPassword)) } case "multi": { if config.Config.HTTPAuthUser == "" { // Still allowed; may be disallowed in future versions log.Fatal("AuthenticationMethod is configured as 'multi' but HTTPAuthUser undefined") } m.Use(auth.BasicFunc(func(username, password string) bool { if username == "readonly" { // Will be treated as "read-only" return true } return auth.SecureCompare(username, config.Config.HTTPAuthUser) && auth.SecureCompare(password, config.Config.HTTPAuthPassword) })) } default: { // We inject a dummy User object because we have function signatures with User argument in api.go m.Map(auth.User("")) } } m.Use(gzip.All()) // Render html templates from templates directory m.Use(render.Renderer(render.Options{ Directory: "resources", Layout: "templates/layout", HTMLContentType: "text/html", })) m.Use(martini.Static("resources/public")) if config.Config.UseMutualTLS { m.Use(ssl.VerifyOUs(config.Config.SSLValidOUs)) } inst.SetMaintenanceOwner(process.ThisHostname) if discovery { log.Info("Starting Discovery") go logic.ContinuousDiscovery() } log.Info("Registering endpoints") http.API.RegisterRequests(m) http.Web.RegisterRequests(m) // Serve if config.Config.ListenSocket != "" { log.Infof("Starting HTTP listener on unix socket %v", config.Config.ListenSocket) unixListener, err := net.Listen("unix", config.Config.ListenSocket) if err != nil { log.Fatale(err) } defer unixListener.Close() if err := nethttp.Serve(unixListener, m); err != nil { log.Fatale(err) } } else if config.Config.UseSSL { log.Info("Starting HTTPS listener") tlsConfig, err := ssl.NewTLSConfig(config.Config.SSLCAFile, config.Config.UseMutualTLS) if err != nil { log.Fatale(err) } tlsConfig.InsecureSkipVerify = config.Config.SSLSkipVerify if err = ssl.AppendKeyPairWithPassword(tlsConfig, config.Config.SSLCertFile, config.Config.SSLPrivateKeyFile, sslPEMPassword); err != nil { log.Fatale(err) } if err = ssl.ListenAndServeTLS(config.Config.ListenAddress, m, tlsConfig); err != nil { log.Fatale(err) } } else { log.Infof("Starting HTTP listener on %+v", config.Config.ListenAddress) if err := nethttp.ListenAndServe(config.Config.ListenAddress, m); err != nil { log.Fatale(err) } } log.Info("Web server started") }
// main is the application's entry point. func main() { servers := flag.String("servers", "", "srv1[:port1][,srv2[:port2]...]") command := flag.String("c", "", "command, required (exists|get|ls|lsr|create|creater|set|delete|rm|deleter|rmr|getacl|setacl)") force := flag.Bool("force", false, "force operation") format := flag.String("format", "txt", "output format (txt|json)") verbose := flag.Bool("verbose", false, "verbose") debug := flag.Bool("debug", false, "debug mode (very verbose)") stack := flag.Bool("stack", false, "add stack trace upon error") authUser := flag.String("auth_usr", "", "optional, digest scheme, user") authPwd := flag.String("auth_pwd", "", "optional, digest scheme, pwd") acls := flag.String("acls", "31", "optional, csv list [1|,2|,4|,8|,16|,31]") flag.Parse() log.SetLevel(log.ERROR) if *verbose { log.SetLevel(log.INFO) } if *debug { log.SetLevel(log.DEBUG) } if *stack { log.SetPrintStackTrace(*stack) } log.Info("starting") if *servers == "" { log.Fatal("Expected comma delimited list of servers via --servers") } serversArray := strings.Split(*servers, ",") if len(serversArray) == 0 { log.Fatal("Expected comma delimited list of servers via --servers") } if len(*command) == 0 { log.Fatal("Expected command (-c) (exists|get|ls|lsr|create|creater|set|delete|rm|deleter|rmr|getacl|setacl)") } if len(flag.Args()) < 1 { log.Fatal("Expected path argument") } path := flag.Arg(0) if *command == "ls" { } else if strings.HasSuffix(path, "/") { log.Fatal("Path must not end with '/'") } rand.Seed(time.Now().UnixNano()) zk.SetServers(serversArray) if *authUser != "" && *authPwd != "" { authExp := fmt.Sprint(*authUser, ":", *authPwd) zk.SetAuth("digest", []byte(authExp)) } if *command == "creater" { *command = "create" *force = true } switch *command { case "exists": { if exists, err := zk.Exists(path); err == nil && exists { output.PrintString([]byte("true"), *format) } else { log.Fatale(err) } } case "get": { if result, err := zk.Get(path); err == nil { output.PrintString(result, *format) } else { log.Fatale(err) } } case "getacl": { if result, err := zk.GetACL(path); err == nil { output.PrintStringArray(result, *format) } else { log.Fatale(err) } } case "ls": { if result, err := zk.Children(path); err == nil { output.PrintStringArray(result, *format) } else { log.Fatale(err) } } case "lsr": { if result, err := zk.ChildrenRecursive(path); err == nil { output.PrintStringArray(result, *format) } else { log.Fatale(err) } } case "create": { var aclstr string if len(flag.Args()) < 2 { log.Fatal("Expected data argument") } if len(flag.Args()) >= 3 { aclstr = flag.Arg(2) } if *authUser != "" && *authPwd != "" { perms, err := zk.BuildACL("digest", *authUser, *authPwd, *acls) if err != nil { log.Fatale(err) } if result, err := zk.CreateWithACL(path, []byte(flag.Arg(1)), *force, perms); err == nil { log.Infof("Created %+v", result) } else { log.Fatale(err) } } else { if result, err := zk.Create(path, []byte(flag.Arg(1)), aclstr, *force); err == nil { log.Infof("Created %+v", result) } else { log.Fatale(err) } } } case "set": { var info []byte if len(flag.Args()) > 1 { info = []byte(flag.Arg(1)) } else { var err error info, err = ioutil.ReadAll(os.Stdin) if err != nil { log.Fatale(err) } } if result, err := zk.Set(path, info); err == nil { log.Infof("Set %+v", result) } else { log.Fatale(err) } } case "setacl": { var aclstr string if len(flag.Args()) > 1 { aclstr = flag.Arg(1) } else { var err error data, err := ioutil.ReadAll(os.Stdin) aclstr = string(data) if err != nil { log.Fatale(err) } } if result, err := zk.SetACL(path, aclstr, *force); err == nil { log.Infof("Set %+v", result) } else { log.Fatale(err) } } case "delete", "rm": { if err := zk.Delete(path); err != nil { log.Fatale(err) } } case "deleter", "rmr": { if !(*force) { log.Fatal("deleter (recursive) command requires --force for safety measure") } if err := zk.DeleteRecursive(path); err != nil { log.Fatale(err) } } default: log.Fatalf("Unknown command: %s", *command) } }
// ContinuousDiscovery starts an asynchronuous infinite discovery process where instances are // periodically investigated and their status captured, and long since unseen instances are // purged and forgotten. func ContinuousDiscovery() { if config.Config.DatabaselessMode__experimental { log.Fatal("Cannot execute continuous mode in databaseless mode") } log.Infof("Starting continuous discovery") recentDiscoveryOperationKeys = cache.New(time.Duration(config.Config.InstancePollSeconds)*time.Second, time.Second) inst.LoadHostnameResolveCache() go handleDiscoveryRequests() discoveryTick := time.Tick(time.Duration(config.Config.GetDiscoveryPollSeconds()) * time.Second) instancePollTick := time.Tick(time.Duration(config.Config.InstancePollSeconds) * time.Second) caretakingTick := time.Tick(time.Minute) recoveryTick := time.Tick(time.Duration(config.Config.RecoveryPollSeconds) * time.Second) var snapshotTopologiesTick <-chan time.Time if config.Config.SnapshotTopologiesIntervalHours > 0 { snapshotTopologiesTick = time.Tick(time.Duration(config.Config.SnapshotTopologiesIntervalHours) * time.Hour) } go ometrics.InitGraphiteMetrics() go acceptSignals() if *config.RuntimeCLIFlags.GrabElection { process.GrabElection() } for { select { case <-discoveryTick: go func() { wasAlreadyElected := atomic.LoadInt64(&isElectedNode) myIsElectedNode, err := process.AttemptElection() if err != nil { log.Errore(err) } if myIsElectedNode { atomic.StoreInt64(&isElectedNode, 1) } else { atomic.StoreInt64(&isElectedNode, 0) } if myIsElectedNode { instanceKeys, err := inst.ReadOutdatedInstanceKeys() if err != nil { log.Errore(err) } log.Debugf("outdated keys: %+v", instanceKeys) for _, instanceKey := range instanceKeys { instanceKey := instanceKey if instanceKey.IsValid() { discoveryQueue.Push(instanceKey) } } if wasAlreadyElected == 0 { // Just turned to be leader! go process.RegisterNode("", "", false) } } else { log.Debugf("Not elected as active node; polling") } }() case <-instancePollTick: go func() { // This tick does NOT do instance poll (these are handled by the oversmapling discoveryTick) // But rather should invoke such routinely operations that need to be as (or roughly as) frequent // as instance poll if atomic.LoadInt64(&isElectedNode) == 1 { go inst.UpdateInstanceRecentRelaylogHistory() go inst.RecordInstanceCoordinatesHistory() } }() case <-caretakingTick: // Various periodic internal maintenance tasks go func() { if atomic.LoadInt64(&isElectedNode) == 1 { go inst.RecordInstanceBinlogFileHistory() go inst.ForgetLongUnseenInstances() go inst.ForgetUnseenInstancesDifferentlyResolved() go inst.ForgetExpiredHostnameResolves() go inst.DeleteInvalidHostnameResolves() go inst.ReviewUnseenInstances() go inst.InjectUnseenMasters() go inst.ResolveUnknownMasterHostnameResolves() go inst.UpdateClusterAliases() go inst.ExpireMaintenance() go inst.ExpireDowntime() go inst.ExpireCandidateInstances() go inst.ExpireHostnameUnresolve() go inst.ExpireClusterDomainName() go inst.ExpireAudit() go inst.ExpireMasterPositionEquivalence() go inst.ExpirePoolInstances() go inst.FlushNontrivialResolveCacheToDatabase() go process.ExpireNodesHistory() go process.ExpireAccessTokens() } else { // Take this opportunity to refresh yourself go inst.LoadHostnameResolveCache() } }() case <-recoveryTick: go func() { if atomic.LoadInt64(&isElectedNode) == 1 { go ClearActiveFailureDetections() go ClearActiveRecoveries() go ExpireBlockedRecoveries() go AcknowledgeCrashedRecoveries() go inst.ExpireInstanceAnalysisChangelog() go CheckAndRecover(nil, nil, false) } }() case <-snapshotTopologiesTick: go func() { go inst.SnapshotTopologies() }() } } }
// ContinuousDiscovery starts an asynchronuous infinite discovery process where instances are // periodically investigated and their status captured, and long since unseen instances are // purged and forgotten. func ContinuousDiscovery() { if config.Config.DatabaselessMode__experimental { log.Fatal("Cannot execute continuous mode in databaseless mode") } log.Infof("Starting continuous discovery") inst.LoadHostnameResolveCacheFromDatabase() go handleDiscoveryRequests() discoveryTick := time.Tick(time.Duration(config.Config.DiscoveryPollSeconds) * time.Second) caretakingTick := time.Tick(time.Minute) recoveryTick := time.Tick(time.Duration(config.Config.RecoveryPollSeconds) * time.Second) var snapshotTopologiesTick <-chan time.Time if config.Config.SnapshotTopologiesIntervalHours > 0 { snapshotTopologiesTick = time.Tick(time.Duration(config.Config.SnapshotTopologiesIntervalHours) * time.Hour) } go ometrics.InitGraphiteMetrics() go acceptSignals() for { select { case <-discoveryTick: go func() { wasAlreadyElected := isElectedNode if isElectedNode, _ = process.AttemptElection(); isElectedNode { instanceKeys, _ := inst.ReadOutdatedInstanceKeys() log.Debugf("outdated keys: %+v", instanceKeys) for _, instanceKey := range instanceKeys { go func() { discoveryInstanceKeys <- instanceKey }() } if !wasAlreadyElected { // Just turned to be leader! go process.RegisterNode("", "", false) } } else { log.Debugf("Not elected as active node; polling") } }() case <-caretakingTick: // Various periodic internal maintenance tasks go func() { if isElectedNode { go inst.ForgetLongUnseenInstances() go inst.ForgetUnseenInstancesDifferentlyResolved() go inst.ForgetExpiredHostnameResolves() go inst.DeleteInvalidHostnameResolves() go inst.ReviewUnseenInstances() go inst.InjectUnseenMasters() go inst.ResolveUnknownMasterHostnameResolves() go inst.UpdateClusterAliases() go inst.ExpireMaintenance() go inst.ExpireDowntime() go inst.ExpireCandidateInstances() go inst.ExpireHostnameUnresolve() go inst.ExpireClusterDomainName() go inst.ExpireAudit() go inst.ExpireMasterPositionEquivalence() go inst.FlushNontrivialResolveCacheToDatabase() go process.ExpireNodesHistory() } if !isElectedNode { // Take this opportunity to refresh yourself go inst.LoadHostnameResolveCacheFromDatabase() } go inst.ReadClusterAliases() }() case <-recoveryTick: go func() { if isElectedNode { go ClearActiveFailureDetections() go ClearActiveRecoveries() go ExpireBlockedRecoveries() go AcknowledgeCrashedRecoveries() go inst.ExpireInstanceAnalysisChangelog() go CheckAndRecover(nil, nil, false) } }() case <-snapshotTopologiesTick: go func() { go inst.SnapshotTopologies() }() } } }