// Invoke tests whether or not this event script should be invoked // for the given Serf event. func (s *EventFilter) Invoke(e serf.Event) bool { if s.Event == "*" { return true } if e.EventType().String() != s.Event { return false } if s.Event == "user" && s.Name != "" { userE, ok := e.(serf.UserEvent) if !ok { return false } if userE.Name != s.Name { return false } } if s.Event == "query" && s.Name != "" { query, ok := e.(*serf.Query) if !ok { return false } if query.Name != s.Name { return false } } return true }
func (c *Cluster) handleSerfEvent(e serf.Event) { switch e.EventType() { case serf.EventMemberJoin: c.logger.Debug("Handling member join event") go c.addEventMembers(e) case serf.EventMemberLeave: c.logger.Debug("Handling graceful member exit event") go c.removeEventMembers(e) case serf.EventMemberFailed: c.logger.Debug("Handling unresponsive member event") go c.updateEventMembers(e) case serf.EventMemberUpdate: c.logger.Debug("Handling member metadata update event") go c.updateEventMembers(e) case serf.EventMemberReap: c.logger.Debug("Handling forced member exit event") go c.removeEventMembers(e) default: c.logger.Warnf("Unhandled Serf event: %#v", e) } if c.config.SerfEvents != nil { c.config.SerfEvents <- e } }
// invokeEventScript will execute the given event script with the given // event. Depending on the event, the semantics of how data are passed // are a bit different. For all events, the SERF_EVENT environmental // variable is the type of the event. For user events, the SERF_USER_EVENT // environmental variable is also set, containing the name of the user // event that was fired. // // In all events, data is passed in via stdin to faciliate piping. See // the various stdin functions below for more information. func invokeEventScript(logger *log.Logger, script string, self serf.Member, event serf.Event) error { defer metrics.MeasureSince([]string{"agent", "invoke", script}, time.Now()) var output bytes.Buffer // Determine the shell invocation based on OS var shell, flag string if runtime.GOOS == windows { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } cmd := exec.Command(shell, flag, script) cmd.Args[0] = "serf-event" cmd.Env = append(os.Environ(), "SERF_EVENT="+event.EventType().String(), "SERF_SELF_NAME="+self.Name, "SERF_SELF_ROLE="+self.Tags["role"], ) cmd.Stderr = &output cmd.Stdout = &output // Add all the tags for name, val := range self.Tags { tag_env := fmt.Sprintf("SERF_TAG_%s=%s", strings.ToUpper(name), val) cmd.Env = append(cmd.Env, tag_env) } stdin, err := cmd.StdinPipe() if err != nil { return err } switch e := event.(type) { case serf.MemberEvent: go memberEventStdin(logger, stdin, &e) case serf.UserEvent: cmd.Env = append(cmd.Env, "SERF_USER_EVENT="+e.Name) cmd.Env = append(cmd.Env, fmt.Sprintf("SERF_USER_LTIME=%d", e.LTime)) go userEventStdin(logger, stdin, &e) default: return fmt.Errorf("Unknown event type: %s", event.EventType().String()) } if err := cmd.Start(); err != nil { return err } err = cmd.Wait() logger.Printf("[DEBUG] Event '%s' script output: %s", event.EventType().String(), output.String()) if err != nil { return err } return nil }
// Invoke tests whether or not this event script should be invoked // for the given Serf event. func (s *EventFilter) Invoke(e serf.Event) bool { if s.Event == "*" { return true } if e.EventType().String() != s.Event { return false } if s.UserEvent != "" { userE, ok := e.(serf.UserEvent) if !ok { return false } if userE.Name != s.UserEvent { return false } } return true }
// invokeEventScript will execute the given event script with the given // event. Depending on the event, the semantics of how data are passed // are a bit different. For all events, the SERF_EVENT environmental // variable is the type of the event. For user events, the SERF_USER_EVENT // environmental variable is also set, containing the name of the user // event that was fired. // // In all events, data is passed in via stdin to faciliate piping. See // the various stdin functions below for more information. func invokeEventScript(logger *log.Logger, script string, self serf.Member, event serf.Event) error { var output bytes.Buffer // Determine the shell invocation based on OS var shell, flag string if runtime.GOOS == windows { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } cmd := exec.Command(shell, flag, script) cmd.Args[0] = "serf-event" cmd.Env = append(cmd.Env, "SERF_EVENT="+event.EventType().String(), "SERF_SELF_NAME="+self.Name, "SERF_SELF_ROLE="+self.Role, ) cmd.Stderr = &output cmd.Stdout = &output stdin, err := cmd.StdinPipe() if err != nil { return err } switch e := event.(type) { case serf.MemberEvent: go memberEventStdin(logger, stdin, &e) case serf.UserEvent: cmd.Env = append(cmd.Env, "SERF_USER_EVENT="+e.Name) cmd.Env = append(cmd.Env, fmt.Sprintf("SERF_USER_LTIME=%d", e.LTime)) go userEventStdin(logger, stdin, &e) default: return fmt.Errorf("Unknown event type: %s", event.EventType().String()) } if err := cmd.Start(); err != nil { return err } err = cmd.Wait() logger.Printf("[DEBUG] Event '%s' script output: %s", event.EventType().String(), output.String()) if err != nil { return err } return nil }
// invokeEventScript will execute the given event script with the given // event. Depending on the event, the semantics of how data are passed // are a bit different. For all events, the SERF_EVENT environmental // variable is the type of the event. For user events, the SERF_USER_EVENT // environmental variable is also set, containing the name of the user // event that was fired. // // In all events, data is passed in via stdin to faciliate piping. See // the various stdin functions below for more information. func invokeEventScript(logger *log.Logger, script string, self serf.Member, event serf.Event) error { var output bytes.Buffer cmd := exec.Command("/bin/sh", "-c", script) cmd.Args[0] = "serf-event" cmd.Env = append(cmd.Env, "SERF_EVENT="+event.EventType().String(), "SERF_SELF_NAME="+self.Name, "SERF_SELF_ROLE="+self.Role, ) cmd.Stderr = &output cmd.Stdout = &output stdin, err := cmd.StdinPipe() if err != nil { return err } switch e := event.(type) { case serf.MemberEvent: go memberEventStdin(logger, stdin, &e) case serf.UserEvent: cmd.Env = append(cmd.Env, "SERF_USER_EVENT="+e.Name) go userEventStdin(logger, stdin, &e) default: return fmt.Errorf("Unknown event type: %s", event.EventType().String()) } if err := cmd.Start(); err != nil { return err } err = cmd.Wait() logger.Printf("[DEBUG] Event '%s' script output: %s", event.EventType().String(), output.String()) if err != nil { return err } return nil }
// invokeEventScript will execute the given event script with the given // event. Depending on the event, the semantics of how data are passed // are a bit different. For all events, the SERF_EVENT environmental // variable is the type of the event. For user events, the SERF_USER_EVENT // environmental variable is also set, containing the name of the user // event that was fired. // // In all events, data is passed in via stdin to faciliate piping. See // the various stdin functions below for more information. func invokeEventScript(logger *log.Logger, script string, self serf.Member, event serf.Event) error { defer metrics.MeasureSince([]string{"agent", "invoke", script}, time.Now()) var output bytes.Buffer // Determine the shell invocation based on OS var shell, flag string if runtime.GOOS == windows { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } cmd := exec.Command(shell, flag, script) cmd.Env = append(os.Environ(), "SERF_EVENT="+event.EventType().String(), "SERF_SELF_NAME="+self.Name, "SERF_SELF_ROLE="+self.Tags["role"], ) cmd.Stderr = &output cmd.Stdout = &output // Add all the tags for name, val := range self.Tags { tag_env := fmt.Sprintf("SERF_TAG_%s=%s", strings.ToUpper(name), val) cmd.Env = append(cmd.Env, tag_env) } stdin, err := cmd.StdinPipe() if err != nil { return err } switch e := event.(type) { case serf.MemberEvent: go memberEventStdin(logger, stdin, &e) case serf.UserEvent: cmd.Env = append(cmd.Env, "SERF_USER_EVENT="+e.Name) cmd.Env = append(cmd.Env, fmt.Sprintf("SERF_USER_LTIME=%d", e.LTime)) go streamPayload(logger, stdin, e.Payload) case *serf.Query: cmd.Env = append(cmd.Env, "SERF_QUERY_NAME="+e.Name) cmd.Env = append(cmd.Env, fmt.Sprintf("SERF_QUERY_LTIME=%d", e.LTime)) go streamPayload(logger, stdin, e.Payload) default: return fmt.Errorf("Unknown event type: %s", event.EventType().String()) } if err := cmd.Start(); err != nil { return err } err = cmd.Wait() logger.Printf("[DEBUG] agent: Event '%s' script output: %s", event.EventType().String(), output.String()) if err != nil { return err } // If this is a query and we have output, respond if query, ok := event.(*serf.Query); ok && len(output.Bytes()) > 0 { if err := query.Respond(output.Bytes()); err != nil { logger.Printf("[WARN] agent: Failed to respond to query '%s': %s", event.String(), err) } } return nil }
// invokeEventScript will execute the given event script with the given // event. Depending on the event, the semantics of how data are passed // are a bit different. For all events, the SERF_EVENT environmental // variable is the type of the event. For user events, the SERF_USER_EVENT // environmental variable is also set, containing the name of the user // event that was fired. // // In all events, data is passed in via stdin to faciliate piping. See // the various stdin functions below for more information. func invokeEventScript(logger *log.Logger, script string, self serf.Member, event serf.Event) error { defer metrics.MeasureSince([]string{"agent", "invoke", script}, time.Now()) output, _ := circbuf.NewBuffer(maxBufSize) // Determine the shell invocation based on OS var shell, flag string if runtime.GOOS == windows { shell = "cmd" flag = "/C" } else { shell = "/bin/sh" flag = "-c" } cmd := exec.Command(shell, flag, script) cmd.Env = append(os.Environ(), "SERF_EVENT="+event.EventType().String(), "SERF_SELF_NAME="+self.Name, "SERF_SELF_IP="+self.Addr.String(), "SERF_SELF_PORT="+fmt.Sprintf("%v", self.Port), "SERF_SELF_ROLE="+self.Tags["role"], ) cmd.Stderr = output cmd.Stdout = output // Add all the tags for name, val := range self.Tags { //http://stackoverflow.com/questions/2821043/allowed-characters-in-linux-environment-variable-names //(http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html for the long version) //says that env var names must be in [A-Z0-9_] and not start with [0-9]. //we only care about the first part, so convert all chars not in [A-Z0-9_] to _ sanitizedName := sanitizeTagRegexp.ReplaceAllString(strings.ToUpper(name), "_") tag_env := fmt.Sprintf("SERF_TAG_%s=%s", sanitizedName, val) cmd.Env = append(cmd.Env, tag_env) } stdin, err := cmd.StdinPipe() if err != nil { return err } switch e := event.(type) { case serf.MemberEvent: go memberEventStdin(logger, stdin, &e) case serf.UserEvent: cmd.Env = append(cmd.Env, "SERF_USER_EVENT="+e.Name) cmd.Env = append(cmd.Env, fmt.Sprintf("SERF_USER_LTIME=%d", e.LTime)) go streamPayload(logger, stdin, e.Payload) case *serf.Query: cmd.Env = append(cmd.Env, "SERF_QUERY_NAME="+e.Name) cmd.Env = append(cmd.Env, fmt.Sprintf("SERF_QUERY_LTIME=%d", e.LTime)) go streamPayload(logger, stdin, e.Payload) default: return fmt.Errorf("Unknown event type: %s", event.EventType().String()) } // Start a timer to warn about slow handlers slowTimer := time.AfterFunc(warnSlow, func() { logger.Printf("[WARN] agent: Script '%s' slow, execution exceeding %v", script, warnSlow) }) if err := cmd.Start(); err != nil { return err } // Warn if buffer is overritten if output.TotalWritten() > output.Size() { logger.Printf("[WARN] agent: Script '%s' generated %d bytes of output, truncated to %d", script, output.TotalWritten(), output.Size()) } err = cmd.Wait() slowTimer.Stop() logger.Printf("[DEBUG] agent: Event '%s' script output: %s", event.EventType().String(), output.String()) if err != nil { return err } // If this is a query and we have output, respond if query, ok := event.(*serf.Query); ok && output.TotalWritten() > 0 { if err := query.Respond(output.Bytes()); err != nil { logger.Printf("[WARN] agent: Failed to respond to query '%s': %s", event.String(), err) } } return nil }
// HandleEvent processes a generic Serf event and dispatches it to the appropriate // destination. func (s SerfEventHandler) HandleEvent(e serf.Event) { if e == nil { return } var reconcile bool switch e.EventType() { // If the event is a Join event, call NodeJoined and then reconcile event with // persistent storage. case serf.EventMemberJoin: reconcile = s.ReconcileOnJoin if s.NodeJoined != nil { s.NodeJoined.HandleMemberJoin(e.(serf.MemberEvent)) } // If the event is a Leave event, call NodeLeft and then reconcile event with // persistent storage. case serf.EventMemberLeave: reconcile = s.ReconcileOnLeave if s.NodeLeft != nil { s.NodeLeft.HandleMemberLeave(e.(serf.MemberEvent)) } // If the event is a Failed event, call NodeFailed and then reconcile event with // persistent storage. case serf.EventMemberFailed: reconcile = s.ReconcileOnFail if s.NodeFailed != nil { s.NodeFailed.HandleMemberFailure(e.(serf.MemberEvent)) } // If the event is a Reap event, reconcile event with persistent storage. case serf.EventMemberReap: reconcile = s.ReconcileOnReap if s.NodeReaped != nil { s.NodeReaped.HandleMemberReap(e.(serf.MemberEvent)) } // If the event is a user event, handle leader elections, user events and unknown events. case serf.EventUser: s.handleUserEvent(e.(serf.UserEvent)) // If the event is an Update event, call NodeUpdated case serf.EventMemberUpdate: reconcile = s.ReconcileOnUpdate if s.NodeUpdated != nil { s.NodeUpdated.HandleMemberUpdate(e.(serf.MemberEvent)) } // If the event is a query, call Query Handler case serf.EventQuery: if s.QueryHandler != nil { s.QueryHandler.HandleQueryEvent(*e.(*serf.Query)) } default: s.Logger.Warn("unhandled Serf Event: %#v", e) return } // Reconcile event with external storage if reconcile && s.Reconciler != nil { s.reconcile(e.(serf.MemberEvent)) } }