From 6f649f9ec8a865ef19be8cae93c5c1c5cbe20122 Mon Sep 17 00:00:00 2001 From: Elia Bracci Date: Mon, 13 Apr 2026 15:25:26 +0200 Subject: [PATCH 1/9] PEEL-1526 allow AWS default credentials chain for pod identity --- sqs/sqs.go | 26 +++++++++++--------------- sqs/sqs_test.go | 29 ++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/sqs/sqs.go b/sqs/sqs.go index de2afdd..b8ff138 100644 --- a/sqs/sqs.go +++ b/sqs/sqs.go @@ -1,7 +1,6 @@ package sqs import ( - "errors" "fmt" "net/url" "os" @@ -37,12 +36,7 @@ func New(options ...Option) (*Driver, error) { } if driver.sqsClient == nil { - clientCredentials, err := getCredentials() - if err != nil { - return nil, err - } - - client, err := createClient(driver.url, driver.region, clientCredentials) + client, err := createClient(driver.url, driver.region, getCredentials()) if err != nil { return nil, err } @@ -59,16 +53,18 @@ func New(options ...Option) (*Driver, error) { return driver, nil } -func getCredentials() (*credentials.Credentials, error) { +// getCredentials returns explicit credentials when the legacy env vars are set, +// or nil to fall through to the AWS SDK default credential chain (which supports +// ECS/Pod Identity, instance profiles, env vars, and shared credentials files). +func getCredentials() *credentials.Credentials { if os.Getenv("AWS_SHARED_CREDENTIALS_FILE") != "" { - return credentials.NewSharedCredentials("", ""), nil - } else if os.Getenv("AWS_ACCESS_KEY_ID") != "" && os.Getenv("AWS_SECRET_ACCESS_KEY") != "" { - return credentials.NewEnvCredentials(), nil + return credentials.NewSharedCredentials("", "") } - - return nil, errors.New( - "missing AWS_SHARED_CREDENTIALS_FILE and AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY env vars", - ) + if os.Getenv("AWS_ACCESS_KEY_ID") != "" && os.Getenv("AWS_SECRET_ACCESS_KEY") != "" { + return credentials.NewEnvCredentials() + } + // nil tells the SDK to use its built-in default chain, including Pod Identity. + return nil } func createClient(queueUrl string, region string, clientCredentials *credentials.Credentials) (*sqs.SQS, error) { diff --git a/sqs/sqs_test.go b/sqs/sqs_test.go index 7adf42f..86ee312 100644 --- a/sqs/sqs_test.go +++ b/sqs/sqs_test.go @@ -55,7 +55,34 @@ func (suite *SQSTestSuite) TestNewWithDefaultOptions() { _, err := sqs.New() suite.Error(err) - suite.Contains(err.Error(), "missing") + suite.Contains(err.Error(), "error creating sqs client") +} + +// TestNewWithEnvCredentials verifies that static key/secret env vars are accepted. +func (suite *SQSTestSuite) TestNewWithEnvCredentials() { + os.Setenv("AWS_ACCESS_KEY_ID", "AKIAIOSFODNN7EXAMPLE") + os.Setenv("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY") + + _, err := sqs.New( + sqs.WithUrl("https://sqs.eu-central-1.amazonaws.com"), + sqs.WithRegion("us-east-1"), + ) + + suite.Nil(err) +} + +// TestNewWithNoCredentialEnvVars verifies that the driver can be created when no +// explicit credential env vars are set, falling through to the AWS SDK default +// credential chain (which covers Pod Identity, instance profiles, etc.). +func (suite *SQSTestSuite) TestNewWithNoCredentialEnvVars() { + _, err := sqs.New( + sqs.WithUrl("https://sqs.eu-central-1.amazonaws.com"), + sqs.WithRegion("us-east-1"), + ) + + // The driver should be created without error — credential resolution is + // deferred to the first actual API call, not at construction time. + suite.Nil(err) } func (suite *SQSTestSuite) TestNew_InvalidQueueURL() { From dedbd66a1dbd61f0f6b9401df5ed4afcfe408d2a Mon Sep 17 00:00:00 2001 From: Elia Bracci Date: Mon, 13 Apr 2026 15:35:27 +0200 Subject: [PATCH 2/9] PEEL-1526 Update .golangci.yml configuration to streamline linter settings and output format. Consolidated linters and added exclusions for generated files, while maintaining specific settings for errcheck, funlen, and govet. --- .golangci.yml | 103 ++++++++++++++++++++------------------------------ 1 file changed, 42 insertions(+), 61 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 083e762..bd005c9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,69 +1,50 @@ -# options for analysis running -run: - # timeout for analysis, e.g. 30s, 5m, default is 1m - deadline: 2m - -issues: - # Only report issues for changes since master - new-from-rev: origin/master - -# output configuration options -output: - # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" - format: colored-line-number - -linters-settings: - errcheck: - # report about not checking of errors in type assertions: `a := b.(MyStruct)`; - # default is false: such cases aren't reported by default. - check-type-assertions: true - - # Function length check - funlen: - lines: 60 - statements: 40 - - # Report deeply nested if statements - nestif: - # minimal complexity of if statements to report, 5 by default - min-complexity: 4 - - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true +version: "2" - golint: - # minimal confidence for issues, default is 0.8 - min-confidence: 0.8 - - govet: - # report about shadowed variables - check-shadowing: true - enable-all: true - disable: - # Do not check field memory alignment because in most cases the performance gain is not worth the headache - - fieldalignment +run: + timeout: 2m linters: - # Disable the default linters so we can explicitly name the linters we want - disable-all: true - - # List of enabled linters + default: none enable: - ##################### - # Default linters - ##################### - - gofmt - # Checks error handling - errcheck - # Linter for Go source code that specializes in simplifying a code - - gosimple - # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose - # arguments do not align with the format string - govet - # Detects when assignments to existing variables are not used - ineffassign - # Static code analytics - staticcheck - # Unused checks - - unused \ No newline at end of file + - unused + settings: + errcheck: + check-type-assertions: true + funlen: + lines: 60 + statements: 40 + govet: + disable: + - fieldalignment + enable-all: true + nestif: + min-complexity: 4 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +issues: + new-from-rev: origin/master +formatters: + enable: + - gofmt + settings: + gofmt: + simplify: true + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ From 2d36d1e57a48d6aad07c7d90d98d8c1f7fd793d6 Mon Sep 17 00:00:00 2001 From: Elia Bracci Date: Mon, 13 Apr 2026 15:36:37 +0200 Subject: [PATCH 3/9] Revert "PEEL-1526 Update .golangci.yml configuration to streamline linter settings and output format. Consolidated linters and added exclusions for generated files, while maintaining specific settings for errcheck, funlen, and govet." This reverts commit dedbd66a1dbd61f0f6b9401df5ed4afcfe408d2a. --- .golangci.yml | 103 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 61 insertions(+), 42 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index bd005c9..083e762 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,50 +1,69 @@ -version: "2" - +# options for analysis running run: - timeout: 2m + # timeout for analysis, e.g. 30s, 5m, default is 1m + deadline: 2m + +issues: + # Only report issues for changes since master + new-from-rev: origin/master + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" + format: colored-line-number + +linters-settings: + errcheck: + # report about not checking of errors in type assertions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: true + + # Function length check + funlen: + lines: 60 + statements: 40 + + # Report deeply nested if statements + nestif: + # minimal complexity of if statements to report, 5 by default + min-complexity: 4 + + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + + golint: + # minimal confidence for issues, default is 0.8 + min-confidence: 0.8 + + govet: + # report about shadowed variables + check-shadowing: true + enable-all: true + disable: + # Do not check field memory alignment because in most cases the performance gain is not worth the headache + - fieldalignment linters: - default: none + # Disable the default linters so we can explicitly name the linters we want + disable-all: true + + # List of enabled linters enable: + ##################### + # Default linters + ##################### + - gofmt + # Checks error handling - errcheck + # Linter for Go source code that specializes in simplifying a code + - gosimple + # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose + # arguments do not align with the format string - govet + # Detects when assignments to existing variables are not used - ineffassign + # Static code analytics - staticcheck - - unused - settings: - errcheck: - check-type-assertions: true - funlen: - lines: 60 - statements: 40 - govet: - disable: - - fieldalignment - enable-all: true - nestif: - min-complexity: 4 - exclusions: - generated: lax - presets: - - comments - - common-false-positives - - legacy - - std-error-handling - paths: - - third_party$ - - builtin$ - - examples$ -issues: - new-from-rev: origin/master -formatters: - enable: - - gofmt - settings: - gofmt: - simplify: true - exclusions: - generated: lax - paths: - - third_party$ - - builtin$ - - examples$ + # Unused checks + - unused \ No newline at end of file From 3778cb1c32dfc42738290fae390d3493688f25a6 Mon Sep 17 00:00:00 2001 From: Elia Bracci Date: Mon, 13 Apr 2026 15:44:50 +0200 Subject: [PATCH 4/9] PEEL-1526 Update .golangci.yml to modify timeout setting and adjust output format configuration. Explicitly enable shadow check in govet and remove deprecated check-shadowing option. --- .golangci.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 083e762..c08b536 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,7 +1,7 @@ # options for analysis running run: # timeout for analysis, e.g. 30s, 5m, default is 1m - deadline: 2m + timeout: 2m issues: # Only report issues for changes since master @@ -10,7 +10,8 @@ issues: # output configuration options output: # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" - format: colored-line-number + formats: + - format: colored-line-number linters-settings: errcheck: @@ -32,14 +33,11 @@ linters-settings: # simplify code: gofmt with `-s` option, true by default simplify: true - golint: - # minimal confidence for issues, default is 0.8 - min-confidence: 0.8 - govet: - # report about shadowed variables - check-shadowing: true enable-all: true + enable: + # Explicitly enable shadow instead of using the deprecated check-shadowing + - shadow disable: # Do not check field memory alignment because in most cases the performance gain is not worth the headache - fieldalignment @@ -66,4 +64,4 @@ linters: # Static code analytics - staticcheck # Unused checks - - unused \ No newline at end of file + - unused From 47f704552a749df5e08444570c893687fbc8ccfd Mon Sep 17 00:00:00 2001 From: Elia Bracci Date: Mon, 13 Apr 2026 15:46:33 +0200 Subject: [PATCH 5/9] PEEL-1526 Update .golangci.yml to enable all govet analyzers and remove explicit shadow check configuration. --- .golangci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index c08b536..50adf01 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -34,10 +34,8 @@ linters-settings: simplify: true govet: + # Enables all analyzers, including 'shadow' enable-all: true - enable: - # Explicitly enable shadow instead of using the deprecated check-shadowing - - shadow disable: # Do not check field memory alignment because in most cases the performance gain is not worth the headache - fieldalignment From 0e28d7aa10ac6cbd8aafcd7d89795854fceb1c3e Mon Sep 17 00:00:00 2001 From: Elia Bracci Date: Mon, 13 Apr 2026 15:49:25 +0200 Subject: [PATCH 6/9] PEEL-1526 Update .golangci.yml and lint workflow to use golangci-lint v2.11.3, streamline linter settings, and add exclusions for generated files. --- .github/workflows/lint.yml | 2 +- .golangci.yml | 95 ++++++++++++++++---------------------- 2 files changed, 41 insertions(+), 56 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ddc7214..6308a19 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,4 +16,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: - version: v1.60 \ No newline at end of file + version: v2.11.3 diff --git a/.golangci.yml b/.golangci.yml index 50adf01..bd005c9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,65 +1,50 @@ -# options for analysis running +version: "2" + run: - # timeout for analysis, e.g. 30s, 5m, default is 1m timeout: 2m -issues: - # Only report issues for changes since master - new-from-rev: origin/master - -# output configuration options -output: - # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" - formats: - - format: colored-line-number - -linters-settings: - errcheck: - # report about not checking of errors in type assertions: `a := b.(MyStruct)`; - # default is false: such cases aren't reported by default. - check-type-assertions: true - - # Function length check - funlen: - lines: 60 - statements: 40 - - # Report deeply nested if statements - nestif: - # minimal complexity of if statements to report, 5 by default - min-complexity: 4 - - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true - - govet: - # Enables all analyzers, including 'shadow' - enable-all: true - disable: - # Do not check field memory alignment because in most cases the performance gain is not worth the headache - - fieldalignment - linters: - # Disable the default linters so we can explicitly name the linters we want - disable-all: true - - # List of enabled linters + default: none enable: - ##################### - # Default linters - ##################### - - gofmt - # Checks error handling - errcheck - # Linter for Go source code that specializes in simplifying a code - - gosimple - # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose - # arguments do not align with the format string - govet - # Detects when assignments to existing variables are not used - ineffassign - # Static code analytics - staticcheck - # Unused checks - unused + settings: + errcheck: + check-type-assertions: true + funlen: + lines: 60 + statements: 40 + govet: + disable: + - fieldalignment + enable-all: true + nestif: + min-complexity: 4 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ +issues: + new-from-rev: origin/master +formatters: + enable: + - gofmt + settings: + gofmt: + simplify: true + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ From 2dc08b404a5e6e1e63d7c1c72020654b25bb5f78 Mon Sep 17 00:00:00 2001 From: Elia Bracci Date: Mon, 13 Apr 2026 15:50:28 +0200 Subject: [PATCH 7/9] PEEL-1526 Update lint workflow to use golangci-lint v7 for improved linting capabilities. --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6308a19..73c9b3f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,6 +14,6 @@ jobs: with: go-version: stable - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v7 with: version: v2.11.3 From f32e8458b17b819e4b26247f325d60d5c97fad5e Mon Sep 17 00:00:00 2001 From: Elia Bracci Date: Mon, 13 Apr 2026 15:55:45 +0200 Subject: [PATCH 8/9] PEEL-1526 Enhance JSON unmarshalling in memory and SQS message handlers to validate the presence of the 'name' field, improving error handling and robustness. --- internal/examples/memory/main.go | 11 ++++++++--- internal/examples/sqs/message.go | 11 ++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/internal/examples/memory/main.go b/internal/examples/memory/main.go index b189e05..664212f 100644 --- a/internal/examples/memory/main.go +++ b/internal/examples/memory/main.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "log" "os" "os/signal" @@ -18,12 +19,16 @@ type myMessage struct { func (e *myMessage) UnmarshalJSON(data []byte) error { var raw map[string]any - err := json.Unmarshal(data, &raw) - if err != nil { + if err := json.Unmarshal(data, &raw); err != nil { return err } - e.name = raw["name"].(string) + name, ok := raw["name"].(string) + if !ok { + return fmt.Errorf("missing or invalid field: name") + } + + e.name = name return nil } diff --git a/internal/examples/sqs/message.go b/internal/examples/sqs/message.go index 56e3343..a3be867 100644 --- a/internal/examples/sqs/message.go +++ b/internal/examples/sqs/message.go @@ -2,6 +2,7 @@ package sqs import ( "encoding/json" + "fmt" ) type MyEvent struct { @@ -10,12 +11,16 @@ type MyEvent struct { func (e *MyEvent) UnmarshalJSON(data []byte) error { var raw map[string]any - err := json.Unmarshal(data, &raw) - if err != nil { + if err := json.Unmarshal(data, &raw); err != nil { return err } - e.Name = raw["name"].(string) + name, ok := raw["name"].(string) + if !ok { + return fmt.Errorf("missing or invalid field: name") + } + + e.Name = name return nil } From 817601c1c8c72b95075125779d946bffabbeb34d Mon Sep 17 00:00:00 2001 From: Elia Bracci Date: Mon, 13 Apr 2026 16:00:22 +0200 Subject: [PATCH 9/9] PEEL-1526 Add unit tests for JSON marshalling and unmarshalling of myMessage and MyEvent structs - Implement tests for successful unmarshalling, missing fields, wrong types, and invalid JSON for both myMessage and MyEvent. - Ensure proper error handling and validation of the 'name' field in JSON data. --- internal/examples/memory/main_test.go | 42 +++++++++++++++++++++++++++ internal/examples/sqs/message_test.go | 42 +++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 internal/examples/memory/main_test.go create mode 100644 internal/examples/sqs/message_test.go diff --git a/internal/examples/memory/main_test.go b/internal/examples/memory/main_test.go new file mode 100644 index 0000000..4682f9d --- /dev/null +++ b/internal/examples/memory/main_test.go @@ -0,0 +1,42 @@ +package main + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMyMessage_UnmarshalJSON_Success(t *testing.T) { + var m myMessage + err := json.Unmarshal([]byte(`{"name":"hello"}`), &m) + assert.NoError(t, err) + assert.Equal(t, "hello", m.name) +} + +func TestMyMessage_UnmarshalJSON_MissingField(t *testing.T) { + var m myMessage + err := json.Unmarshal([]byte(`{}`), &m) + assert.Error(t, err) + assert.Contains(t, err.Error(), "missing or invalid field: name") +} + +func TestMyMessage_UnmarshalJSON_WrongType(t *testing.T) { + var m myMessage + err := json.Unmarshal([]byte(`{"name": 42}`), &m) + assert.Error(t, err) + assert.Contains(t, err.Error(), "missing or invalid field: name") +} + +func TestMyMessage_UnmarshalJSON_InvalidJSON(t *testing.T) { + var m myMessage + err := json.Unmarshal([]byte(`not-json`), &m) + assert.Error(t, err) +} + +func TestMyMessage_MarshalJSON(t *testing.T) { + m := myMessage{name: "hello"} + data, err := json.Marshal(&m) + assert.NoError(t, err) + assert.JSONEq(t, `{"name":"hello"}`, string(data)) +} diff --git a/internal/examples/sqs/message_test.go b/internal/examples/sqs/message_test.go new file mode 100644 index 0000000..ea9c21f --- /dev/null +++ b/internal/examples/sqs/message_test.go @@ -0,0 +1,42 @@ +package sqs + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMyEvent_UnmarshalJSON_Success(t *testing.T) { + var e MyEvent + err := json.Unmarshal([]byte(`{"name":"test-event"}`), &e) + assert.NoError(t, err) + assert.Equal(t, "test-event", e.Name) +} + +func TestMyEvent_UnmarshalJSON_MissingField(t *testing.T) { + var e MyEvent + err := json.Unmarshal([]byte(`{}`), &e) + assert.Error(t, err) + assert.Contains(t, err.Error(), "missing or invalid field: name") +} + +func TestMyEvent_UnmarshalJSON_WrongType(t *testing.T) { + var e MyEvent + err := json.Unmarshal([]byte(`{"name": 42}`), &e) + assert.Error(t, err) + assert.Contains(t, err.Error(), "missing or invalid field: name") +} + +func TestMyEvent_UnmarshalJSON_InvalidJSON(t *testing.T) { + var e MyEvent + err := json.Unmarshal([]byte(`not-json`), &e) + assert.Error(t, err) +} + +func TestMyEvent_MarshalJSON(t *testing.T) { + e := MyEvent{Name: "test-event"} + data, err := json.Marshal(&e) + assert.NoError(t, err) + assert.JSONEq(t, `{"name":"test-event"}`, string(data)) +}