github.com/Jeffail/benthos/v3@v3.65.0/lib/input/azure_queue_storage.go (about)

     1  //go:build !wasm
     2  // +build !wasm
     3  
     4  package input
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strconv"
    10  	"time"
    11  
    12  	"github.com/Jeffail/benthos/v3/internal/bloblang/field"
    13  	"github.com/Jeffail/benthos/v3/internal/interop"
    14  
    15  	"github.com/Azure/azure-storage-queue-go/azqueue"
    16  	"github.com/Jeffail/benthos/v3/internal/impl/azure"
    17  	"github.com/Jeffail/benthos/v3/lib/input/reader"
    18  	"github.com/Jeffail/benthos/v3/lib/log"
    19  	"github.com/Jeffail/benthos/v3/lib/message"
    20  	"github.com/Jeffail/benthos/v3/lib/metrics"
    21  	"github.com/Jeffail/benthos/v3/lib/types"
    22  )
    23  
    24  // AzureQueueStorage is a benthos reader.Type implementation that reads messages
    25  // from an Azure Queue Storage container.
    26  type azureQueueStorage struct {
    27  	conf AzureQueueStorageConfig
    28  
    29  	queueName                *field.Expression
    30  	serviceURL               *azqueue.ServiceURL
    31  	dequeueVisibilityTimeout time.Duration
    32  
    33  	log   log.Modular
    34  	stats metrics.Type
    35  }
    36  
    37  // newAzureQueueStorage creates a new Azure Storage Queue input type.
    38  func newAzureQueueStorage(conf AzureQueueStorageConfig, mgr types.Manager, log log.Modular, stats metrics.Type) (*azureQueueStorage, error) {
    39  	serviceURL, err := azure.GetQueueServiceURL(conf.StorageAccount, conf.StorageAccessKey, conf.StorageConnectionString)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  
    44  	a := &azureQueueStorage{
    45  		conf:       conf,
    46  		log:        log,
    47  		stats:      stats,
    48  		serviceURL: serviceURL,
    49  	}
    50  
    51  	if a.queueName, err = interop.NewBloblangField(mgr, conf.QueueName); err != nil {
    52  		return nil, fmt.Errorf("failed to parse queue name expression: %v", err)
    53  	}
    54  
    55  	if len(conf.DequeueVisibilityTimeout) > 0 {
    56  		var err error
    57  		if a.dequeueVisibilityTimeout, err = time.ParseDuration(conf.DequeueVisibilityTimeout); err != nil {
    58  			return nil, fmt.Errorf("unable to parse dequeue visibility timeout duration string: %w", err)
    59  		}
    60  	}
    61  
    62  	return a, nil
    63  }
    64  
    65  // ConnectWithContext attempts to establish a connection
    66  func (a *azureQueueStorage) ConnectWithContext(ctx context.Context) error {
    67  	return nil
    68  }
    69  
    70  // ReadWithContext attempts to read a new message from the target Azure Storage Queue Storage container.
    71  func (a *azureQueueStorage) ReadWithContext(ctx context.Context) (msg types.Message, ackFn reader.AsyncAckFn, err error) {
    72  	queueName := a.queueName.String(0, msg)
    73  	queueURL := a.serviceURL.NewQueueURL(queueName)
    74  	messageURL := queueURL.NewMessagesURL()
    75  	var approxMsgCount int32
    76  	if a.conf.TrackProperties {
    77  		if props, err := queueURL.GetProperties(ctx); err == nil {
    78  			approxMsgCount = props.ApproximateMessagesCount()
    79  		}
    80  	}
    81  	dequeue, err := messageURL.Dequeue(ctx, a.conf.MaxInFlight, a.dequeueVisibilityTimeout)
    82  	if err != nil {
    83  		if cerr, ok := err.(azqueue.StorageError); ok {
    84  			if cerr.ServiceCode() == azqueue.ServiceCodeQueueNotFound {
    85  				ctx := context.Background()
    86  				_, err = queueURL.Create(ctx, azqueue.Metadata{})
    87  				return nil, nil, err
    88  			}
    89  			return nil, nil, fmt.Errorf("storage error message: %v", cerr)
    90  		}
    91  		return nil, nil, fmt.Errorf("error dequeing message: %v", err)
    92  	}
    93  	if n := dequeue.NumMessages(); n > 0 {
    94  		props, _ := queueURL.GetProperties(ctx)
    95  		metadata := props.NewMetadata()
    96  		msg := message.New(nil)
    97  		dqm := make([]*azqueue.DequeuedMessage, n)
    98  		for i := int32(0); i < n; i++ {
    99  			queueMsg := dequeue.Message(i)
   100  			part := message.NewPart([]byte(queueMsg.Text))
   101  			meta := part.Metadata()
   102  			meta.Set("queue_storage_insertion_time", queueMsg.InsertionTime.Format(time.RFC3339))
   103  			meta.Set("queue_storage_queue_name", queueName)
   104  			if a.conf.TrackProperties {
   105  				msgLag := 0
   106  				if approxMsgCount >= n {
   107  					msgLag = int(approxMsgCount - n)
   108  				}
   109  				meta.Set("queue_storage_message_lag", strconv.Itoa(msgLag))
   110  			}
   111  			for k, v := range metadata {
   112  				meta.Set(k, v)
   113  			}
   114  			msg.Append(part)
   115  			dqm[i] = queueMsg
   116  		}
   117  		return msg, func(ctx context.Context, res types.Response) error {
   118  			for i := int32(0); i < n; i++ {
   119  				msgIDURL := messageURL.NewMessageIDURL(dqm[i].ID)
   120  				_, err = msgIDURL.Delete(ctx, dqm[i].PopReceipt)
   121  				if err != nil {
   122  					return fmt.Errorf("error deleting message: %v", err)
   123  				}
   124  			}
   125  			return nil
   126  		}, nil
   127  	}
   128  	return nil, nil, nil
   129  }
   130  
   131  // CloseAsync begins cleaning up resources used by this reader asynchronously.
   132  func (a *azureQueueStorage) CloseAsync() {
   133  }
   134  
   135  // WaitForClose will block until either the reader is closed or a specified
   136  // timeout occurs.
   137  func (a *azureQueueStorage) WaitForClose(time.Duration) error {
   138  	return nil
   139  }
   140  
   141  //------------------------------------------------------------------------------