github.com/argoproj/argo-events@v1.9.1/eventsources/sources/redis/start.go (about) 1 /* 2 Copyright 2020 BlackRock, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package redis 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "time" 24 25 "github.com/go-redis/redis/v8" 26 "go.uber.org/zap" 27 28 "github.com/argoproj/argo-events/common" 29 "github.com/argoproj/argo-events/common/logging" 30 eventsourcecommon "github.com/argoproj/argo-events/eventsources/common" 31 "github.com/argoproj/argo-events/eventsources/sources" 32 metrics "github.com/argoproj/argo-events/metrics" 33 apicommon "github.com/argoproj/argo-events/pkg/apis/common" 34 "github.com/argoproj/argo-events/pkg/apis/events" 35 "github.com/argoproj/argo-events/pkg/apis/eventsource/v1alpha1" 36 ) 37 38 // EventListener implements Eventing for the Redis event source 39 type EventListener struct { 40 EventSourceName string 41 EventName string 42 RedisEventSource v1alpha1.RedisEventSource 43 Metrics *metrics.Metrics 44 } 45 46 // GetEventSourceName returns name of event source 47 func (el *EventListener) GetEventSourceName() string { 48 return el.EventSourceName 49 } 50 51 // GetEventName returns name of event 52 func (el *EventListener) GetEventName() string { 53 return el.EventName 54 } 55 56 // GetEventSourceType return type of event server 57 func (el *EventListener) GetEventSourceType() apicommon.EventSourceType { 58 return apicommon.RedisEvent 59 } 60 61 // StartListening listens events published by redis 62 func (el *EventListener) StartListening(ctx context.Context, dispatch func([]byte, ...eventsourcecommon.Option) error) error { 63 log := logging.FromContext(ctx). 64 With(logging.LabelEventSourceType, el.GetEventSourceType(), logging.LabelEventName, el.GetEventName()) 65 log.Info("started processing the Redis event source...") 66 defer sources.Recover(el.GetEventName()) 67 68 redisEventSource := &el.RedisEventSource 69 70 opt := &redis.Options{ 71 Addr: redisEventSource.HostAddress, 72 DB: int(redisEventSource.DB), 73 } 74 75 log.Info("retrieving password if it has been configured...") 76 if redisEventSource.Password != nil { 77 password, err := common.GetSecretFromVolume(redisEventSource.Password) 78 if err != nil { 79 return fmt.Errorf("failed to find the secret password %s, %w", redisEventSource.Password.Name, err) 80 } 81 opt.Password = password 82 } 83 84 if redisEventSource.Username != "" { 85 opt.Username = redisEventSource.Username 86 } 87 88 if redisEventSource.TLS != nil { 89 tlsConfig, err := common.GetTLSConfig(redisEventSource.TLS) 90 if err != nil { 91 return fmt.Errorf("failed to get the tls configuration, %w", err) 92 } 93 opt.TLSConfig = tlsConfig 94 } 95 96 log.Info("setting up a redis client...") 97 client := redis.NewClient(opt) 98 99 if status := client.Ping(ctx); status.Err() != nil { 100 return fmt.Errorf("failed to connect to host %s and db %d for event source %s, %w", redisEventSource.HostAddress, redisEventSource.DB, el.GetEventName(), status.Err()) 101 } 102 103 pubsub := client.Subscribe(ctx, redisEventSource.Channels...) 104 // Wait for confirmation that subscription is created before publishing anything. 105 if _, err := pubsub.Receive(ctx); err != nil { 106 return fmt.Errorf("failed to receive the subscription confirmation for event source %s, %w", el.GetEventName(), err) 107 } 108 109 // Go channel which receives messages. 110 ch := pubsub.Channel() 111 for { 112 select { 113 case message, ok := <-ch: 114 if !ok { 115 log.Error("failed to read a message, channel might have been closed") 116 return fmt.Errorf("channel might have been closed") 117 } 118 119 if err := el.handleOne(message, dispatch, log); err != nil { 120 log.With("channel", message.Channel).Errorw("failed to process a Redis message", zap.Error(err)) 121 el.Metrics.EventProcessingFailed(el.GetEventSourceName(), el.GetEventName()) 122 } 123 case <-ctx.Done(): 124 log.Info("event source is stopped. unsubscribing the subscription") 125 if err := pubsub.Unsubscribe(ctx, redisEventSource.Channels...); err != nil { 126 log.Errorw("failed to unsubscribe", zap.Error(err)) 127 } 128 return nil 129 } 130 } 131 } 132 133 func (el *EventListener) handleOne(message *redis.Message, dispatch func([]byte, ...eventsourcecommon.Option) error, log *zap.SugaredLogger) error { 134 defer func(start time.Time) { 135 el.Metrics.EventProcessingDuration(el.GetEventSourceName(), el.GetEventName(), float64(time.Since(start)/time.Millisecond)) 136 }(time.Now()) 137 138 log.With("channel", message.Channel).Info("received a message") 139 eventData := &events.RedisEventData{ 140 Channel: message.Channel, 141 Pattern: message.Pattern, 142 Body: message.Payload, 143 Metadata: el.RedisEventSource.Metadata, 144 } 145 if el.RedisEventSource.JSONBody { 146 body := []byte(message.Payload) 147 eventData.Body = (*json.RawMessage)(&body) 148 } 149 150 eventBody, err := json.Marshal(&eventData) 151 if err != nil { 152 return fmt.Errorf("failed to marshal the event data, rejecting the event, %w", err) 153 } 154 log.With("channel", message.Channel).Info("dispatching the event on the data channel...") 155 if err = dispatch(eventBody); err != nil { 156 return fmt.Errorf("failed dispatch a Redis event, %w", err) 157 } 158 return nil 159 }