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  }