github.com/Axway/agent-sdk@v1.1.101/pkg/agent/events/eventlistener.go (about)

     1  package events
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/Axway/agent-sdk/pkg/agent/handler"
     8  	"github.com/Axway/agent-sdk/pkg/util/log"
     9  
    10  	apiv1 "github.com/Axway/agent-sdk/pkg/apic/apiserver/models/api/v1"
    11  	"github.com/Axway/agent-sdk/pkg/watchmanager/proto"
    12  )
    13  
    14  // Listener starts the EventListener
    15  type Listener interface {
    16  	Listen() chan error
    17  	Stop()
    18  }
    19  
    20  // APIClient -
    21  type APIClient interface {
    22  	GetResource(url string) (*apiv1.ResourceInstance, error)
    23  	CreateResourceInstance(ri apiv1.Interface) (*apiv1.ResourceInstance, error)
    24  	DeleteResourceInstance(ri apiv1.Interface) error
    25  	GetAPIV1ResourceInstances(map[string]string, string) ([]*apiv1.ResourceInstance, error)
    26  }
    27  
    28  // EventListener holds the various caches to save events into as they get written to the source channel.
    29  type EventListener struct {
    30  	cancel          context.CancelFunc
    31  	client          APIClient
    32  	ctx             context.Context
    33  	handlers        []handler.Handler
    34  	logger          log.FieldLogger
    35  	sequenceManager SequenceProvider
    36  	source          chan *proto.Event
    37  }
    38  
    39  // NewListenerFunc type for creating a new listener
    40  type NewListenerFunc func(
    41  	source chan *proto.Event, client APIClient, sequenceManager SequenceProvider, cbs ...handler.Handler,
    42  ) *EventListener
    43  
    44  // NewEventListener creates a new EventListener to process events based on the provided Handlers.
    45  func NewEventListener(
    46  	source chan *proto.Event, client APIClient, sequenceManager SequenceProvider, cbs ...handler.Handler,
    47  ) *EventListener {
    48  	ctx, cancel := context.WithCancel(context.Background())
    49  	logger := log.NewFieldLogger().
    50  		WithComponent("EventListener").
    51  		WithPackage("sdk.agent.events")
    52  
    53  	return &EventListener{
    54  		cancel:          cancel,
    55  		client:          client,
    56  		ctx:             ctx,
    57  		handlers:        cbs,
    58  		logger:          logger,
    59  		sequenceManager: sequenceManager,
    60  		source:          source,
    61  	}
    62  }
    63  
    64  // Stop stops the listener
    65  func (em *EventListener) Stop() {
    66  	if em != nil {
    67  		em.cancel()
    68  	}
    69  }
    70  
    71  // Listen starts a loop that will process events as they are sent on the channel
    72  func (em *EventListener) Listen() chan error {
    73  	errCh := make(chan error)
    74  	go func() {
    75  		for {
    76  			done, err := em.start()
    77  			if done && err == nil {
    78  				errCh <- nil
    79  				break
    80  			}
    81  
    82  			if err != nil {
    83  				errCh <- err
    84  				break
    85  			}
    86  		}
    87  	}()
    88  
    89  	return errCh
    90  }
    91  
    92  func (em *EventListener) start() (done bool, err error) {
    93  	select {
    94  	case event, ok := <-em.source:
    95  		if !ok {
    96  			done = true
    97  			err = fmt.Errorf("stream event source has been closed")
    98  			break
    99  		}
   100  
   101  		err := em.handleEvent(event)
   102  		if err != nil {
   103  			em.logger.WithError(err).Error("stream event listener error")
   104  		}
   105  	case <-em.ctx.Done():
   106  		em.logger.Trace("stream event listener has been gracefully stopped")
   107  		done = true
   108  		err = nil
   109  		break
   110  	}
   111  
   112  	return done, err
   113  }
   114  
   115  // handleEvent fetches the api server ResourceClient based on the event self link, and then tries to save it to the cache.
   116  func (em *EventListener) handleEvent(event *proto.Event) error {
   117  	ctx := handler.NewEventContext(event.Type, event.Metadata, event.Payload.Kind, event.Payload.Name)
   118  	em.logger.
   119  		WithField("sequence", event.Metadata.SequenceID).
   120  		WithField("kind", event.Payload.Kind).
   121  		WithField("name", event.Payload.Name).
   122  		WithField("type", event.Type.String()).
   123  		Debug("processing watch event")
   124  
   125  	ri, err := em.getEventResource(event)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	em.HandleResource(ctx, event.Metadata, ri)
   131  	em.sequenceManager.SetSequence(event.Metadata.SequenceID)
   132  	return nil
   133  }
   134  
   135  func (em *EventListener) getEventResource(event *proto.Event) (*apiv1.ResourceInstance, error) {
   136  	if event.Type == proto.Event_DELETED {
   137  		return em.convertEventPayload(event), nil
   138  	}
   139  	return em.client.GetResource(event.Payload.Metadata.SelfLink)
   140  }
   141  
   142  // HandleResource loops through all the handlers and passes the event to each one for processing.
   143  func (em *EventListener) HandleResource(
   144  	ctx context.Context,
   145  	eventMetadata *proto.EventMeta,
   146  	resource *apiv1.ResourceInstance,
   147  ) {
   148  	for _, h := range em.handlers {
   149  		err := h.Handle(ctx, eventMetadata, resource)
   150  		if err != nil {
   151  			em.logger.Error(err)
   152  		}
   153  	}
   154  }
   155  
   156  func (em *EventListener) convertEventPayload(event *proto.Event) *apiv1.ResourceInstance {
   157  	ri := &apiv1.ResourceInstance{
   158  		ResourceMeta: apiv1.ResourceMeta{
   159  			GroupVersionKind: apiv1.GroupVersionKind{
   160  				GroupKind: apiv1.GroupKind{
   161  					Group: event.Payload.Group,
   162  					Kind:  event.Payload.Kind,
   163  				},
   164  			},
   165  			Name: event.Payload.Name,
   166  			Metadata: apiv1.Metadata{
   167  				ID:       event.Payload.Metadata.Id,
   168  				SelfLink: event.Payload.Metadata.SelfLink,
   169  			},
   170  			Attributes: event.Payload.Attributes,
   171  		},
   172  	}
   173  	if event.Payload.Metadata.Scope != nil {
   174  		ri.Metadata.Scope = apiv1.MetadataScope{
   175  			ID:       event.Payload.Metadata.Scope.Id,
   176  			Kind:     event.Payload.Metadata.Scope.Kind,
   177  			Name:     event.Payload.Metadata.Scope.Name,
   178  			SelfLink: event.Payload.Metadata.Scope.SelfLink,
   179  		}
   180  	}
   181  	return ri
   182  }