github.com/argoproj/argo-events@v1.9.1/eventsources/sources/mqtt/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 mqtt
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"time"
    24  
    25  	mqttlib "github.com/eclipse/paho.mqtt.golang"
    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 mqtt event source
    39  type EventListener struct {
    40  	EventSourceName string
    41  	EventName       string
    42  	MQTTEventSource v1alpha1.MQTTEventSource
    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.MQTTEvent
    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  	log.Info("starting MQTT event source...")
    68  	mqttEventSource := &el.MQTTEventSource
    69  
    70  	if mqttEventSource.JSONBody {
    71  		log.Info("assuming all events have a json body...")
    72  	}
    73  
    74  	log.Info("setting up the message handler...")
    75  	handler := func(c mqttlib.Client, msg mqttlib.Message) {
    76  		defer func(start time.Time) {
    77  			el.Metrics.EventProcessingDuration(el.GetEventSourceName(), el.GetEventName(), float64(time.Since(start)/time.Millisecond))
    78  		}(time.Now())
    79  
    80  		eventData := &events.MQTTEventData{
    81  			Topic:     msg.Topic(),
    82  			MessageID: int(msg.MessageID()),
    83  			Body:      msg.Payload(),
    84  			Metadata:  mqttEventSource.Metadata,
    85  		}
    86  		if mqttEventSource.JSONBody {
    87  			body := msg.Payload()
    88  			eventData.Body = (*json.RawMessage)(&body)
    89  		} else {
    90  			eventData.Body = msg.Payload()
    91  		}
    92  
    93  		eventBody, err := json.Marshal(eventData)
    94  		if err != nil {
    95  			log.Errorw("failed to marshal the event data, rejecting the event...", zap.Error(err))
    96  			el.Metrics.EventProcessingFailed(el.GetEventSourceName(), el.GetEventName())
    97  			return
    98  		}
    99  		log.Info("dispatching event on the data channel...")
   100  		if err = dispatch(eventBody); err != nil {
   101  			log.Errorw("failed to dispatch MQTT event...", zap.Error(err))
   102  			el.Metrics.EventProcessingFailed(el.GetEventSourceName(), el.GetEventName())
   103  		}
   104  	}
   105  
   106  	log.Info("setting up the mqtt broker client...")
   107  	opts := mqttlib.NewClientOptions().AddBroker(mqttEventSource.URL).SetClientID(mqttEventSource.ClientID)
   108  	if mqttEventSource.TLS != nil {
   109  		tlsConfig, err := common.GetTLSConfig(mqttEventSource.TLS)
   110  		if err != nil {
   111  			return fmt.Errorf("failed to get the tls configuration, %w", err)
   112  		}
   113  		opts.TLSConfig = tlsConfig
   114  	}
   115  
   116  	if mqttEventSource.Auth != nil {
   117  		username, err := common.GetSecretFromVolume(mqttEventSource.Auth.Username)
   118  		if err != nil {
   119  			return fmt.Errorf("username not found, %w", err)
   120  		}
   121  		password, err := common.GetSecretFromVolume(mqttEventSource.Auth.Password)
   122  		if err != nil {
   123  			return fmt.Errorf("password not found, %w", err)
   124  		}
   125  		opts.SetUsername(username)
   126  		opts.SetPassword(password)
   127  	}
   128  
   129  	var client mqttlib.Client
   130  
   131  	log.Info("connecting to mqtt broker...")
   132  	if err := common.DoWithRetry(mqttEventSource.ConnectionBackoff, func() error {
   133  		client = mqttlib.NewClient(opts)
   134  		if token := client.Connect(); token.Wait() && token.Error() != nil {
   135  			return token.Error()
   136  		}
   137  		return nil
   138  	}); err != nil {
   139  		return fmt.Errorf("failed to connect to the mqtt broker for event source %s, %w", el.GetEventName(), err)
   140  	}
   141  
   142  	log.Info("subscribing to the topic...")
   143  	if token := client.Subscribe(mqttEventSource.Topic, 0, handler); token.Wait() && token.Error() != nil {
   144  		return fmt.Errorf("failed to subscribe to the topic %s for event source %s, %w", mqttEventSource.Topic, el.GetEventName(), token.Error())
   145  	}
   146  
   147  	<-ctx.Done()
   148  	log.Info("event source is stopped, unsubscribing the client...")
   149  
   150  	token := client.Unsubscribe(mqttEventSource.Topic)
   151  	if token.Error() != nil {
   152  		log.Errorw("failed to unsubscribe client", zap.Error(token.Error()))
   153  	}
   154  
   155  	return nil
   156  }