github.com/darmach/terratest@v0.34.8-0.20210517103231-80931f95e3ff/modules/aws/sqs.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/aws/aws-sdk-go/aws"
     9  	"github.com/aws/aws-sdk-go/service/sqs"
    10  	"github.com/google/uuid"
    11  	"github.com/gruntwork-io/terratest/modules/logger"
    12  	"github.com/gruntwork-io/terratest/modules/testing"
    13  )
    14  
    15  // CreateRandomQueue creates a new SQS queue with a random name that starts with the given prefix and return the queue URL.
    16  func CreateRandomQueue(t testing.TestingT, awsRegion string, prefix string) string {
    17  	url, err := CreateRandomQueueE(t, awsRegion, prefix)
    18  	if err != nil {
    19  		t.Fatal(err)
    20  	}
    21  	return url
    22  }
    23  
    24  // CreateRandomQueueE creates a new SQS queue with a random name that starts with the given prefix and return the queue URL.
    25  func CreateRandomQueueE(t testing.TestingT, awsRegion string, prefix string) (string, error) {
    26  	logger.Logf(t, "Creating randomly named SQS queue with prefix %s", prefix)
    27  
    28  	sqsClient, err := NewSqsClientE(t, awsRegion)
    29  	if err != nil {
    30  		return "", err
    31  	}
    32  
    33  	channel, err := uuid.NewUUID()
    34  	if err != nil {
    35  		return "", err
    36  	}
    37  
    38  	channelName := fmt.Sprintf("%s-%s", prefix, channel.String())
    39  
    40  	queue, err := sqsClient.CreateQueue(&sqs.CreateQueueInput{
    41  		QueueName: aws.String(channelName),
    42  	})
    43  
    44  	if err != nil {
    45  		return "", err
    46  	}
    47  
    48  	return aws.StringValue(queue.QueueUrl), nil
    49  }
    50  
    51  // CreateRandomFifoQueue creates a new FIFO SQS queue with a random name that starts with the given prefix and return the queue URL.
    52  func CreateRandomFifoQueue(t testing.TestingT, awsRegion string, prefix string) string {
    53  	url, err := CreateRandomFifoQueueE(t, awsRegion, prefix)
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  	return url
    58  }
    59  
    60  // CreateRandomFifoQueueE creates a new FIFO SQS queue with a random name that starts with the given prefix and return the queue URL.
    61  func CreateRandomFifoQueueE(t testing.TestingT, awsRegion string, prefix string) (string, error) {
    62  	logger.Logf(t, "Creating randomly named FIFO SQS queue with prefix %s", prefix)
    63  
    64  	sqsClient, err := NewSqsClientE(t, awsRegion)
    65  	if err != nil {
    66  		return "", err
    67  	}
    68  
    69  	channel, err := uuid.NewUUID()
    70  	if err != nil {
    71  		return "", err
    72  	}
    73  
    74  	channelName := fmt.Sprintf("%s-%s.fifo", prefix, channel.String())
    75  
    76  	queue, err := sqsClient.CreateQueue(&sqs.CreateQueueInput{
    77  		QueueName: aws.String(channelName),
    78  		Attributes: map[string]*string{
    79  			"ContentBasedDeduplication": aws.String("true"),
    80  			"FifoQueue":                 aws.String("true"),
    81  		},
    82  	})
    83  
    84  	if err != nil {
    85  		return "", err
    86  	}
    87  
    88  	return aws.StringValue(queue.QueueUrl), nil
    89  }
    90  
    91  // DeleteQueue deletes the SQS queue with the given URL.
    92  func DeleteQueue(t testing.TestingT, awsRegion string, queueURL string) {
    93  	err := DeleteQueueE(t, awsRegion, queueURL)
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  }
    98  
    99  // DeleteQueueE deletes the SQS queue with the given URL.
   100  func DeleteQueueE(t testing.TestingT, awsRegion string, queueURL string) error {
   101  	logger.Logf(t, "Deleting SQS Queue %s", queueURL)
   102  
   103  	sqsClient, err := NewSqsClientE(t, awsRegion)
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	_, err = sqsClient.DeleteQueue(&sqs.DeleteQueueInput{
   109  		QueueUrl: aws.String(queueURL),
   110  	})
   111  
   112  	return err
   113  }
   114  
   115  // DeleteMessageFromQueue deletes the message with the given receipt from the SQS queue with the given URL.
   116  func DeleteMessageFromQueue(t testing.TestingT, awsRegion string, queueURL string, receipt string) {
   117  	err := DeleteMessageFromQueueE(t, awsRegion, queueURL, receipt)
   118  	if err != nil {
   119  		t.Fatal(err)
   120  	}
   121  }
   122  
   123  // DeleteMessageFromQueueE deletes the message with the given receipt from the SQS queue with the given URL.
   124  func DeleteMessageFromQueueE(t testing.TestingT, awsRegion string, queueURL string, receipt string) error {
   125  	logger.Logf(t, "Deleting message from queue %s (%s)", queueURL, receipt)
   126  
   127  	sqsClient, err := NewSqsClientE(t, awsRegion)
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	_, err = sqsClient.DeleteMessage(&sqs.DeleteMessageInput{
   133  		ReceiptHandle: &receipt,
   134  		QueueUrl:      &queueURL,
   135  	})
   136  
   137  	return err
   138  }
   139  
   140  // SendMessageToQueue sends the given message to the SQS queue with the given URL.
   141  func SendMessageToQueue(t testing.TestingT, awsRegion string, queueURL string, message string) {
   142  	err := SendMessageToQueueE(t, awsRegion, queueURL, message)
   143  	if err != nil {
   144  		t.Fatal(err)
   145  	}
   146  }
   147  
   148  // SendMessageToQueueE sends the given message to the SQS queue with the given URL.
   149  func SendMessageToQueueE(t testing.TestingT, awsRegion string, queueURL string, message string) error {
   150  	logger.Logf(t, "Sending message %s to queue %s", message, queueURL)
   151  
   152  	sqsClient, err := NewSqsClientE(t, awsRegion)
   153  	if err != nil {
   154  		return err
   155  	}
   156  
   157  	res, err := sqsClient.SendMessage(&sqs.SendMessageInput{
   158  		MessageBody: &message,
   159  		QueueUrl:    &queueURL,
   160  	})
   161  
   162  	if err != nil {
   163  		if strings.Contains(err.Error(), "AWS.SimpleQueueService.NonExistentQueue") {
   164  			logger.Logf(t, fmt.Sprintf("WARN: Client has stopped listening on queue %s", queueURL))
   165  			return nil
   166  		}
   167  		return err
   168  	}
   169  
   170  	logger.Logf(t, "Message id %s sent to queue %s", aws.StringValue(res.MessageId), queueURL)
   171  
   172  	return nil
   173  }
   174  
   175  // SendMessageToFifoQueue sends the given message to the FIFO SQS queue with the given URL.
   176  func SendMessageFifoToQueue(t testing.TestingT, awsRegion string, queueURL string, message string, messageGroupID string) {
   177  	err := SendMessageToFifoQueueE(t, awsRegion, queueURL, message, messageGroupID)
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  }
   182  
   183  // SendMessageToFifoQueueE sends the given message to the FIFO SQS queue with the given URL.
   184  func SendMessageToFifoQueueE(t testing.TestingT, awsRegion string, queueURL string, message string, messageGroupID string) error {
   185  	logger.Logf(t, "Sending message %s to queue %s", message, queueURL)
   186  
   187  	sqsClient, err := NewSqsClientE(t, awsRegion)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	res, err := sqsClient.SendMessage(&sqs.SendMessageInput{
   193  		MessageBody:    &message,
   194  		QueueUrl:       &queueURL,
   195  		MessageGroupId: &messageGroupID,
   196  	})
   197  
   198  	if err != nil {
   199  		if strings.Contains(err.Error(), "AWS.SimpleQueueService.NonExistentQueue") {
   200  			logger.Logf(t, fmt.Sprintf("WARN: Client has stopped listening on queue %s", queueURL))
   201  			return nil
   202  		}
   203  		return err
   204  	}
   205  
   206  	logger.Logf(t, "Message id %s sent to queue %s", aws.StringValue(res.MessageId), queueURL)
   207  
   208  	return nil
   209  }
   210  
   211  // QueueMessageResponse contains a queue message.
   212  type QueueMessageResponse struct {
   213  	ReceiptHandle string
   214  	MessageBody   string
   215  	Error         error
   216  }
   217  
   218  // WaitForQueueMessage waits to receive a message from on the queueURL. Since the API only allows us to wait a max 20 seconds for a new
   219  // message to arrive, we must loop TIMEOUT/20 number of times to be able to wait for a total of TIMEOUT seconds
   220  func WaitForQueueMessage(t testing.TestingT, awsRegion string, queueURL string, timeout int) QueueMessageResponse {
   221  	sqsClient, err := NewSqsClientE(t, awsRegion)
   222  	if err != nil {
   223  		return QueueMessageResponse{Error: err}
   224  	}
   225  
   226  	cycles := timeout
   227  	cycleLength := 1
   228  	if timeout >= 20 {
   229  		cycleLength = 20
   230  		cycles = timeout / cycleLength
   231  	}
   232  
   233  	for i := 0; i < cycles; i++ {
   234  		logger.Logf(t, "Waiting for message on %s (%ss)", queueURL, strconv.Itoa(i*cycleLength))
   235  		result, err := sqsClient.ReceiveMessage(&sqs.ReceiveMessageInput{
   236  			QueueUrl:              aws.String(queueURL),
   237  			AttributeNames:        aws.StringSlice([]string{"SentTimestamp"}),
   238  			MaxNumberOfMessages:   aws.Int64(1),
   239  			MessageAttributeNames: aws.StringSlice([]string{"All"}),
   240  			WaitTimeSeconds:       aws.Int64(int64(cycleLength)),
   241  		})
   242  
   243  		if err != nil {
   244  			return QueueMessageResponse{Error: err}
   245  		}
   246  
   247  		if len(result.Messages) > 0 {
   248  			logger.Logf(t, "Message %s received on %s", *result.Messages[0].MessageId, queueURL)
   249  			return QueueMessageResponse{ReceiptHandle: *result.Messages[0].ReceiptHandle, MessageBody: *result.Messages[0].Body}
   250  		}
   251  	}
   252  
   253  	return QueueMessageResponse{Error: ReceiveMessageTimeout{QueueUrl: queueURL, TimeoutSec: timeout}}
   254  }
   255  
   256  // NewSqsClient creates a new SQS client.
   257  func NewSqsClient(t testing.TestingT, region string) *sqs.SQS {
   258  	client, err := NewSqsClientE(t, region)
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  	return client
   263  }
   264  
   265  // NewSqsClientE creates a new SQS client.
   266  func NewSqsClientE(t testing.TestingT, region string) (*sqs.SQS, error) {
   267  	sess, err := NewAuthenticatedSession(region)
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  
   272  	return sqs.New(sess), nil
   273  }
   274  
   275  // ReceiveMessageTimeout is an error that occurs if receiving a message times out.
   276  type ReceiveMessageTimeout struct {
   277  	QueueUrl   string
   278  	TimeoutSec int
   279  }
   280  
   281  func (err ReceiveMessageTimeout) Error() string {
   282  	return fmt.Sprintf("Failed to receive messages on %s within %s seconds", err.QueueUrl, strconv.Itoa(err.TimeoutSec))
   283  }