Browse Source

Initial version: list queues and receive message from the first one.

Frédéric G. Marand 1 year ago
commit
d320ed7b36
11 changed files with 358 additions and 0 deletions
  1. 2 0
      .gitignore
  2. 18 0
      LICENSE.txt
  3. 34 0
      Makefile
  4. 45 0
      README.md
  5. 27 0
      aws.go
  6. 44 0
      consumer.go
  7. 102 0
      demo.go
  8. 19 0
      docker-compose.yml
  9. 25 0
      go.mod
  10. 41 0
      go.sum
  11. 1 0
      producer.go

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/.idea
+/volume

+ 18 - 0
LICENSE.txt

@@ -0,0 +1,18 @@
+Copyright 2022 Frédéric G. MARAND <fgm@osinet.fr>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 34 - 0
Makefile

@@ -0,0 +1,34 @@
+PROFILE = test-profile
+REGION = eu-west-3
+URL = http://localhost:4566
+AWSOPTS = --profile $(PROFILE) --region $(REGION)
+SQSOPTS = --queue-url $(URL)/000000000000/dummy-queue
+SQS = aws --endpoint-url=$(URL) sqs $(AWSOPTS)
+QNAME = dummy-queue
+
+BODY = '{"event_id": "7456c8ee-949d-4100-a0c6-6ae8e581ae15", "event_time": "2019-11-26T16:00:47Z","data": {"test": 83411}}'
+
+clean:
+	docker compose down
+	rm -fr volume
+
+configure:
+	aws configure set aws_access_key_id "dummy" --profile $(PROFILE)
+	aws configure set aws_secret_access_key "dummy" --profile $(PROFILE)
+	aws configure set region $(REGION) --profile $(PROFILE)
+	aws configure set output "table" --profile $(PROFILE)
+
+create-queue: configure
+	$(SQS) create-queue --queue-name $(QNAME) --output table
+
+receive:
+	$(SQS) receive-message $(SQSOPTS) --output json
+
+delete:
+	$(SQS) delete-message  $(SQSOPTS) --receipt-handle "ODhkYTI2ZjQtNGE4YS00NmFhLTliM2UtNTNhNWVjYjVmMmYwIGFybjphd3M6c3FzOmV1LXdlc3QtMzowMDAwMDAwMDAwMDA6ZHVtbXktcXVldWUgZmE2MDExY2UtNTBhMS00Y2EyLTkxYTktMmMxMmEwZGE4YTczIDE2NzI3NTk2NzEuNzg1MTU0Ng=="
+
+send:
+	$(SQS) send-message    $(SQSOPTS) --message-body $(BODY)
+
+list-queues:
+	$(SQS) list-queues $(AWSOPTS)

+ 45 - 0
README.md

