Skip to content

Commit

Permalink
Add SQS polling infrastructure (#187)
Browse files Browse the repository at this point in the history
* sqs

* mark false

* remove unneeded import

* first pass comments

* remove header

* no-op

* move interface
  • Loading branch information
samrabelachew authored Feb 16, 2022
1 parent 59ae206 commit 688a360
Show file tree
Hide file tree
Showing 11 changed files with 829 additions and 11 deletions.
18 changes: 15 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,21 @@ require (
)

require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/aws/aws-sdk-go-v2 v1.13.0
github.com/aws/aws-sdk-go-v2/config v1.13.1
github.com/aws/aws-sdk-go-v2/service/sqs v1.16.0
)

require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 // indirect
github.com/aws/smithy-go v1.10.0 // indirect
github.com/onsi/ginkgo v1.14.0 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
)
32 changes: 24 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki
github.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4 v0.0.0-20201206235148-c87e55b61113 h1:+Je12tQpLUUQEfMUrLkTPXe1wh8VXCPjFsdwY29co30=
github.com/antlr/antlr4 v0.0.0-20201206235148-c87e55b61113/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
Expand All @@ -72,6 +68,30 @@ github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:W
github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=
github.com/aws/aws-sdk-go v1.40.12 h1:66+IAWhl+aaZCW1+ndS/GNfAxy8tJca2cMoIF2O325I=
github.com/aws/aws-sdk-go v1.40.12/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v1.13.0 h1:1XIXAfxsEmbhbj5ry3D3vX+6ZcUYvIqSm4CWWEuGZCA=
github.com/aws/aws-sdk-go-v2 v1.13.0/go.mod h1:L6+ZpqHaLbAaxsqV0L4cvxZY7QupWJB4fhkf8LXvC7w=
github.com/aws/aws-sdk-go-v2/config v1.13.1 h1:yLv8bfNoT4r+UvUKQKqRtdnvuWGMK5a82l4ru9Jvnuo=
github.com/aws/aws-sdk-go-v2/config v1.13.1/go.mod h1:Ba5Z4yL/UGbjQUzsiaN378YobhFo0MLfueXGiOsYtEs=
github.com/aws/aws-sdk-go-v2/credentials v1.8.0 h1:8Ow0WcyDesGNL0No11jcgb1JAtE+WtubqXjgxau+S0o=
github.com/aws/aws-sdk-go-v2/credentials v1.8.0/go.mod h1:gnMo58Vwx3Mu7hj1wpcG8DI0s57c9o42UQ6wgTQT5to=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0 h1:NITDuUZO34mqtOwFWZiXo7yAHj7kf+XPE+EiKuCBNUI=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.10.0/go.mod h1:I6/fHT/fH460v09eg2gVrd8B/IqskhNdpcLH0WNO3QI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4 h1:CRiQJ4E2RhfDdqbie1ZYDo8QtIo75Mk7oTdJSfwJTMQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.4/go.mod h1:XHgQ7Hz2WY2GAn//UXHofLfPXWh+s62MbMOijrg12Lw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0 h1:3ADoioDMOtF4uiK59vCpplpCwugEU+v4ZFD29jDL3RQ=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.2.0/go.mod h1:BsCSJHx5DnDXIrOcqB8KN1/B+hXLG/bi4Y6Vjcx/x9E=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5 h1:ixotxbfTCFpqbuwFv/RcZwyzhkxPSYDYEMcj4niB5Uk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.5/go.mod h1:R3sWUqPcfXSiF/LSFJhjyJmpg9uV6yP2yv3YZZjldVI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0 h1:4QAOB3KrvI1ApJK14sliGr3Ie2pjyvNypn/lfzDHfUw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.7.0/go.mod h1:K/qPe6AP2TGYv4l6n7c88zh9jWBDf6nHhvg1fx/EWfU=
github.com/aws/aws-sdk-go-v2/service/sqs v1.16.0 h1:dzWS4r8E9bA0TesHM40FSAtedwpTVCuTsLI8EziSqyk=
github.com/aws/aws-sdk-go-v2/service/sqs v1.16.0/go.mod h1:IBTQMG8mtyj37OWg7vIXcg714Ntcb/LlYou/rZpvV1k=
github.com/aws/aws-sdk-go-v2/service/sso v1.9.0 h1:1qLJeQGBmNQW3mBNzK2CFmrQNmoXWrscPqsrAaU1aTA=
github.com/aws/aws-sdk-go-v2/service/sso v1.9.0/go.mod h1:vCV4glupK3tR7pw7ks7Y4jYRL86VvxS+g5qk04YeWrU=
github.com/aws/aws-sdk-go-v2/service/sts v1.14.0 h1:ksiDXhvNYg0D2/UFkLejsaz3LqpW5yjNQ8Nx9Sn2c0E=
github.com/aws/aws-sdk-go-v2/service/sts v1.14.0/go.mod h1:u0xMJKDvvfocRjiozsoZglVNXRG19043xzp3r2ivLIk=
github.com/aws/smithy-go v1.10.0 h1:gsoZQMNHnX+PaghNw4ynPsyGP7aUCqx5sY2dlPQsZ0w=
github.com/aws/smithy-go v1.10.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
Expand Down Expand Up @@ -205,8 +225,6 @@ github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2-0.20200519141726-cb32006e483f h1:qa1wFcvZzVLbFVPdsdTsWL6k5IP6BEmFmd9SeahRQ5s=
github.com/google/uuid v1.1.2-0.20200519141726-cb32006e483f/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
Expand Down Expand Up @@ -781,8 +799,6 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
33 changes: 33 additions & 0 deletions server/controllers/events/mocks/matchers/http_responsewriter.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

