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 }