@@ -0,0 +1,45 @@
+# SQS Demo with Localstack
+## Configuring the environment
+- install Docker
+- install Localstack. 
+  - On macOS: `brew install localstack`
+- install AWS CLI: see [AWS doc](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
+- validate Docker configuration
+  - `localstack config validate`
+
+## Running the demo
+
+- on one terminal tab:
+  - start a local SQS instance
+    - `docker compose up` 
+    - or `docker-compose up` on older Docker versions
+  - you should be getting something like this:
+
+```
+Attaching to localstack_main
+localstack_main  | Waiting for all LocalStack services to be ready
+localstack_main  | 2023-01-04 06:36:28,138 CRIT Supervisor is running as root.  Privileges were not dropped because no user is specified in the config file.  If you intend to run as root, you can set user=root in the config file to avoid this message.
+localstack_main  | 2023-01-04 06:36:28,139 INFO supervisord started with pid 16
+localstack_main  | 2023-01-04 06:36:29,144 INFO spawned: 'infra' with pid 21
+localstack_main  |
+localstack_main  | LocalStack version: 1.3.2.dev
+localstack_main  | LocalStack Docker container id: 7ceddc18cd46
+localstack_main  | LocalStack build date: 2023-01-04
+localstack_main  | LocalStack build git hash: 8e5b4b09
+localstack_main  |
+localstack_main  | 2023-01-04 06:36:30,256 INFO success: infra entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
+localstack_main  | Ready.
+localstack_main  | 2023-01-04T06:36:30.138  WARN --- [-functhread5] hypercorn.error            : ASGI Framework Lifespan error, continuing without Lifespan support
+localstack_main  | 2023-01-04T06:36:30.138  WARN --- [-functhread5] hypercorn.error            : ASGI Framework Lifespan error, continuing without Lifespan support
+localstack_main  | 2023-01-04T06:36:30.140  INFO --- [-functhread5] hypercorn.error            : Running on https://0.0.0.0:4566 (CTRL + C to quit)
+localstack_main  | 2023-01-04T06:36:30.140  INFO --- [-functhread5] hypercorn.error            : Running on https://0.0.0.0:4566 (CTRL + C to quit)
+```
+
+- on another terminal tab
+  - create a queue: `make create-queue`
+  - send a message: `make send`
+  - list queues from Go and receive a message: `go run .`
+
+## Cleaning up
+
+After use, remove the SQS data space: `make clean`

+ 27 - 0
aws.go

@@ -0,0 +1,27 @@
+package main
+
+import (
+	"fmt"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+)
+
+type endpointResolver struct {
+	region string
+}
+
+func (e endpointResolver) ResolveEndpoint(service, _ string, options ...interface{}) (aws.Endpoint, error) {
+	if service != `SQS` {
+		return aws.Endpoint{}, fmt.Errorf("trying to resolve non-SQS service: %s", service)
+	}
+	ep := aws.Endpoint{
+		URL:               "http://localhost:4566/",
+		HostnameImmutable: false,
+		PartitionID:       "000000000000",
+		SigningName:       "",
+		SigningRegion:     e.region,
+		SigningMethod:     "",
+		Source:            0,
+	}
+	return ep, nil
+}

+ 44 - 0
consumer.go

@@ -0,0 +1,44 @@
+package main
+
+import (
+	"context"
+	"io"
+	"log"
+
+	"github.com/aws/aws-sdk-go-v2/aws"
+	"github.com/aws/aws-sdk-go-v2/service/sqs"
+	"github.com/aws/aws-sdk-go-v2/service/sqs/types"
+	"github.com/davecgh/go-spew/spew"
+	"gopkg.in/yaml.v2"
+)
+
+func lister(ctx context.Context, w io.Writer, client *sqs.Client) string {
+	lqo, err := client.ListQueues(ctx, &sqs.ListQueuesInput{
+		MaxResults:      aws.Int32(10),
+		NextToken:       nil,
+		QueueNamePrefix: aws.String(""),
+	})
+	if err != nil {
+		log.Fatalf("failed listing queues: %v", err)
+	}
+	y := yaml.NewEncoder(w)
+	y.Encode(lqo.QueueUrls)
+	return lqo.QueueUrls[0]
+}
+
+func receiver(ctx context.Context, w io.Writer, client *sqs.Client, qURL string) {
+	rmi := sqs.ReceiveMessageInput{
+		QueueUrl:                &qURL,
+		AttributeNames:          []types.QueueAttributeName{"All"},
+		MaxNumberOfMessages:     0,
+		MessageAttributeNames:   nil,
+		ReceiveRequestAttemptId: nil,
+		VisibilityTimeout:       0,
+		WaitTimeSeconds:         0,
+	}
+	msg, err := client.ReceiveMessage(ctx, &rmi)
+	if err != nil {
+		log.Fatalf("failed receiving from queue %s: %v", err)
+	}
+	spew.Dump(msg.Messages)
+}

+ 102 - 0
demo.go

@@ -0,0 +1,102 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"io"
+	"log"
+	"os"
+
+	"github.com/aws/aws-sdk-go-v2/config"
+	"github.com/aws/aws-sdk-go-v2/service/sqs"
+	"github.com/fgm/izidic"
+)
+
+func main() {
+	main2(os.Stdout, os.Args[0], os.Args[1:])
+}
+
+func main2(w io.Writer, name string, args []string) {
+	ctx := context.Background()
+	dic := resolve(w, name, args)
+	lister := dic.MustService("lister").(func(ctx context.Context) string)
+	receiver := dic.MustService("receiver").(func(ctx context.Context, qName string))
+	qName := lister(ctx)
+	receiver(ctx, qName)
+}
+
+func resolve(w io.Writer, name string, args []string) *izidic.Container {
+	dic := izidic.New()
+	dic.Store("name", name)
+	dic.Store("args", args)
+	dic.Store("writer", w)
+	dic.Register("flags", flagsService)
+	dic.Register("logger", loggerService)
+	dic.Register("sqs", sqsClientService)
+	dic.Register("lister", listerService)
+	dic.Register("receiver", receiverService)
+
+	dic.MustService("flags") // Store generated params before freeze.
+	dic.Freeze()
+	return dic
+}
+
+func flagsService(dic *izidic.Container) (any, error) {
+	fs := flag.NewFlagSet(dic.MustParam("name").(string), flag.ContinueOnError)
+	profile := fs.String("profile", "test-profile", "The AWS profile")
+	region := fs.String("region", "eu-west-3", "The AWS region to connect to")
+	qName := fs.String("queue-name", "dummy-queue", "The queue name")
+	sqsURL := fs.String("url", "http://localhost:4566", "The SQS endpoint URL")
+	if err := fs.Parse(dic.MustParam("args").([]string)); err != nil {
+		return nil, fmt.Errorf("cannot obtain CLI args")
+	}
+
+	dic.Store("profile", *profile)
+	dic.Store("region", *region)
+	dic.Store("url", *sqsURL)
+	dic.Store("queue-name", *qName)
+	return fs, nil
+}
+
+// loggerService is an izidic.Service also containing a one-time initialization action.
+func loggerService(dic *izidic.Container) (any, error) {
+	w := dic.MustParam("writer").(io.Writer)
+	log.SetOutput(w) // Support dependency code not taking an injected logger.
+	logger := log.New(w, "", log.LstdFlags)
+	return logger, nil
+}
+
+func sqsClientService(dic *izidic.Container) (any, error) {
+	ctx := context.Background()
+	profile := dic.MustParam("profile").(string)
+	region := dic.MustParam("region").(string)
+	epr := endpointResolver{region: region}
+	cfg, err := config.LoadDefaultConfig(ctx,
+		config.WithRegion(region),
+		config.WithSharedConfigProfile(profile),
+		config.WithEndpointResolverWithOptions(epr),
+	)
+	if err != nil {
+		return nil, fmt.Errorf("failed loading default AWS config: %w", err)
+	}
+
+	client := sqs.NewFromConfig(cfg)
+	return client, nil
+}
+
+func listerService(dic *izidic.Container) (any, error) {
+	cli := dic.MustService("sqs").(*sqs.Client)
+	w := dic.MustParam("writer").(io.Writer)
+	return func(ctx context.Context) string {
+		return lister(ctx, w, cli)
+	}, nil
+}
+
+func receiverService(dic *izidic.Container) (any, error) {
+	cli := dic.MustService("sqs").(*sqs.Client)
+	w := dic.MustParam("writer").(io.Writer)
+	return func(ctx context.Context, qName string) {
+		receiver(ctx, w, cli, qName)
+	}, nil
+}

