github.com/argoproj/argo-events@v1.9.1/sensors/triggers/pulsar/pulsar.go (about) 1 /* 2 Copyright 2021 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 23 "github.com/apache/pulsar-client-go/pulsar" 24 "go.uber.org/zap" 25 26 "github.com/argoproj/argo-events/common" 27 "github.com/argoproj/argo-events/common/logging" 28 apicommon "github.com/argoproj/argo-events/pkg/apis/common" 29 "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1" 30 "github.com/argoproj/argo-events/sensors/triggers" 31 ) 32 33 // PulsarTrigger describes the trigger to place messages on Pulsar topic using a producer 34 type PulsarTrigger struct { 35 // Sensor object 36 Sensor *v1alpha1.Sensor 37 // Trigger reference 38 Trigger *v1alpha1.Trigger 39 // Pulsar async producer 40 Producer pulsar.Producer 41 // Logger to log stuff 42 Logger *zap.SugaredLogger 43 } 44 45 // NewPulsarTrigger returns a new Pulsar trigger context. 46 func NewPulsarTrigger(sensor *v1alpha1.Sensor, trigger *v1alpha1.Trigger, pulsarProducers common.StringKeyedMap[pulsar.Producer], logger *zap.SugaredLogger) (*PulsarTrigger, error) { 47 pulsarTrigger := trigger.Template.Pulsar 48 49 producer, ok := pulsarProducers.Load(trigger.Template.Name) 50 if !ok { 51 var err error 52 tlsTrustCertsFilePath := "" 53 if pulsarTrigger.TLSTrustCertsSecret != nil { 54 tlsTrustCertsFilePath, err = common.GetSecretVolumePath(pulsarTrigger.TLSTrustCertsSecret) 55 if err != nil { 56 logger.Errorw("failed to get TLSTrustCertsFilePath from the volume", zap.Error(err)) 57 return nil, err 58 } 59 } 60 clientOpt := pulsar.ClientOptions{ 61 URL: pulsarTrigger.URL, 62 TLSTrustCertsFilePath: tlsTrustCertsFilePath, 63 TLSAllowInsecureConnection: pulsarTrigger.TLSAllowInsecureConnection, 64 TLSValidateHostname: pulsarTrigger.TLSValidateHostname, 65 } 66 67 if pulsarTrigger.AuthTokenSecret != nil { 68 token, err := common.GetSecretFromVolume(pulsarTrigger.AuthTokenSecret) 69 if err != nil { 70 logger.Errorw("failed to get AuthTokenSecret from the volume", zap.Error(err)) 71 return nil, err 72 } 73 clientOpt.Authentication = pulsar.NewAuthenticationToken(token) 74 } 75 76 if len(pulsarTrigger.AuthAthenzParams) > 0 { 77 logger.Info("setting athenz auth option...") 78 if pulsarTrigger.AuthAthenzSecret != nil { 79 authAthenzFilePath, err := common.GetSecretVolumePath(pulsarTrigger.AuthAthenzSecret) 80 if err != nil { 81 logger.Errorw("failed to get authAthenzSecret from the volume", zap.Error(err)) 82 return nil, err 83 } 84 pulsarTrigger.AuthAthenzParams["privateKey"] = "file://" + authAthenzFilePath 85 } 86 clientOpt.Authentication = pulsar.NewAuthenticationAthenz(pulsarTrigger.AuthAthenzParams) 87 } 88 89 if pulsarTrigger.TLS != nil { 90 logger.Info("setting tls auth option...") 91 var clientCertPath, clientKeyPath string 92 switch { 93 case pulsarTrigger.TLS.ClientCertSecret != nil && pulsarTrigger.TLS.ClientKeySecret != nil: 94 clientCertPath, err = common.GetSecretVolumePath(pulsarTrigger.TLS.ClientCertSecret) 95 if err != nil { 96 logger.Errorw("failed to get ClientCertPath from the volume", zap.Error(err)) 97 return nil, err 98 } 99 clientKeyPath, err = common.GetSecretVolumePath(pulsarTrigger.TLS.ClientKeySecret) 100 if err != nil { 101 logger.Errorw("failed to get ClientKeyPath from the volume", zap.Error(err)) 102 return nil, err 103 } 104 default: 105 return nil, fmt.Errorf("invalid TLS config") 106 } 107 clientOpt.Authentication = pulsar.NewAuthenticationTLS(clientCertPath, clientKeyPath) 108 } 109 110 var client pulsar.Client 111 112 if err := common.DoWithRetry(pulsarTrigger.ConnectionBackoff, func() error { 113 var err error 114 if client, err = pulsar.NewClient(clientOpt); err != nil { 115 return err 116 } 117 return nil 118 }); err != nil { 119 return nil, fmt.Errorf("failed to connect to %s for sensor %s, %w", pulsarTrigger.URL, trigger.Template.Name, err) 120 } 121 122 producer, err = client.CreateProducer(pulsar.ProducerOptions{ 123 Topic: pulsarTrigger.Topic, 124 }) 125 if err != nil { 126 return nil, err 127 } 128 129 pulsarProducers.Store(trigger.Template.Name, producer) 130 } 131 132 return &PulsarTrigger{ 133 Sensor: sensor, 134 Trigger: trigger, 135 Producer: producer, 136 Logger: logger.With(logging.LabelTriggerType, apicommon.PulsarTrigger), 137 }, nil 138 } 139 140 // GetTriggerType returns the type of the trigger 141 func (t *PulsarTrigger) GetTriggerType() apicommon.TriggerType { 142 return apicommon.PulsarTrigger 143 } 144 145 // FetchResource fetches the trigger. As the Pulsar trigger is simply a Pulsar producer, there 146 // is no need to fetch any resource from external source 147 func (t *PulsarTrigger) FetchResource(ctx context.Context) (interface{}, error) { 148 return t.Trigger.Template.Pulsar, nil 149 } 150 151 // ApplyResourceParameters applies parameters to the trigger resource 152 func (t *PulsarTrigger) ApplyResourceParameters(events map[string]*v1alpha1.Event, resource interface{}) (interface{}, error) { 153 fetchedResource, ok := resource.(*v1alpha1.PulsarTrigger) 154 if !ok { 155 return nil, fmt.Errorf("failed to interpret the fetched trigger resource") 156 } 157 158 resourceBytes, err := json.Marshal(fetchedResource) 159 if err != nil { 160 return nil, fmt.Errorf("failed to marshal the pulsar trigger resource, %w", err) 161 } 162 163 parameters := fetchedResource.Parameters 164 if parameters != nil { 165 updatedResourceBytes, err := triggers.ApplyParams(resourceBytes, parameters, events) 166 if err != nil { 167 return nil, err 168 } 169 var ht *v1alpha1.PulsarTrigger 170 if err := json.Unmarshal(updatedResourceBytes, &ht); err != nil { 171 return nil, fmt.Errorf("failed to unmarshal the updated pulsar trigger resource after applying resource parameters, %w", err) 172 } 173 return ht, nil 174 } 175 return resource, nil 176 } 177 178 // Execute executes the trigger 179 func (t *PulsarTrigger) Execute(ctx context.Context, events map[string]*v1alpha1.Event, resource interface{}) (interface{}, error) { 180 trigger, ok := resource.(*v1alpha1.PulsarTrigger) 181 if !ok { 182 return nil, fmt.Errorf("failed to interpret the trigger resource") 183 } 184 185 if trigger.Payload == nil { 186 return nil, fmt.Errorf("payload parameters are not specified") 187 } 188 189 payload, err := triggers.ConstructPayload(events, trigger.Payload) 190 if err != nil { 191 return nil, err 192 } 193 194 _, err = t.Producer.Send(ctx, &pulsar.ProducerMessage{ 195 Payload: payload, 196 }) 197 if err != nil { 198 return nil, fmt.Errorf("failed to send message to pulsar, %w", err) 199 } 200 201 t.Logger.Infow("successfully produced a message", zap.Any("topic", trigger.Topic)) 202 203 return nil, nil 204 } 205 206 // ApplyPolicy applies policy on the trigger 207 func (t *PulsarTrigger) ApplyPolicy(ctx context.Context, resource interface{}) error { 208 return nil 209 }