Skip to content

Commit 8fb9678

Browse files
committed
Initial Open Source Commit
This existed as an internal project before, but the history contained access keys and the like. Currently at 1.3
0 parents  commit 8fb9678

15 files changed

+1253
-0
lines changed

.travis.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
language: go
2+
3+
go:
4+
- 1.6
5+
- 1.7
6+
- tip
7+
8+
before_script:
9+
- go get github.com/GeertJohan/fgt
10+
- go get github.com/golang/lint
11+
12+
script:
13+
- fgt gomft -l .
14+
- fgt golint ./..
15+
- go vet ./...
16+
- go test -v ./...
17+
18+
matrix:
19+
allow_failures:
20+
- go: tip

Changelog.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Scout Changes
2+
3+
1.3
4+
----------
5+
- Rewrite the SQS integration to the use the AWS SDK instead of goamz
6+
7+
1.2
8+
----------
9+
- Save jobs in Redis with the Sidekiq `retry` flag set to `true`
10+
11+
1.1
12+
----------
13+
14+
- Remove the `--quiet` flag in favor of `--log-level` which defaults to `INFO`
15+
- Move some of the more verbose logging to `DEBUG` level logs
16+
- Log full message body after parsing it
17+

LICENSE.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Copyright (c) 2016 Enova International
2+
3+
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:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
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.
8+

README.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Scout
2+
3+
Scout is a daemon for listening to a set of SNS topics and enqueuing anything it
4+
finds into sidekiq jobs. It's meant to extract processing of SQS from the rails
5+
apps that increasingly need to do so.
6+
7+
## Usage
8+
9+
```
10+
NAME:
11+
scout - SQS Listener
12+
Poll SQS queues specified in a config and enqueue Sidekiq jobs with the queue items.
13+
It gracefully stops when sent SIGTERM.
14+
15+
USAGE:
16+
scout [global options] command [command options] [arguments...]
17+
18+
VERSION:
19+
1.3
20+
21+
COMMANDS:
22+
help, h Shows a list of commands or help for one command
23+
24+
GLOBAL OPTIONS:
25+
--config FILE, -c FILE Load config from FILE, required
26+
--freq N, -f N Poll SQS every N milliseconds (default: 100)
27+
--log-level value, -l value Sets log level. Accepts one of: debug, info, warn, error
28+
--json, -j Log in json format
29+
--help, -h show help
30+
--version, -v print the version
31+
```
32+
33+
## Configuration
34+
35+
The configuration requires 3 distinct sets of information. It needs information
36+
about how to connect to redis to enqueue jobs, credentials to talk to AWS and
37+
read SQS, and a mapping from SNS topics to sidekiq worker classes in the
38+
application. The structure looks like this.
39+
40+
```yaml
41+
redis:
42+
host: "localhost:9000"
43+
namespace: "test"
44+
queue: "background"
45+
aws:
46+
access_key: "super"
47+
secret_key: "secret"
48+
region: "us-best"
49+
queue:
50+
name: "myapp_queue"
51+
topics:
52+
foo-topic: "FooWorker"
53+
bar-topic: "BazWorker"
54+
```
55+
56+
None of this information is actually an example of anything other than the
57+
strucure of the file, so if you copy paste it you'll probably be disappointed.
58+
59+
## Versioning
60+
61+
Scout uses tagged commits to be compatible with gopkg.in. To pin to version 1,
62+
you can import it as `gopkg.in/enova/scout.v1`. The "first" version is version
63+
1.3, since all other versions were before this project was made open source.
64+
Version 2 is possible at some point and may contain breaking changes, so pinning
65+
to version 1 is recommended unless you want to work with the bleeding edge.
66+
67+
## Development
68+
69+
To get set up make sure to run `go get -t -u ./...` to get all the dependencies.
70+
71+
### Testing
72+
73+
The normal test suite can be run as expected with go test. There are also two
74+
tagged files with expensive integration tests that require external services.
75+
They can be run as follows
76+
77+
```
78+
[FG-386] scout > go test -run=TestSQS -v -tags=sqsint
79+
=== RUN TestSQS_Init
80+
--- PASS: TestSQS_Init (3.84s)
81+
=== RUN TestSQS_FetchDelete
82+
--- PASS: TestSQS_FetchDelete (3.58s)
83+
PASS
84+
ok github.com/enova/scout 7.422s
85+
[FG-386] scout > go test -run=TestWorker -v -tags=redisint
86+
=== RUN TestWorker_Init
87+
--- PASS: TestWorker_Init (0.00s)
88+
=== RUN TestWorker_Push
89+
--- PASS: TestWorker_Push (0.00s)
90+
PASS
91+
ok github.com/enova/scout 0.013s
92+
```
93+
94+
The tests themselves (found in `sqs_client_test.go` and `worker_client_test.go`)
95+
explain what is required to run them. In particular, the SQS integration tests
96+
require that you provide AWS credentials to run them.