+ 19 - 0
docker-compose.yml

@@ -0,0 +1,19 @@
+version: "3.8"
+
+services:
+  localstack:
+    container_name: "${LOCALSTACK_DOCKER_NAME-localstack_main}"
+    image: localstack/localstack
+    ports:
+      - "127.0.0.1:4566:4566"            # LocalStack Gateway
+      - "127.0.0.1:4510-4559:4510-4559"  # external services port range
+    environment:
+      - DEBUG=${DEBUG-}
+      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR-}
+      - DOCKER_HOST=unix:///var/run/docker.sock
+
+      - SERVICES=sqs
+      - EAGER_SERVICE_LOADING=1
+    volumes:
+      - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack"
+      - "/var/run/docker.sock:/var/run/docker.sock"

+ 25 - 0
go.mod

@@ -0,0 +1,25 @@
+module code.osinet.fr/fgm/sqs_demo
+
+go 1.19
+
+require (
+	github.com/aws/aws-sdk-go-v2 v1.17.3
+	github.com/aws/aws-sdk-go-v2/config v1.18.7
+	github.com/aws/aws-sdk-go-v2/service/sqs v1.19.17
+	github.com/davecgh/go-spew v1.1.0
+	github.com/fgm/izidic v0.0.2
+	gopkg.in/yaml.v2 v2.4.0
+)
+
+require (
+	github.com/aws/aws-sdk-go-v2/credentials v1.13.7 // indirect
+	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 // indirect
+	github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 // indirect
+	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 // indirect
+	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 // indirect
+	github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 // indirect
+	github.com/aws/smithy-go v1.13.5 // indirect
+)

