github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/aagent/machine/notifications.go (about)

     1  // Copyright (c) 2019-2022, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package machine
     6  
     7  import (
     8  	"fmt"
     9  	"time"
    10  
    11  	"github.com/choria-io/go-choria/internal/util"
    12  
    13  	cloudevents "github.com/cloudevents/sdk-go/v2"
    14  )
    15  
    16  // TransitionNotification is a notification when a transition completes
    17  type TransitionNotification struct {
    18  	Protocol   string `json:"protocol"`
    19  	Identity   string `json:"identity"`
    20  	ID         string `json:"id"`
    21  	Version    string `json:"version"`
    22  	Timestamp  int64  `json:"timestamp"`
    23  	Machine    string `json:"machine"`
    24  	Transition string `json:"transition"`
    25  	FromState  string `json:"from_state"`
    26  	ToState    string `json:"to_state"`
    27  
    28  	Info InfoSource `json:"-"`
    29  }
    30  
    31  // String returns a string representation of the event
    32  func (t *TransitionNotification) String() string {
    33  	return fmt.Sprintf("%s %s transitioned via event %s: %s => %s", t.Identity, t.Machine, t.Transition, t.FromState, t.ToState)
    34  }
    35  
    36  // CloudEvent creates a cloud event from the transition
    37  func (t *TransitionNotification) CloudEvent() cloudevents.Event {
    38  	event := cloudevents.NewEvent("1.0")
    39  
    40  	event.SetType(t.Protocol)
    41  	event.SetSource("io.choria.machine")
    42  	event.SetSubject(t.Identity)
    43  	event.SetID(util.UniqueID())
    44  	event.SetTime(time.Unix(t.Timestamp, 0))
    45  	event.SetData(cloudevents.ApplicationJSON, t)
    46  
    47  	return event
    48  }
    49  
    50  // InfoSource provides information about a running machine
    51  type InfoSource interface {
    52  	// Identity retrieves the identity of the node hosting this machine, "unknown" when not set
    53  	Identity() string
    54  	// Version returns the version of the machine
    55  	Version() string
    56  	// Name is the name of the machine
    57  	Name() string
    58  	// State returns the current state of the machine
    59  	State() string
    60  	// InstanceID return the unique ID of the machine instance
    61  	InstanceID() string
    62  }
    63  
    64  // WatcherStateNotification is a notification about the state of a watcher
    65  type WatcherStateNotification interface {
    66  	JSON() ([]byte, error)
    67  	CloudEvent() cloudevents.Event
    68  	String() string
    69  	WatcherType() string
    70  	SenderID() string
    71  }
    72  
    73  // NotificationService receives events notifications about the state machine
    74  type NotificationService interface {
    75  	// NotifyPostTransition receives an event after a transition completed
    76  	NotifyPostTransition(t *TransitionNotification) error
    77  
    78  	// NotifyWatcherState receives the current state of a watcher either after running or periodically
    79  	NotifyWatcherState(watcher string, state WatcherStateNotification) error
    80  
    81  	// Debugf logs a message at debug level
    82  	Debugf(machine InfoSource, watcher string, format string, args ...any)
    83  
    84  	// Infof logs a message at info level
    85  	Infof(machine InfoSource, watcher string, format string, args ...any)
    86  
    87  	// Warnf logs a message at warning level
    88  	Warnf(machine InfoSource, watcher string, format string, args ...any)
    89  
    90  	// Errorf logs a message at error level
    91  	Errorf(machine InfoSource, watcher string, format string, args ...any)
    92  }
    93  
    94  // RegisterNotifier adds a new NotificationService to the list of ones to receive notifications
    95  func (m *Machine) RegisterNotifier(services ...NotificationService) {
    96  	m.notifiers = append(m.notifiers, services...)
    97  }
    98  
    99  // Debugf implements NotificationService
   100  func (m *Machine) Debugf(watcher string, format string, args ...any) {
   101  	for _, n := range m.notifiers {
   102  		n.Debugf(m, watcher, format, args...)
   103  	}
   104  }
   105  
   106  // Infof implements NotificationService
   107  func (m *Machine) Infof(watcher string, format string, args ...any) {
   108  	for _, n := range m.notifiers {
   109  		n.Infof(m, watcher, format, args...)
   110  	}
   111  }
   112  
   113  // Warnf implements NotificationService
   114  func (m *Machine) Warnf(watcher string, format string, args ...any) {
   115  	for _, n := range m.notifiers {
   116  		n.Warnf(m, watcher, format, args...)
   117  	}
   118  }
   119  
   120  // Errorf implements NotificationService
   121  func (m *Machine) Errorf(watcher string, format string, args ...any) {
   122  	for _, n := range m.notifiers {
   123  		n.Errorf(m, watcher, format, args...)
   124  	}
   125  }
   126  
   127  // NotifyWatcherState implements NotificationService
   128  func (m *Machine) NotifyWatcherState(watcher string, state any) {
   129  	notification, ok := state.(WatcherStateNotification)
   130  	if !ok {
   131  		m.Errorf(watcher, "Could not notify watcher state: state does not implement WatcherStateNotification: %#v", state)
   132  		return
   133  	}
   134  
   135  	for _, n := range m.notifiers {
   136  		err := n.NotifyWatcherState(watcher, notification)
   137  		if err != nil {
   138  			m.Errorf(watcher, "Could not notify watcher state: %s", err)
   139  		}
   140  	}
   141  }