github.com/argoproj/argo-events@v1.9.1/eventsources/sources/pulsar/start.go (about) 1 /* 2 Copyright 2018 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 package pulsar 17 18 import ( 19 "context" 20 "encoding/json" 21 "fmt" 22 "time" 23 24 "github.com/apache/pulsar-client-go/pulsar" 25 "go.uber.org/zap" 26 27 "github.com/argoproj/argo-events/common" 28 "github.com/argoproj/argo-events/common/logging" 29 eventsourcecommon "github.com/argoproj/argo-events/eventsources/common" 30 "github.com/argoproj/argo-events/eventsources/sources" 31 metrics "github.com/argoproj/argo-events/metrics" 32 apicommon "github.com/argoproj/argo-events/pkg/apis/common" 33 "github.com/argoproj/argo-events/pkg/apis/events" 34 "github.com/argoproj/argo-events/pkg/apis/eventsource/v1alpha1" 35 ) 36 37 // EventListener implements Eventing for the Pulsar event source 38 type EventListener struct { 39 EventSourceName string 40 EventName string 41 PulsarEventSource v1alpha1.PulsarEventSource 42 Metrics *metrics.Metrics 43 } 44 45 // GetEventSourceName returns name of event source 46 func (el *EventListener) GetEventSourceName() string { 47 return el.EventSourceName 48 } 49 50 // GetEventName returns name of event 51 func (el *EventListener) GetEventName() string { 52 return el.EventName 53 } 54 55 // GetEventSourceType return type of event server 56 func (el *EventListener) GetEventSourceType() apicommon.EventSourceType { 57 return apicommon.PulsarEvent 58 } 59 60 // StartListening listens Pulsar events 61 func (el *EventListener) StartListening(ctx context.Context, dispatch func([]byte, ...eventsourcecommon.Option) error) error { 62 log := logging.FromContext(ctx). 63 With(logging.LabelEventSourceType, el.GetEventSourceType(), logging.LabelEventName, el.GetEventName()) 64 log.Info("started processing the Pulsar event source...") 65 defer sources.Recover(el.GetEventName()) 66 67 msgChannel := make(chan pulsar.ConsumerMessage) 68 69 pulsarEventSource := &el.PulsarEventSource 70 71 subscriptionType := pulsar.Exclusive 72 if pulsarEventSource.Type == "shared" { 73 subscriptionType = pulsar.Shared 74 } 75 76 log.Info("setting consumer options...") 77 consumerOpt := pulsar.ConsumerOptions{ 78 Topics: pulsarEventSource.Topics, 79 SubscriptionName: el.EventName, 80 Type: subscriptionType, 81 MessageChannel: msgChannel, 82 } 83 84 log.Info("setting client options...") 85 var err error 86 tlsTrustCertsFilePath := "" 87 if pulsarEventSource.TLSTrustCertsSecret != nil { 88 tlsTrustCertsFilePath, err = common.GetSecretVolumePath(pulsarEventSource.TLSTrustCertsSecret) 89 if err != nil { 90 log.Errorw("failed to get TLSTrustCertsFilePath from the volume", zap.Error(err)) 91 return err 92 } 93 } 94 clientOpt := pulsar.ClientOptions{ 95 URL: pulsarEventSource.URL, 96 TLSTrustCertsFilePath: tlsTrustCertsFilePath, 97 TLSAllowInsecureConnection: pulsarEventSource.TLSAllowInsecureConnection, 98 TLSValidateHostname: pulsarEventSource.TLSValidateHostname, 99 } 100 101 if pulsarEventSource.AuthTokenSecret != nil { 102 token, err := common.GetSecretFromVolume(pulsarEventSource.AuthTokenSecret) 103 if err != nil { 104 log.Errorw("failed to get AuthTokenSecret from the volume", zap.Error(err)) 105 return err 106 } 107 clientOpt.Authentication = pulsar.NewAuthenticationToken(token) 108 } 109 110 if len(pulsarEventSource.AuthAthenzParams) > 0 { 111 log.Info("setting athenz auth option...") 112 if pulsarEventSource.AuthAthenzSecret != nil { 113 authAthenzFilePath, err := common.GetSecretVolumePath(pulsarEventSource.AuthAthenzSecret) 114 if err != nil { 115 log.Errorw("failed to get authAthenzSecret from the volume", zap.Error(err)) 116 return err 117 } 118 pulsarEventSource.AuthAthenzParams["privateKey"] = "file://" + authAthenzFilePath 119 } 120 clientOpt.Authentication = pulsar.NewAuthenticationAthenz(pulsarEventSource.AuthAthenzParams) 121 } 122 123 if pulsarEventSource.TLS != nil { 124 log.Info("setting tls auth option...") 125 var clientCertPath, clientKeyPath string 126 switch { 127 case pulsarEventSource.TLS.ClientCertSecret != nil && pulsarEventSource.TLS.ClientKeySecret != nil: 128 clientCertPath, err = common.GetSecretVolumePath(pulsarEventSource.TLS.ClientCertSecret) 129 if err != nil { 130 log.Errorw("failed to get ClientCertPath from the volume", zap.Error(err)) 131 return err 132 } 133 clientKeyPath, err = common.GetSecretVolumePath(pulsarEventSource.TLS.ClientKeySecret) 134 if err != nil { 135 log.Errorw("failed to get ClientKeyPath from the volume", zap.Error(err)) 136 return err 137 } 138 default: 139 return fmt.Errorf("invalid TLS config") 140 } 141 clientOpt.Authentication = pulsar.NewAuthenticationTLS(clientCertPath, clientKeyPath) 142 } 143 144 var client pulsar.Client 145 146 if err := common.DoWithRetry(pulsarEventSource.ConnectionBackoff, func() error { 147 var err error 148 if client, err = pulsar.NewClient(clientOpt); err != nil { 149 return err 150 } 151 return nil 152 }); err != nil { 153 return fmt.Errorf("failed to connect to %s for event source %s, %w", pulsarEventSource.URL, el.GetEventName(), err) 154 } 155 156 log.Info("subscribing to messages on the topic...") 157 consumer, err := client.Subscribe(consumerOpt) 158 if err != nil { 159 return fmt.Errorf("failed to connect to topic %+v for event source %s, %w", pulsarEventSource.Topics, el.GetEventName(), err) 160 } 161 162 consumeMessages: 163 for { 164 select { 165 case msg, ok := <-msgChannel: 166 if !ok { 167 log.Error("failed to read a message, channel might have been closed") 168 return fmt.Errorf("channel might have been closed") 169 } 170 171 if err := el.handleOne(msg, dispatch, log); err != nil { 172 log.Errorw("failed to process a Pulsar event", zap.Error(err)) 173 el.Metrics.EventProcessingFailed(el.GetEventSourceName(), el.GetEventName()) 174 } 175 176 if err := consumer.Ack(msg.Message); err != nil { 177 return fmt.Errorf("failed to process a consumer ack, %w", err) 178 } 179 case <-ctx.Done(): 180 consumer.Close() 181 client.Close() 182 break consumeMessages 183 } 184 } 185 186 log.Info("event source is stopped") 187 return nil 188 } 189 190 func (el *EventListener) handleOne(msg pulsar.Message, dispatch func([]byte, ...eventsourcecommon.Option) error, log *zap.SugaredLogger) error { 191 defer func(start time.Time) { 192 el.Metrics.EventProcessingDuration(el.GetEventSourceName(), el.GetEventName(), float64(time.Since(start)/time.Millisecond)) 193 }(time.Now()) 194 195 log.Infof("received a message on the topic %s", msg.Topic()) 196 payload := msg.Payload() 197 eventData := &events.PulsarEventData{ 198 Key: msg.Key(), 199 PublishTime: msg.PublishTime().UTC().String(), 200 Body: payload, 201 Metadata: el.PulsarEventSource.Metadata, 202 } 203 if el.PulsarEventSource.JSONBody { 204 eventData.Body = (*json.RawMessage)(&payload) 205 } 206 207 eventBody, err := json.Marshal(eventData) 208 if err != nil { 209 return fmt.Errorf("failed to marshal the event data. rejecting the event, %w", err) 210 } 211 212 log.Infof("dispatching the message received on the topic %s to eventbus", msg.Topic()) 213 if err = dispatch(eventBody); err != nil { 214 return fmt.Errorf("failed to dispatch a Pulsar event, %w", err) 215 } 216 return nil 217 }