+ 41 - 0
go.sum

@@ -0,0 +1,41 @@
+github.com/aws/aws-sdk-go-v2 v1.17.3 h1:shN7NlnVzvDUgPQ+1rLMSxY8OWRNDRYtiqe0p/PgrhY=
+github.com/aws/aws-sdk-go-v2 v1.17.3/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
+github.com/aws/aws-sdk-go-v2/config v1.18.7 h1:V94lTcix6jouwmAsgQMAEBozVAGJMFhVj+6/++xfe3E=
+github.com/aws/aws-sdk-go-v2/config v1.18.7/go.mod h1:OZYsyHFL5PB9UpyS78NElgKs11qI/B5KJau2XOJDXHA=
+github.com/aws/aws-sdk-go-v2/credentials v1.13.7 h1:qUUcNS5Z1092XBFT66IJM7mYkMwgZ8fcC8YDIbEwXck=
+github.com/aws/aws-sdk-go-v2/credentials v1.13.7/go.mod h1:AdCcbZXHQCjJh6NaH3pFaw8LUeBFn5+88BZGMVGuBT8=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21 h1:j9wi1kQ8b+e0FBVHxCqCGo4kxDU175hoDHcWAi0sauU=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.21/go.mod h1:ugwW57Z5Z48bpvUyZuaPy4Kv+vEfJWnIrky7RmkBvJg=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27 h1:I3cakv2Uy1vNmmhRQmFptYDxOvBnwCdNwyw63N0RaRU=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.27/go.mod h1:a1/UpzeyBBerajpnP5nGZa9mGzsBn5cOKxm6NWQsvoI=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21 h1:5NbbMrIzmUn/TXFqAle6mgrH5m9cOvMLRGL7pnG8tRE=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.21/go.mod h1:+Gxn8jYn5k9ebfHEqlhrMirFjSW0v0C9fI+KN5vk2kE=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28 h1:KeTxcGdNnQudb46oOl4d90f2I33DF/c6q3RnZAmvQdQ=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.3.28/go.mod h1:yRZVr/iT0AqyHeep00SZ4YfBAKojXz08w3XMBscdi0c=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21 h1:5C6XgTViSb0bunmU57b3CT+MhxULqHH2721FVA+/kDM=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.21/go.mod h1:lRToEJsn+DRA9lW4O9L9+/3hjTkUzlzyzHqn8MTds5k=
+github.com/aws/aws-sdk-go-v2/service/sqs v1.19.17 h1:bTr3F70BsgeJZW5QU0O4pVapJbgXuuiaaX9vQQfJAp8=
+github.com/aws/aws-sdk-go-v2/service/sqs v1.19.17/go.mod h1:jQhN5f4p3PALMNlUtfb/0wGIFlV7vGtJlPDVfxfNfPY=
+github.com/aws/aws-sdk-go-v2/service/sso v1.11.28 h1:gItLq3zBYyRDPmqAClgzTH8PBjDQGeyptYGHIwtYYNA=
+github.com/aws/aws-sdk-go-v2/service/sso v1.11.28/go.mod h1:wo/B7uUm/7zw/dWhBJ4FXuw1sySU5lyIhVg1Bu2yL9A=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11 h1:KCacyVSs/wlcPGx37hcbT3IGYO8P8Jx+TgSDhAXtQMY=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.11/go.mod h1:TZSH7xLO7+phDtViY/KUp9WGCJMQkLJ/VpgkTFd5gh8=
+github.com/aws/aws-sdk-go-v2/service/sts v1.17.7 h1:9Mtq1KM6nD8/+HStvWcvYnixJ5N85DX+P+OY3kI3W2k=
+github.com/aws/aws-sdk-go-v2/service/sts v1.17.7/go.mod h1:+lGbb3+1ugwKrNTWcf2RT05Xmp543B06zDFTwiTLp7I=
+github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
+github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fgm/izidic v0.0.2 h1:xpIr9sEVE2xVMlUPu7zcQhumxPB0mjW2/j8Cm598sMw=
+github.com/fgm/izidic v0.0.2/go.mod h1:HSUQlWnf88mpvaovrffBQnjqug3WLjTisLZl5g2RaEc=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=

+ 1 - 0
producer.go

@@ -0,0 +1 @@
+package main