github.com/argoproj/argo-events@v1.9.1/eventsources/sources/nats/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  
    17  package nats
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"time"
    24  
    25  	natslib "github.com/nats-io/nats.go"
    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 nats event source
    39  type EventListener struct {
    40  	EventSourceName string
    41  	EventName       string
    42  	NATSEventSource v1alpha1.NATSEventsSource
    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.NATSEvent
    59  }
    60  
    61  // StartListening starts listening events
    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  	defer sources.Recover(el.GetEventName())
    66  
    67  	natsEventSource := &el.NATSEventSource
    68  
    69  	var opt []natslib.Option
    70  	if natsEventSource.TLS != nil {
    71  		tlsConfig, err := common.GetTLSConfig(natsEventSource.TLS)
    72  		if err != nil {
    73  			return fmt.Errorf("failed to get the tls configuration, %w", err)
    74  		}
    75  		opt = append(opt, natslib.Secure(tlsConfig))
    76  	}
    77  
    78  	if natsEventSource.Auth != nil {
    79  		switch {
    80  		case natsEventSource.Auth.Basic != nil:
    81  			username, err := common.GetSecretFromVolume(natsEventSource.Auth.Basic.Username)
    82  			if err != nil {
    83  				return err
    84  			}
    85  			password, err := common.GetSecretFromVolume(natsEventSource.Auth.Basic.Password)
    86  			if err != nil {
    87  				return err
    88  			}
    89  			opt = append(opt, natslib.UserInfo(username, password))
    90  		case natsEventSource.Auth.Token != nil:
    91  			token, err := common.GetSecretFromVolume(natsEventSource.Auth.Token)
    92  			if err != nil {
    93  				return err
    94  			}
    95  			opt = append(opt, natslib.Token(token))
    96  		case natsEventSource.Auth.NKey != nil:
    97  			nkeyFile, err := common.GetSecretVolumePath(natsEventSource.Auth.NKey)
    98  			if err != nil {
    99  				return err
   100  			}
   101  			o, err := natslib.NkeyOptionFromSeed(nkeyFile)
   102  			if err != nil {
   103  				return fmt.Errorf("failed to get NKey, %w", err)
   104  			}
   105  			opt = append(opt, o)
   106  		case natsEventSource.Auth.Credential != nil:
   107  			cFile, err := common.GetSecretVolumePath(natsEventSource.Auth.Credential)
   108  			if err != nil {
   109  				return err
   110  			}
   111  			opt = append(opt, natslib.UserCredentials(cFile))
   112  		}
   113  	}
   114  
   115  	var conn *natslib.Conn
   116  	log.Info("connecting to nats cluster...")
   117  	if err := common.DoWithRetry(natsEventSource.ConnectionBackoff, func() error {
   118  		var err error
   119  		if conn, err = natslib.Connect(natsEventSource.URL, opt...); err != nil {
   120  			return err
   121  		}
   122  		return nil
   123  	}); err != nil {
   124  		return fmt.Errorf("failed to connect to the nats server for event source %s, %w", el.GetEventName(), err)
   125  	}
   126  	defer conn.Close()
   127  
   128  	if natsEventSource.JSONBody {
   129  		log.Info("assuming all events have a json body...")
   130  	}
   131  
   132  	log.Info("subscribing to messages on the queue...")
   133  	_, err := conn.Subscribe(natsEventSource.Subject, func(msg *natslib.Msg) {
   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  		eventData := &events.NATSEventData{
   139  			Subject:  msg.Subject,
   140  			Header:   msg.Header,
   141  			Metadata: natsEventSource.Metadata,
   142  		}
   143  		if natsEventSource.JSONBody {
   144  			eventData.Body = (*json.RawMessage)(&msg.Data)
   145  		} else {
   146  			eventData.Body = msg.Data
   147  		}
   148  
   149  		eventBody, err := json.Marshal(eventData)
   150  		if err != nil {
   151  			log.Errorw("failed to marshal the event data, rejecting the event...", zap.Error(err))
   152  			el.Metrics.EventProcessingFailed(el.GetEventSourceName(), el.GetEventName())
   153  			return
   154  		}
   155  		log.Info("dispatching the event on data channel...")
   156  		if err = dispatch(eventBody); err != nil {
   157  			log.Errorw("failed to dispatch a NATS event", zap.Error(err))
   158  			el.Metrics.EventProcessingFailed(el.GetEventSourceName(), el.GetEventName())
   159  		}
   160  	})
   161  	if err != nil {
   162  		return fmt.Errorf("failed to subscribe to the subject %s for event source %s, %w", natsEventSource.Subject, el.GetEventName(), err)
   163  	}
   164  
   165  	conn.Flush()
   166  	if err := conn.LastError(); err != nil {
   167  		return fmt.Errorf("connection failure for event source %s, %w", el.GetEventName(), err)
   168  	}
   169  
   170  	<-ctx.Done()
   171  	log.Info("event source is stopped")
   172  	return nil
   173  }