github.com/argoproj/argo-events@v1.9.1/eventsources/sources/azurequeuestorage/start.go (about) 1 package azurequeuestorage 2 3 import ( 4 "context" 5 "encoding/base64" 6 "encoding/json" 7 "fmt" 8 "time" 9 10 "github.com/Azure/azure-sdk-for-go/sdk/azidentity" 11 "github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue" 12 "go.uber.org/zap" 13 14 "github.com/argoproj/argo-events/common" 15 "github.com/argoproj/argo-events/common/logging" 16 eventsourcecommon "github.com/argoproj/argo-events/eventsources/common" 17 "github.com/argoproj/argo-events/eventsources/sources" 18 metrics "github.com/argoproj/argo-events/metrics" 19 apicommon "github.com/argoproj/argo-events/pkg/apis/common" 20 "github.com/argoproj/argo-events/pkg/apis/events" 21 "github.com/argoproj/argo-events/pkg/apis/eventsource/v1alpha1" 22 ) 23 24 // EventListener implements Eventing for azure events hub event source 25 type EventListener struct { 26 EventSourceName string 27 EventName string 28 AzureQueueStorageEventSource v1alpha1.AzureQueueStorageEventSource 29 Metrics *metrics.Metrics 30 } 31 32 // GetEventSourceName returns name of event source 33 func (el *EventListener) GetEventSourceName() string { 34 return el.EventSourceName 35 } 36 37 // GetEventName returns name of event 38 func (el *EventListener) GetEventName() string { 39 return el.EventName 40 } 41 42 // GetEventSourceType return type of event server 43 func (el *EventListener) GetEventSourceType() apicommon.EventSourceType { 44 return apicommon.AzureQueueStorage 45 } 46 47 // StartListening starts listening events 48 func (el *EventListener) StartListening(ctx context.Context, dispatch func([]byte, ...eventsourcecommon.Option) error) error { 49 log := logging.FromContext(ctx). 50 With(logging.LabelEventSourceType, el.GetEventSourceType(), logging.LabelEventName, el.GetEventName()) 51 52 log.Info("started processing the Azure Queue Storage event source...") 53 defer sources.Recover(el.GetEventName()) 54 55 queueStorageEventSource := &el.AzureQueueStorageEventSource 56 var client *azqueue.ServiceClient 57 // if connectionString is set then use it 58 // otherwise try to connect via Azure Active Directory (AAD) with storageAccountName 59 if queueStorageEventSource.ConnectionString != nil { 60 connStr, err := common.GetSecretFromVolume(queueStorageEventSource.ConnectionString) 61 if err != nil { 62 log.With("connection-string", queueStorageEventSource.ConnectionString.Name).Errorw("failed to retrieve connection string from secret", zap.Error(err)) 63 return err 64 } 65 66 log.Info("connecting to azure queue storage with connection string...") 67 client, err = azqueue.NewServiceClientFromConnectionString(connStr, nil) 68 if err != nil { 69 log.Errorw("failed to create a service client", zap.Error(err)) 70 return err 71 } 72 } else { 73 cred, err := azidentity.NewDefaultAzureCredential(nil) 74 if err != nil { 75 log.Errorw("failed to create DefaultAzureCredential", zap.Error(err)) 76 return err 77 } 78 log.Info("connecting to azure queue storage with AAD credentials...") 79 serviceURL := fmt.Sprintf("https://%s.queue.core.windows.net/", queueStorageEventSource.StorageAccountName) 80 client, err = azqueue.NewServiceClient(serviceURL, cred, nil) 81 if err != nil { 82 log.Errorw("failed to create a service client", zap.Error(err)) 83 return err 84 } 85 } 86 87 queueClient := client.NewQueueClient(el.AzureQueueStorageEventSource.QueueName) 88 if queueStorageEventSource.JSONBody { 89 log.Info("assuming all events have a json body...") 90 } 91 var numMessages int32 = 10 92 var visibilityTimeout int32 = 120 93 var waitTime int32 = 3 // Defaults to 3 seconds 94 if el.AzureQueueStorageEventSource.WaitTimeInSeconds != nil { 95 waitTime = *el.AzureQueueStorageEventSource.WaitTimeInSeconds 96 } 97 log.Info("listening for messages on the queue...") 98 for { 99 select { 100 case <-ctx.Done(): 101 log.Info("exiting AQS event listener...") 102 return nil 103 default: 104 } 105 log.Info("dequeing messages....") 106 messages, err := queueClient.DequeueMessages(ctx, &azqueue.DequeueMessagesOptions{ 107 NumberOfMessages: &numMessages, 108 VisibilityTimeout: &visibilityTimeout, 109 }) 110 if err != nil { 111 log.Errorw("failed to get messages from AQS", zap.Error(err)) 112 time.Sleep(time.Second) 113 continue 114 } 115 for _, m := range messages.Messages { 116 el.processMessage(m, dispatch, func() { 117 _, err = queueClient.DeleteMessage(ctx, *m.MessageID, *m.PopReceipt, &azqueue.DeleteMessageOptions{}) 118 if err != nil { 119 log.Errorw("Failed to delete message", zap.Error(err)) 120 } 121 }, log) 122 } 123 if len(messages.Messages) == 0 { 124 time.Sleep(time.Second * time.Duration(waitTime)) 125 } 126 } 127 } 128 129 func (el *EventListener) processMessage(message *azqueue.DequeuedMessage, dispatch func([]byte, ...eventsourcecommon.Option) error, ack func(), log *zap.SugaredLogger) { 130 defer func(start time.Time) { 131 el.Metrics.EventProcessingDuration(el.GetEventSourceName(), el.GetEventName(), float64(time.Since(start)/time.Millisecond)) 132 }(time.Now()) 133 data := &events.AzureQueueStorageEventData{ 134 MessageID: *message.MessageID, 135 InsertionTime: *message.InsertionTime, 136 Metadata: el.AzureQueueStorageEventSource.Metadata, 137 } 138 body := []byte(*message.MessageText) 139 if el.AzureQueueStorageEventSource.DecodeMessage { 140 rawDecodedText, err := base64.RawURLEncoding.DecodeString(*message.MessageText) 141 if err != nil { 142 log.Errorw("failed to base64 decode message...", zap.Error(err)) 143 el.Metrics.EventProcessingFailed(el.GetEventSourceName(), el.GetEventName()) 144 if !el.AzureQueueStorageEventSource.DLQ { 145 ack() 146 } 147 return 148 } 149 body = rawDecodedText 150 } 151 if el.AzureQueueStorageEventSource.JSONBody { 152 data.Body = (*json.RawMessage)(&body) 153 } else { 154 data.Body = body 155 } 156 eventBytes, err := json.Marshal(data) 157 if err != nil { 158 log.Errorw("failed to marshal event data, will process next message...", zap.Error(err)) 159 el.Metrics.EventProcessingFailed(el.GetEventSourceName(), el.GetEventName()) 160 // Don't ack if a DLQ is configured to allow to forward the message to the DLQ 161 if !el.AzureQueueStorageEventSource.DLQ { 162 ack() 163 } 164 return 165 } 166 if err = dispatch(eventBytes); err != nil { 167 log.Errorw("failed to dispatch azure queue storage event", zap.Error(err)) 168 el.Metrics.EventProcessingFailed(el.GetEventSourceName(), el.GetEventName()) 169 } else { 170 ack() 171 } 172 }