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 }