2019-04-21 00:38:12 +00:00
|
|
|
package server
|
|
|
|
|
2019-12-01 01:08:11 +00:00
|
|
|
import (
|
2020-04-07 04:03:50 +00:00
|
|
|
"encoding/json"
|
2020-04-07 04:22:43 +00:00
|
|
|
"strings"
|
2020-01-18 22:04:26 +00:00
|
|
|
"sync"
|
2019-12-01 01:08:11 +00:00
|
|
|
)
|
|
|
|
|
2019-04-21 00:38:12 +00:00
|
|
|
// Defines all of the possible output events for a server.
|
2019-05-28 01:03:37 +00:00
|
|
|
// noinspection GoNameStartsWithPackageName
|
2019-04-21 00:38:12 +00:00
|
|
|
const (
|
2020-07-30 04:39:27 +00:00
|
|
|
DaemonMessageEvent = "daemon message"
|
|
|
|
InstallOutputEvent = "install output"
|
|
|
|
InstallStartedEvent = "install started"
|
|
|
|
InstallCompletedEvent = "install completed"
|
|
|
|
ConsoleOutputEvent = "console output"
|
|
|
|
StatusEvent = "status"
|
|
|
|
StatsEvent = "stats"
|
|
|
|
BackupCompletedEvent = "backup completed"
|
2019-04-21 00:38:12 +00:00
|
|
|
)
|
|
|
|
|
2020-01-18 22:04:26 +00:00
|
|
|
type Event struct {
|
|
|
|
Data string
|
|
|
|
Topic string
|
|
|
|
}
|
2019-04-21 00:38:12 +00:00
|
|
|
|
2020-01-18 22:04:26 +00:00
|
|
|
type EventBus struct {
|
2020-05-04 04:30:16 +00:00
|
|
|
sync.RWMutex
|
|
|
|
|
2020-01-18 22:04:26 +00:00
|
|
|
subscribers map[string][]chan Event
|
2019-04-21 00:38:12 +00:00
|
|
|
}
|
|
|
|
|
2020-01-18 22:04:26 +00:00
|
|
|
// Returns the server's emitter instance.
|
|
|
|
func (s *Server) Events() *EventBus {
|
2020-07-30 04:56:22 +00:00
|
|
|
s.emitterLock.Lock()
|
|
|
|
defer s.emitterLock.Unlock()
|
|
|
|
|
2020-01-18 22:04:26 +00:00
|
|
|
if s.emitter == nil {
|
|
|
|
s.emitter = &EventBus{
|
|
|
|
subscribers: map[string][]chan Event{},
|
2019-04-21 00:38:12 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-18 22:04:26 +00:00
|
|
|
|
|
|
|
return s.emitter
|
2019-04-21 00:38:12 +00:00
|
|
|
}
|
|
|
|
|
2020-01-18 22:04:26 +00:00
|
|
|
// Publish data to a given topic.
|
|
|
|
func (e *EventBus) Publish(topic string, data string) {
|
2020-05-04 04:30:16 +00:00
|
|
|
e.RLock()
|
|
|
|
defer e.RUnlock()
|
2020-01-18 22:04:26 +00:00
|
|
|
|
2020-04-07 04:22:43 +00:00
|
|
|
t := topic
|
|
|
|
// Some of our topics for the socket support passing a more specific namespace,
|
|
|
|
// such as "backup completed:1234" to indicate which specific backup was completed.
|
|
|
|
//
|
|
|
|
// In these cases, we still need to the send the event using the standard listener
|
|
|
|
// name of "backup completed".
|
|
|
|
if strings.Contains(topic, ":") {
|
|
|
|
parts := strings.SplitN(topic, ":", 2)
|
|
|
|
|
|
|
|
if len(parts) == 2 {
|
|
|
|
t = parts[0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ch, ok := e.subscribers[t]; ok {
|
2020-01-18 22:04:26 +00:00
|
|
|
go func(data Event, cs []chan Event) {
|
|
|
|
for _, channel := range cs {
|
|
|
|
channel <- data
|
|
|
|
}
|
|
|
|
}(Event{Data: data, Topic: topic}, ch)
|
2019-04-21 00:38:12 +00:00
|
|
|
}
|
2019-12-01 01:08:11 +00:00
|
|
|
}
|
|
|
|
|
2020-04-07 04:03:50 +00:00
|
|
|
func (e *EventBus) PublishJson(topic string, data interface{}) error {
|
|
|
|
b, err := json.Marshal(data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
e.Publish(topic, string(b))
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-18 22:04:26 +00:00
|
|
|
// Subscribe to an emitter topic using a channel.
|
|
|
|
func (e *EventBus) Subscribe(topic string, ch chan Event) {
|
2020-05-04 04:30:16 +00:00
|
|
|
e.Lock()
|
|
|
|
defer e.Unlock()
|
2020-01-18 22:04:26 +00:00
|
|
|
|
2020-07-30 04:56:22 +00:00
|
|
|
p, ok := e.subscribers[topic]
|
|
|
|
|
|
|
|
// If there is nothing currently subscribed to this topic just go ahead and create
|
|
|
|
// the item and then return.
|
|
|
|
if !ok {
|
2020-01-18 22:04:26 +00:00
|
|
|
e.subscribers[topic] = append([]chan Event{}, ch)
|
2020-07-30 04:56:22 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this topic is already setup, first iterate over the event channels currently in there
|
|
|
|
// and confirm there is not a match. If there _is_ a match do nothing since that means this
|
|
|
|
// channel is already being tracked. This avoids registering two identical handlers for the
|
|
|
|
// same topic, and means the Unsubscribe function can safely assume there will only be a
|
|
|
|
// single match for an event.
|
|
|
|
for i := range e.subscribers[topic] {
|
|
|
|
if ch == e.subscribers[topic][i] {
|
|
|
|
return
|
|
|
|
}
|
2020-01-18 22:04:26 +00:00
|
|
|
}
|
2020-07-30 04:56:22 +00:00
|
|
|
|
|
|
|
e.subscribers[topic] = append(p, ch)
|
2019-12-01 01:08:11 +00:00
|
|
|
}
|
2020-01-18 22:04:26 +00:00
|
|
|
|
|
|
|
// Unsubscribe a channel from a topic.
|
|
|
|
func (e *EventBus) Unsubscribe(topic string, ch chan Event) {
|
2020-05-04 04:30:16 +00:00
|
|
|
e.Lock()
|
|
|
|
defer e.Unlock()
|
2020-01-18 22:04:26 +00:00
|
|
|
|
|
|
|
if _, ok := e.subscribers[topic]; ok {
|
|
|
|
for i := range e.subscribers[topic] {
|
|
|
|
if ch == e.subscribers[topic][i] {
|
|
|
|
e.subscribers[topic] = append(e.subscribers[topic][:i], e.subscribers[topic][i+1:]...)
|
2020-07-30 04:56:22 +00:00
|
|
|
// Subscribe enforces a unique event channel for the topic, so we can safely exit
|
|
|
|
// this loop once matched since there should not be any additional matches after
|
|
|
|
// this point.
|
|
|
|
break
|
2020-01-18 22:04:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-07 04:03:50 +00:00
|
|
|
}
|
2020-05-04 04:30:16 +00:00
|
|
|
|
|
|
|
// Removes all of the event listeners for the server. This is used when a server
|
|
|
|
// is being deleted to avoid a bunch of de-reference errors cropping up. Obviously
|
|
|
|
// should also check elsewhere and handle a server reference going nil, but this
|
|
|
|
// won't hurt.
|
|
|
|
func (e *EventBus) UnsubscribeAll() {
|
|
|
|
e.Lock()
|
|
|
|
defer e.Unlock()
|
|
|
|
|
|
|
|
// Loop over all of the subscribers and just remove all of the events
|
|
|
|
// for them.
|
|
|
|
for t := range e.subscribers {
|
|
|
|
e.subscribers[t] = make([]chan Event, 0)
|
|
|
|
}
|
|
|
|
}
|