config.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package main
2+
3+
import (
4+
"io/ioutil"
5+
6+
"gopkg.in/yaml.v2"
7+
)
8+
9+
// Config is the internal representation of the yaml that determines what
10+
// the app listens to an enqueues
11+
type Config struct {
12+
Redis RedisConfig `yaml:"redis"`
13+
AWS AWSConfig `yaml:"aws"`
14+
Queue QueueConfig `yaml:"queue"`
15+
}
16+
17+
// RedisConfig is a nested config that contains the necessary parameters to
18+
// connect to a redis instance and enqueue workers.
19+
type RedisConfig struct {
20+
Host string `yaml:"host"`
21+
Namespace string `yaml:"namespace"`
22+
Queue string `yaml:"queue"`
23+
}
24+
25+
// AWSConfig is a nested config that contains the necessary parameters to
26+
// connect to AWS and read from SQS
27+
type AWSConfig struct {
28+
AccessKey string `yaml:"access_key"`
29+
SecretKey string `yaml:"secret_key"`
30+
Region string `yaml:"region"`
31+
}
32+
33+
// QueueConfig is a nested config that gives the SQS queue to listen on
34+
// and a mapping of topics to workeers
35+
type QueueConfig struct {
36+
Name string `yaml:"name"`
37+
Topics map[string]string `yaml:"topics"`
38+
}
39+
40+
// ReadConfig reads from a file with the given name and returns a config or
41+
// an error if the file was unable to be parsed. It does no error checking
42+
// as far as required fields.
43+
func ReadConfig(file string) (*Config, error) {
44+
data, err := ioutil.ReadFile(file)
45+
if err != nil {
46+
return nil, err
47+
}
48+
49+
config := new(Config)
50+
51+
err = yaml.Unmarshal(data, config)
52+
return config, err
53+
}

config_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package main
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
"github.com/stretchr/testify/suite"
10+
)
11+
12+
func TestConfig(t *testing.T) {
13+
suite.Run(t, new(ConfigTestSuite))
14+
}
15+
16+
type ConfigTestSuite struct {
17+
suite.Suite
18+
tempfile *os.File
19+
assert *require.Assertions
20+
}
21+
22+
func (c *ConfigTestSuite) SetupTest() {
23+
c.assert = require.New(c.T())
24+
25+
var err error
26+
c.tempfile, err = ioutil.TempFile("", "config")
27+
c.assert.NoError(err)
28+
}
29+
30+
func (c *ConfigTestSuite) TearDownTest() {
31+
os.Remove(c.tempfile.Name())
32+
}
33+
34+
func (c *ConfigTestSuite) WriteTemp(content string) {
35+
_, err := c.tempfile.Write([]byte(content))
36+
c.assert.NoError(err)
37+
ReadConfig(c.tempfile.Name())
38+
err = c.tempfile.Close()
39+
c.assert.NoError(err)
40+
}
41+
42+
var validConfig = `
43+
redis:
44+
host: "localhost:9000"
45+
namespace: "test"
46+
queue: "background"
47+
aws:
48+
access_key: "super"
49+
secret_key: "secret"
50+
region: "us_best"
51+
queue:
52+
name: "myapp_queue"
53+
topics:
54+
foo_topic: "FooWorker"
55+
bar_topic: "BazWorker"`
56+
57+
func (c *ConfigTestSuite) TestConfig_Valid() {
58+
c.WriteTemp(validConfig)
59+
config, err := ReadConfig(c.tempfile.Name())
60+
c.assert.NoError(err)
61+
62+
// More to convince myself that the yaml package works than anything
63+
c.assert.Equal(config.Redis.Host, "localhost:9000")
64+
c.assert.Equal(config.Redis.Queue, "background")
65+
c.assert.Equal(config.AWS.Region, "us_best")
66+
c.assert.Equal(config.Queue.Name, "myapp_queue")
67+
c.assert.Equal(config.Queue.Topics["foo_topic"], "FooWorker")
68+
}
69+
70+
var sparseConfig = `
71+
redis:
72+
host: "localhost:9000"
73+
aws:
74+
access_key: "super"
75+
secret_key: "secret"
76+
region: "us_best"`
77+
78+
// It's ok for stuff to be missing, we'll check that elsewhere
79+
func (c *ConfigTestSuite) TestConfig_Sparse() {
80+
c.WriteTemp(sparseConfig)
81+
config, err := ReadConfig(c.tempfile.Name())
82+
c.assert.NoError(err)
83+
84+
c.assert.Equal(config.Redis.Namespace, "")
85+
c.assert.Equal(config.AWS.Region, "us_best")
86+
c.assert.Equal(len(config.Queue.Topics), 0)
87+
}

0 commit comments

Comments
 (0)