102 changes: 102 additions & 0 deletions server/controllers/events/mocks/mock_vcs_post_handler.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 70 additions & 0 deletions server/lyft/aws/sqs/message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package sqs

import (
"bufio"
"bytes"
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
"github.com/pkg/errors"
"github.com/uber-go/tally"
"net/http"
)

//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_sqs_message_handler.go MessageProcessor
type MessageProcessor interface {
ProcessMessage(types.Message) error
}

//go:generate pegomock generate -m --use-experimental-model-gen --package mocks -o mocks/mock_vcs_post_handler.go VCSPostHandler
type VCSPostHandler interface {
Post(w http.ResponseWriter, r *http.Request)
}

type VCSEventMessageProcessor struct {
PostHandler VCSPostHandler
}

func (p *VCSEventMessageProcessor) ProcessMessage(msg types.Message) error {
if msg.Body == nil {
return errors.New("message received from sqs has no body")
}

buffer := bytes.NewBufferString(*msg.Body)
buf := bufio.NewReader(buffer)
req, err := http.ReadRequest(buf)
if err != nil {
return errors.Wrap(err, "reading bytes from sqs into http request")
}

// using a no-op writer since we shouldn't send response back in worker mode
p.PostHandler.Post(&NoOpResponseWriter{}, req)
return nil
}

type VCSEventMessageProcessorStats struct {
Scope tally.Scope
VCSEventMessageProcessor
}

func (s *VCSEventMessageProcessorStats) ProcessMessage(msg types.Message) error {
successCount := s.Scope.Counter(Success)
errorCount := s.Scope.Counter(Error)

if err := s.VCSEventMessageProcessor.ProcessMessage(msg); err != nil {
errorCount.Inc(1)
return err
}
successCount.Inc(1)
return nil
}

type NoOpResponseWriter struct{}

func (n *NoOpResponseWriter) Header() http.Header {
return nil
}

func (n *NoOpResponseWriter) Write([]byte) (int, error) {
return 0, nil
}

func (n *NoOpResponseWriter) WriteHeader(statusCode int) {}
71 changes: 71 additions & 0 deletions server/lyft/aws/sqs/message_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package sqs_test

import (
"bytes"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/sqs/types"
. "github.com/petergtz/pegomock"
controller_mocks "github.com/runatlantis/atlantis/server/controllers/events/mocks"
"github.com/runatlantis/atlantis/server/controllers/events/mocks/matchers"
"github.com/runatlantis/atlantis/server/lyft/aws/sqs"
. "github.com/runatlantis/atlantis/testing"
"github.com/stretchr/testify/assert"
"github.com/uber-go/tally"
"net/http"
"net/url"

"testing"
)

func TestAtlantisMessageHandler_PostSuccess(t *testing.T) {
RegisterMockTestingT(t)
testScope := tally.NewTestScope("test", nil)
req := createExampleRequest(t)
mockPostHandler := controller_mocks.NewMockVCSPostHandler()
handler := &sqs.VCSEventMessageProcessorStats{
VCSEventMessageProcessor: sqs.VCSEventMessageProcessor{
PostHandler: mockPostHandler,
},
Scope: testScope,
}

err := handler.ProcessMessage(toSqsMessage(t, req))
assert.NoError(t, err)
mockPostHandler.VerifyWasCalledOnce().Post(matchers.AnyHttpResponseWriter(), matchers.AnyPtrToHttpRequest())
Assert(t, testScope.Snapshot().Counters()["test.success+"].Value() == 1, "message handler was successful")
}

func TestAtlantisMessageHandler_Error(t *testing.T) {
RegisterMockTestingT(t)
testScope := tally.NewTestScope("test", nil)
mockPostHandler := controller_mocks.NewMockVCSPostHandler()
handler := &sqs.VCSEventMessageProcessorStats{
VCSEventMessageProcessor: sqs.VCSEventMessageProcessor{
PostHandler: mockPostHandler,
},
Scope: testScope,
}
invalidMessage := types.Message{}
err := handler.ProcessMessage(invalidMessage)
assert.Error(t, err)
mockPostHandler.VerifyWasCalled(Never()).Post(matchers.AnyHttpResponseWriter(), matchers.AnyPtrToHttpRequest())
Assert(t, testScope.Snapshot().Counters()["test.error+"].Value() == 1, "message handler was not successful")
}

func toSqsMessage(t *testing.T, req *http.Request) types.Message {
buffer := bytes.NewBuffer([]byte{})
err := req.Write(buffer)
assert.NoError(t, err)
return types.Message{
Body: aws.String(string(buffer.Bytes())),
}
}

func createExampleRequest(t *testing.T) *http.Request {
url, err := url.Parse("http://www.atlantis.com")
assert.NoError(t, err)
req := &http.Request{
URL: url,
}
return req
}
33 changes: 33 additions & 0 deletions server/lyft/aws/sqs/mocks/matchers/types_message.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 688a360

Please sign in to comment.