├── .sonarcloud.properties ├── config.yml ├── Dockerfile ├── vendor ├── github.com │ ├── BurntSushi │ │ └── toml │ │ │ ├── session.vim │ │ │ ├── .gitignore │ │ │ ├── COMPATIBLE │ │ │ ├── .travis.yml │ │ │ ├── Makefile │ │ │ ├── COPYING │ │ │ ├── encoding_types_1.1.go │ │ │ ├── encoding_types.go │ │ │ ├── doc.go │ │ │ ├── type_check.go │ │ │ ├── decode_meta.go │ │ │ └── README.md │ ├── op │ │ └── go-logging │ │ │ ├── .travis.yml │ │ │ ├── CONTRIBUTORS │ │ │ ├── CHANGELOG.md │ │ │ ├── syslog_fallback.go │ │ │ ├── backend.go │ │ │ ├── LICENSE │ │ │ ├── syslog.go │ │ │ ├── multi.go │ │ │ ├── log_nix.go │ │ │ ├── README.md │ │ │ ├── log_windows.go │ │ │ ├── level.go │ │ │ └── memory.go │ ├── stretchr │ │ ├── objx │ │ │ ├── README.md │ │ │ ├── security.go │ │ │ ├── .gitignore │ │ │ ├── constants.go │ │ │ ├── tests.go │ │ │ ├── LICENSE.md │ │ │ ├── value.go │ │ │ ├── mutations.go │ │ │ ├── doc.go │ │ │ ├── conversions.go │ │ │ ├── accessors.go │ │ │ └── map.go │ │ └── testify │ │ │ ├── assert │ │ │ ├── assertion_forward.go.tmpl │ │ │ ├── errors.go │ │ │ ├── forward_assertions.go │ │ │ ├── doc.go │ │ │ └── http_assertions.go │ │ │ ├── LICENSE │ │ │ ├── LICENCE.txt │ │ │ └── mock │ │ │ └── doc.go │ ├── satori │ │ └── go.uuid │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ └── README.md │ ├── davecgh │ │ └── go-spew │ │ │ ├── LICENSE │ │ │ └── spew │ │ │ ├── bypasssafe.go │ │ │ ├── bypass.go │ │ │ └── spew.go │ ├── pmezard │ │ └── go-difflib │ │ │ └── LICENSE │ └── jinzhu │ │ └── configor │ │ ├── configor.go │ │ ├── README.md │ │ └── utils.go └── gopkg.in │ └── yaml.v1 │ ├── LICENSE.libyaml │ ├── writerc.go │ ├── sorter.go │ ├── README.md │ ├── resolve.go │ ├── yamlprivateh.go │ └── encode.go ├── .circleci ├── images │ └── primary │ │ └── Dockerfile └── config.yml ├── Godeps ├── Readme └── Godeps.json ├── testintegration ├── testintegration_suite_test.go ├── put_test.go └── util.go ├── tests ├── package.json ├── kick_job.js ├── kick.js ├── restarting_producer.js ├── producer.js ├── failure_worker.js ├── restarting_worker.js ├── worker.js └── timeout_worker.js ├── .gitignore ├── backend ├── backends.go ├── min_heap_test.go └── min_heap.go ├── config.go ├── architecture ├── protocol_test.go ├── job_state.go ├── job_test.go ├── mock_priority_queue.go ├── protocol_config.go ├── tube_test.go ├── job.go └── protocol.go ├── hooks └── pre-commit ├── plan.md ├── LICENSE ├── operation ├── tube_handler.go ├── tube_register.go └── client_handler.go ├── main.go └── Readme.md /.sonarcloud.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /config.yml: -------------------------------------------------------------------------------- 1 | port: 11300 2 | debug: false 3 | backend: minheap 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centurylink/ca-certs 2 | ADD beanstalkg / 3 | EXPOSE 11300 4 | CMD ["/beanstalkg"] 5 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/session.vim: -------------------------------------------------------------------------------- 1 | au BufWritePost *.go silent!make tags > /dev/null 2>&1 2 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/.gitignore: -------------------------------------------------------------------------------- 1 | TAGS 2 | tags 3 | .*.swp 4 | tomlcheck/tomlcheck 5 | toml.test 6 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.0 5 | - 1.1 6 | - tip 7 | -------------------------------------------------------------------------------- /.circleci/images/primary/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.8.0-stretch 2 | RUN go get github.com/jstemmer/go-junit-report 3 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/README.md: -------------------------------------------------------------------------------- 1 | # objx 2 | 3 | * Jump into the [API Documentation](http://godoc.org/github.com/stretchr/objx) 4 | -------------------------------------------------------------------------------- /Godeps/Readme: -------------------------------------------------------------------------------- 1 | This directory tree is generated automatically by godep. 2 | 3 | Please do not edit. 4 | 5 | See https://github.com/tools/godep for more information. 6 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/COMPATIBLE: -------------------------------------------------------------------------------- 1 | Compatible with TOML version 2 | [v0.4.0](https://github.com/toml-lang/toml/blob/v0.4.0/versions/en/toml-v0.4.0.md) 3 | 4 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl: -------------------------------------------------------------------------------- 1 | {{.CommentWithoutT "a"}} 2 | func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { 3 | return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) 4 | } 5 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | Alec Thomas 2 | Guilhem Lettron 3 | Ivan Daniluk 4 | Nimi Wariboko Jr 5 | Róbert Selvek 6 | -------------------------------------------------------------------------------- /testintegration/testintegration_suite_test.go: -------------------------------------------------------------------------------- 1 | package testintegration 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestTestintegration(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Testintegration Suite") 13 | } 14 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.1 4 | - 1.2 5 | - 1.3 6 | - 1.4 7 | - 1.5 8 | - 1.6 9 | - tip 10 | install: 11 | - go install ./... 12 | - go get github.com/BurntSushi/toml-test 13 | script: 14 | - export PATH="$PATH:$HOME/gopath/bin" 15 | - make test 16 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tests", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "producer.js", 6 | "scripts": { 7 | "test": "test" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "bluebird": "^3.4.6", 13 | "co": "^4.6.0", 14 | "fivebeans": "^1.5.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/security.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/hex" 6 | ) 7 | 8 | // HashWithKey hashes the specified string using the security 9 | // key. 10 | func HashWithKey(data, key string) string { 11 | hash := sha1.New() 12 | hash.Write([]byte(data + ":" + key)) 13 | return hex.EncodeToString(hash.Sum(nil)) 14 | } 15 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | go install ./... 3 | 4 | test: install 5 | go test -v 6 | toml-test toml-test-decoder 7 | toml-test -encoder toml-test-encoder 8 | 9 | fmt: 10 | gofmt -w *.go */*.go 11 | colcheck *.go */*.go 12 | 13 | tags: 14 | find ./ -name '*.go' -print0 | xargs -0 gotags > TAGS 15 | 16 | push: 17 | git push origin master 18 | git push github master 19 | 20 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/errors.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // AnError is an error instance useful for testing. If the code does not care 8 | // about error specifics, and only needs to return the error for example, this 9 | // error should be used to make the test code more readable. 10 | var AnError = errors.New("assert.AnError general error for testing") 11 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/constants.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | const ( 4 | // PathSeparator is the character used to separate the elements 5 | // of the keypath. 6 | // 7 | // For example, `location.address.city` 8 | PathSeparator string = "." 9 | 10 | // SignatureSeparator is the character that is used to 11 | // separate the Base64 string from the security signature. 12 | SignatureSeparator = "_" 13 | ) 14 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/tests.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | // Has gets whether there is something at the specified selector 4 | // or not. 5 | // 6 | // If m is nil, Has will always return false. 7 | func (m Map) Has(selector string) bool { 8 | if m == nil { 9 | return false 10 | } 11 | return !m.Get(selector).IsNil() 12 | } 13 | 14 | // IsNil gets whether the data is nil or not. 15 | func (v *Value) IsNil() bool { 16 | return v == nil || v.data == nil 17 | } 18 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/forward_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | // Assertions provides assertion methods around the 4 | // TestingT interface. 5 | type Assertions struct { 6 | t TestingT 7 | } 8 | 9 | // New makes a new Assertions object for the specified TestingT. 10 | func New(t TestingT) *Assertions { 11 | return &Assertions{ 12 | t: t, 13 | } 14 | } 15 | 16 | //go:generate go run ../_codegen/main.go -output-package=assert -template=assertion_forward.go.tmpl 17 | -------------------------------------------------------------------------------- /vendor/github.com/satori/go.uuid/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | - 1.2 5 | - 1.3 6 | - 1.4 7 | - 1.5 8 | - 1.6 9 | - 1.7 10 | - 1.8 11 | - tip 12 | matrix: 13 | allow_failures: 14 | - go: tip 15 | fast_finish: true 16 | before_install: 17 | - go get github.com/mattn/goveralls 18 | - go get golang.org/x/tools/cmd/cover 19 | script: 20 | - $HOME/gopath/bin/goveralls -service=travis-ci 21 | notifications: 22 | email: false 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Go template 3 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 4 | *.o 5 | *.a 6 | *.so 7 | 8 | # Folders 9 | _obj 10 | _test 11 | 12 | # Architecture specific extensions/prefixes 13 | *.[568vq] 14 | [568vq].out 15 | 16 | *.cgo1.go 17 | *.cgo2.c 18 | _cgo_defun.c 19 | _cgo_gotypes.go 20 | _cgo_export.* 21 | 22 | _testmain.go 23 | 24 | *.exe 25 | *.test 26 | *.prof 27 | 28 | .idea 29 | *.iml 30 | 31 | **/node_modules 32 | 33 | beanstalkg 34 | 35 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/COPYING: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/encoding_types_1.1.go: -------------------------------------------------------------------------------- 1 | // +build !go1.2 2 | 3 | package toml 4 | 5 | // These interfaces were introduced in Go 1.2, so we add them manually when 6 | // compiling for Go 1.1. 7 | 8 | // TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here 9 | // so that Go 1.1 can be supported. 10 | type TextMarshaler interface { 11 | MarshalText() (text []byte, err error) 12 | } 13 | 14 | // TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined 15 | // here so that Go 1.1 can be supported. 16 | type TextUnmarshaler interface { 17 | UnmarshalText(text []byte) error 18 | } 19 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/encoding_types.go: -------------------------------------------------------------------------------- 1 | // +build go1.2 2 | 3 | package toml 4 | 5 | // In order to support Go 1.1, we define our own TextMarshaler and 6 | // TextUnmarshaler types. For Go 1.2+, we just alias them with the 7 | // standard library interfaces. 8 | 9 | import ( 10 | "encoding" 11 | ) 12 | 13 | // TextMarshaler is a synonym for encoding.TextMarshaler. It is defined here 14 | // so that Go 1.1 can be supported. 15 | type TextMarshaler encoding.TextMarshaler 16 | 17 | // TextUnmarshaler is a synonym for encoding.TextUnmarshaler. It is defined 18 | // here so that Go 1.1 can be supported. 19 | type TextUnmarshaler encoding.TextUnmarshaler 20 | -------------------------------------------------------------------------------- /tests/kick_job.js: -------------------------------------------------------------------------------- 1 | var fivebeans = require('fivebeans'); 2 | 3 | var args = process.argv.slice(2); 4 | var client = new fivebeans.client('localhost', 11300); 5 | client 6 | .on('connect', function() 7 | { 8 | console.log("connected"); 9 | client.use("test", function(err, tubename) { 10 | if (err == null) { 11 | client.kick_job(args[0], function(err, jobid) { 12 | console.log("kicking job ", args[0]); 13 | process.exit(); 14 | }); 15 | } 16 | }); 17 | }) 18 | .on('error', function(err) 19 | { 20 | // connection failure 21 | }) 22 | .on('close', function() 23 | { 24 | // underlying connection has closed 25 | }) 26 | .connect(); 27 | -------------------------------------------------------------------------------- /tests/kick.js: -------------------------------------------------------------------------------- 1 | var fivebeans = require('fivebeans'); 2 | 3 | var args = process.argv.slice(2); 4 | var client = new fivebeans.client('localhost', 11300); 5 | client 6 | .on('connect', function() 7 | { 8 | console.log("connected"); 9 | client.use("test", function(err, tubename) { 10 | if (err == null) { 11 | client.kick(parseInt(args[0]), function(err, jobid) { 12 | console.log("kicking ", args[0], "jobs"); 13 | process.exit(); 14 | }); 15 | } 16 | }); 17 | }) 18 | .on('error', function(err) 19 | { 20 | // connection failure 21 | }) 22 | .on('close', function() 23 | { 24 | // underlying connection has closed 25 | }) 26 | .connect(); 27 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 2.0.0-rc1 (2016-02-11) 4 | 5 | Time flies and it has been three years since this package was first released. 6 | There have been a couple of API changes I have wanted to do for some time but 7 | I've tried to maintain backwards compatibility. Some inconsistencies in the 8 | API have started to show, proper vendor support in Go out of the box and 9 | the fact that `go vet` will give warnings -- I have decided to bump the major 10 | version. 11 | 12 | * Make eg. `Info` and `Infof` do different things. You want to change all calls 13 | to `Info` with a string format go to `Infof` etc. In many cases, `go vet` will 14 | guide you. 15 | * `Id` in `Record` is now called `ID` 16 | 17 | ## 1.0.0 (2013-02-21) 18 | 19 | Initial release 20 | -------------------------------------------------------------------------------- /backend/backends.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "github.com/beanstalkg/beanstalkg/architecture" 5 | ) 6 | 7 | const defaultBackend = "minheap" 8 | 9 | var validBackends map[string]architecture.PriorityQueueCreator = map[string]architecture.PriorityQueueCreator{ 10 | "minheap": func() architecture.PriorityQueue { return &MinHeap{} }, 11 | } 12 | 13 | // QueueCreator retrieves the PriorityQueueCreator that returns a 14 | // PriorityQueue for the specified backend. Falls back to 15 | // defaultBackend if the requested backend is invalid. 16 | func QueueCreator(backend string) architecture.PriorityQueueCreator { 17 | if _, ok := validBackends[backend]; !ok { 18 | log.Debugf("%s backend not supported, falling back to %s.", backend, defaultBackend) 19 | backend = defaultBackend 20 | } 21 | 22 | return validBackends[backend] 23 | } 24 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2012-2016 Dave Collins 4 | 5 | Permission to use, copy, modify, and distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/syslog_fallback.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, Örjan Persson. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //+build windows plan9 6 | 7 | package logging 8 | 9 | import ( 10 | "fmt" 11 | ) 12 | 13 | type Priority int 14 | 15 | type SyslogBackend struct { 16 | } 17 | 18 | func NewSyslogBackend(prefix string) (b *SyslogBackend, err error) { 19 | return nil, fmt.Errorf("Platform does not support syslog") 20 | } 21 | 22 | func NewSyslogBackendPriority(prefix string, priority Priority) (b *SyslogBackend, err error) { 23 | return nil, fmt.Errorf("Platform does not support syslog") 24 | } 25 | 26 | func (b *SyslogBackend) Log(level Level, calldepth int, rec *Record) error { 27 | return fmt.Errorf("Platform does not support syslog") 28 | } 29 | -------------------------------------------------------------------------------- /tests/restarting_producer.js: -------------------------------------------------------------------------------- 1 | var fivebeans = require('fivebeans'); 2 | 3 | setInterval(function () { 4 | var client = new fivebeans.client('localhost', 11300); 5 | client 6 | .on('connect', function() 7 | { 8 | console.log("connected"); 9 | client.use("test", function(err, tubename) { 10 | if (err == null) { 11 | client.put(1, 0, 1, "payload", function(err, jobid) { 12 | console.log("put job id", jobid); 13 | }); 14 | } 15 | }); 16 | client.close(); 17 | }) 18 | .on('error', function(err) 19 | { 20 | // connection failure 21 | }) 22 | .on('close', function() 23 | { 24 | // underlying connection has closed 25 | }) 26 | .connect(); 27 | }, 100); -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/jinzhu/configor" 7 | ) 8 | 9 | type ServerConfig struct { 10 | Port string `default:"11300"` 11 | Debug bool 12 | Backend string `default:"minheap"` 13 | } 14 | 15 | // getConfig sets values based on the following order of precedence: 16 | // flags, environment variables, configuration files, and finally 17 | // defaults. 18 | func getConfig() *ServerConfig { 19 | cfg := &ServerConfig{} 20 | configor.New(&configor.Config{ENVPrefix: "BEANSTALKG"}).Load(cfg, "config.yml") 21 | 22 | flag.StringVar(&cfg.Port, "port", cfg.Port, "Port for beanstalkg server") 23 | flag.StringVar(&cfg.Backend, "backend", cfg.Backend, "Use this backend for job storage.") 24 | flag.BoolVar(&cfg.Debug, "debug", cfg.Debug, "Start server in debug mode. Logs shall contain more information") 25 | flag.Parse() 26 | 27 | return cfg 28 | } 29 | -------------------------------------------------------------------------------- /tests/producer.js: -------------------------------------------------------------------------------- 1 | var fivebeans = require('fivebeans'); 2 | 3 | var client = new fivebeans.client('localhost', 11300); 4 | client 5 | .on('connect', function() 6 | { 7 | console.log("connected"); 8 | client.use("test", function(err, tubename) { 9 | if (err == null) { 10 | //setInterval(function() { 11 | // priority, delay, ttr, payload 12 | client.put(1, 20, 5, JSON.stringify({"number": 1}), function(err, jobid) { 13 | console.log("put job id", jobid); 14 | process.exit(); 15 | }); 16 | //}, 100); 17 | } 18 | 19 | }); 20 | 21 | }) 22 | .on('error', function(err) 23 | { 24 | // connection failure 25 | }) 26 | .on('close', function() 27 | { 28 | // underlying connection has closed 29 | }) 30 | .connect(); 31 | -------------------------------------------------------------------------------- /architecture/protocol_test.go: -------------------------------------------------------------------------------- 1 | package architecture 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestCommand_Parse(t *testing.T) { 9 | c := Command{} 10 | c.Parse("use test") 11 | fmt.Println(c) 12 | if c.RawCommand != "use test" { 13 | fmt.Println("First") 14 | t.Fail() 15 | } 16 | if c.Name != "use" { 17 | fmt.Println("Second") 18 | t.Fail() 19 | } 20 | 21 | c = Command{} 22 | done, err := c.Parse("put 1 2 3 4") 23 | 24 | if err != nil { 25 | fmt.Println("Third") 26 | t.Fail() 27 | } 28 | 29 | if done { 30 | fmt.Println("Fourth") 31 | t.Fail() 32 | } 33 | 34 | if c.Name != "put" || c.Params["ttr"] != "3" { 35 | fmt.Println("Fifth") 36 | t.Fail() 37 | } 38 | 39 | c.Parse("data") 40 | 41 | if c.Params["data"] != "data" { 42 | fmt.Println("Sixth") 43 | t.Fail() 44 | } 45 | 46 | c = Command{} 47 | kicked, err := c.Parse("kick 4") 48 | if err != nil { 49 | t.Fail() 50 | } 51 | if !kicked { 52 | fmt.Println("failed to kicked") 53 | t.Fail() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | readonly reset=$(tput sgr0) 4 | readonly red=$(tput bold; tput setaf 1) 5 | readonly green=$(tput bold; tput setaf 2) 6 | 7 | exit_code=0 8 | 9 | echo -ne "Checking for files that need gofmt... " 10 | files_need_gofmt=() 11 | files=($(git diff --cached --name-only --diff-filter ACM | grep "\.go" | grep -v -e "^third_party" -e "^vendor")) 12 | for file in "${files[@]}"; do 13 | # Check for files that fail gofmt. 14 | diff="$(git show ":${file}" | gofmt -s -d 2>&1)" 15 | if [[ -n "$diff" ]]; then 16 | files_need_gofmt+=("${file}") 17 | fi 18 | done 19 | 20 | if [[ "${#files_need_gofmt[@]}" -ne 0 ]]; then 21 | echo "${red}ERROR!" 22 | echo "Some files have not been gofmt'd. To fix these errors, " 23 | echo "copy and paste the following:" 24 | echo " gofmt -s -w ${files_need_gofmt[@]}" 25 | exit_code=1 26 | else 27 | echo "${green}OK" 28 | fi 29 | echo "${reset}" 30 | 31 | if [[ "${exit_code}" != 0 ]]; then 32 | echo "${red}Aborting commit${reset}" 33 | fi 34 | exit ${exit_code} 35 | 36 | # ex: ts=2 sw=2 et filetype=sh -------------------------------------------------------------------------------- /plan.md: -------------------------------------------------------------------------------- 1 | ## Implementation 2 | 3 | We can follow three main approaches to support high availability and failover. 4 | 5 | - Dumb Proxy Approach - This is to support an existing beanstalk server scale to load and failover easily. Beanstalkg will act as a proxy to 6 | multiple beanstalkd servers allowing their state to be replicated. Master will be elected based on configuration. This approach requires 7 | the proxy to keep long running connections with all the beanstalks for each client that connects to it. 8 | - Simpler/Less Performance/Expensive Approach - Save all job state to a backing store such as dynamodb/mongodb/mysql that support high availability. Then we can use 9 | this store to coordinate the multi beanstalkg server setup. 10 | - Complex/High Peformance/Cheaper Approach - Make all beanstalkg servers act as proxies to each other while using some algorithm such as Raft to coordinate leadership and failover. 11 | Once a leader has been elected all the operations will be verified with it before execution. This might require the priority queue to be implemented with a merge-able heap. 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2017 The Beanstalkg Authors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /architecture/job_state.go: -------------------------------------------------------------------------------- 1 | package architecture 2 | 3 | import "errors" 4 | 5 | type updateFunc func(*Job, int64) 6 | 7 | // helper functions for updating timers. 8 | func updateTTR(job *Job, t int64) { 9 | job.StartedTTRAt = t 10 | } 11 | 12 | func updateDelayed(job *Job, t int64) { 13 | job.StartedDelayAt = t 14 | } 15 | 16 | // Describes valid transitions to a given state. 17 | // key = to state, value = valid from states. 18 | var validTransitionsTo = map[State]map[State]bool{ 19 | READY: map[State]bool{RESERVED: true, DELAYED: true, BURIED: true}, 20 | DELAYED: map[State]bool{RESERVED: true}, 21 | RESERVED: map[State]bool{READY: true}, 22 | BURIED: map[State]bool{RESERVED: true}, 23 | } 24 | 25 | var transitionErrors = map[State]error{ 26 | READY: errors.New("Invalid state transition to READY"), 27 | DELAYED: errors.New("Invalid state transition to DELAYED"), 28 | RESERVED: errors.New("Invalid state transition to RESERVED"), 29 | BURIED: errors.New("Invalid state transition to BURIED"), 30 | } 31 | 32 | var updateFuncs = map[State]updateFunc{ 33 | DELAYED: updateTTR, 34 | RESERVED: updateDelayed, 35 | } 36 | -------------------------------------------------------------------------------- /tests/failure_worker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fivebeans = require('fivebeans'); 4 | var co = require('co'); 5 | var bb = require('bluebird'); 6 | 7 | var client = new fivebeans.client('localhost', 11300); 8 | bb.promisifyAll(client, {multiArgs: true}); 9 | 10 | client.on('connect', function () { 11 | co(function* () { 12 | console.log("connected"); 13 | yield client.watchAsync("test"); 14 | yield client.ignoreAsync("default"); 15 | yield client.useAsync("test"); 16 | yield doit(client); 17 | }).catch(function(e) { 18 | console.log(e); 19 | }); 20 | }); 21 | 22 | client.connect(); 23 | 24 | function doit(client) { 25 | return co(function* () { 26 | var res = yield client.reserveAsync(); 27 | console.log(res[1].toString()); 28 | var ob = JSON.parse(res[1].toString()); 29 | ob.number = ob.number + 1; 30 | if (ob.number < 4) { 31 | yield client.destroyAsync(res[0]); 32 | yield client.putAsync(1, ob.number, 5, JSON.stringify(ob)); 33 | } else { 34 | yield client.buryAsync(res[0], 1); 35 | } 36 | setTimeout(function() {doit(client)}, 1); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /tests/restarting_worker.js: -------------------------------------------------------------------------------- 1 | var fivebeans = require('fivebeans'); 2 | 3 | setInterval(function () { 4 | var client = new fivebeans.client('localhost', 11300); 5 | client 6 | .on('connect', function() 7 | { 8 | console.log("connected"); 9 | client.watch("test", function(err, numwatched) { 10 | if (err == null) { 11 | client.ignore("default", function(err, numwatched) { 12 | if (err == null) { 13 | client.reserve(function(err, jobid, payload) { 14 | console.log("reserved job id", jobid, payload); 15 | }); 16 | } 17 | }); 18 | } 19 | }); 20 | }) 21 | .on('error', function(err) 22 | { 23 | // connection failure 24 | }) 25 | .on('close', function() 26 | { 27 | // underlying connection has closed 28 | }) 29 | .connect(); 30 | }, 1000); -------------------------------------------------------------------------------- /vendor/github.com/satori/go.uuid/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013-2016 by Maxim Bublis 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /tests/worker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fivebeans = require('fivebeans'); 4 | var co = require('co'); 5 | var bb = require('bluebird'); 6 | 7 | var client = new fivebeans.client('localhost', 11300); 8 | bb.promisifyAll(client, {multiArgs: true}); 9 | 10 | client.on('connect', function () { 11 | co(function* () { 12 | console.log("connected"); 13 | yield client.watchAsync("test"); 14 | yield client.ignoreAsync("default"); 15 | yield client.useAsync("test"); 16 | yield doit(client); 17 | 18 | }).catch(function(e) { 19 | console.log(e); 20 | }); 21 | }); 22 | 23 | client.connect(); 24 | 25 | 26 | 27 | 28 | 29 | function doit(client) { 30 | return co(function* () { 31 | var res = yield client.reserveAsync(); 32 | console.log(res[1].toString()); 33 | var ob = JSON.parse(res[1].toString()); 34 | ob.number = ob.number + 1; 35 | yield client.destroyAsync(res[0]); 36 | client.putAsync(1, ob.number, 5, JSON.stringify(ob)); 37 | setTimeout(function() {doit(client)}, 1); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /tests/timeout_worker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fivebeans = require('fivebeans'); 4 | var co = require('co'); 5 | var bb = require('bluebird'); 6 | 7 | var client = new fivebeans.client('localhost', 11300); 8 | bb.promisifyAll(client, {multiArgs: true}); 9 | 10 | client.on('connect', function () { 11 | co(function* () { 12 | console.log("connected"); 13 | yield client.watchAsync("test"); 14 | yield client.ignoreAsync("default"); 15 | yield client.useAsync("test"); 16 | yield doit(client); 17 | 18 | }).catch(function(e) { 19 | console.log(e); 20 | }); 21 | }); 22 | 23 | client.connect(); 24 | 25 | 26 | 27 | 28 | 29 | function doit(client) { 30 | return co(function* () { 31 | var res = yield client.reserve_with_timeoutAsync(10); 32 | console.log(res[1].toString()); 33 | var ob = JSON.parse(res[1].toString()); 34 | ob.number = ob.number + 1; 35 | yield client.destroyAsync(res[0]); 36 | client.putAsync(1, ob.number, 5, JSON.stringify(ob)); 37 | setTimeout(function() {doit(client)}, 1); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/LICENSE.md: -------------------------------------------------------------------------------- 1 | objx - by Mat Ryer and Tyler Bunnell 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2014 Stretchr, Inc. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package toml provides facilities for decoding and encoding TOML configuration 3 | files via reflection. There is also support for delaying decoding with 4 | the Primitive type, and querying the set of keys in a TOML document with the 5 | MetaData type. 6 | 7 | The specification implemented: https://github.com/toml-lang/toml 8 | 9 | The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify 10 | whether a file is a valid TOML document. It can also be used to print the 11 | type of each key in a TOML document. 12 | 13 | Testing 14 | 15 | There are two important types of tests used for this package. The first is 16 | contained inside '*_test.go' files and uses the standard Go unit testing 17 | framework. These tests are primarily devoted to holistically testing the 18 | decoder and encoder. 19 | 20 | The second type of testing is used to verify the implementation's adherence 21 | to the TOML specification. These tests have been factored into their own 22 | project: https://github.com/BurntSushi/toml-test 23 | 24 | The reason the tests are in a separate project is so that they can be used by 25 | any implementation of TOML. Namely, it is language agnostic. 26 | */ 27 | package toml 28 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell 2 | 3 | Please consider promoting this project if you find it useful. 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 21 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 22 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - 2013 Mat Ryer and Tyler Bunnell 2 | 3 | Please consider promoting this project if you find it useful. 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated documentation 7 | files (the "Software"), to deal in the Software without restriction, 8 | including without limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of the Software, 10 | and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 21 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 22 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/backend.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, Örjan Persson. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package logging 6 | 7 | // defaultBackend is the backend used for all logging calls. 8 | var defaultBackend LeveledBackend 9 | 10 | // Backend is the interface which a log backend need to implement to be able to 11 | // be used as a logging backend. 12 | type Backend interface { 13 | Log(Level, int, *Record) error 14 | } 15 | 16 | // SetBackend replaces the backend currently set with the given new logging 17 | // backend. 18 | func SetBackend(backends ...Backend) LeveledBackend { 19 | var backend Backend 20 | if len(backends) == 1 { 21 | backend = backends[0] 22 | } else { 23 | backend = MultiLogger(backends...) 24 | } 25 | 26 | defaultBackend = AddModuleLevel(backend) 27 | return defaultBackend 28 | } 29 | 30 | // SetLevel sets the logging level for the specified module. The module 31 | // corresponds to the string specified in GetLogger. 32 | func SetLevel(level Level, module string) { 33 | defaultBackend.SetLevel(level, module) 34 | } 35 | 36 | // GetLevel returns the logging level for the specified module. 37 | func GetLevel(module string) Level { 38 | return defaultBackend.GetLevel(module) 39 | } 40 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v1/LICENSE.libyaml: -------------------------------------------------------------------------------- 1 | The following files were ported to Go from C files of libyaml, and thus 2 | are still covered by their original copyright and license: 3 | 4 | apic.go 5 | emitterc.go 6 | parserc.go 7 | readerc.go 8 | scannerc.go 9 | writerc.go 10 | yamlh.go 11 | yamlprivateh.go 12 | 13 | Copyright (c) 2006 Kirill Simonov 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy of 16 | this software and associated documentation files (the "Software"), to deal in 17 | the Software without restriction, including without limitation the rights to 18 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 19 | of the Software, and to permit persons to whom the Software is furnished to do 20 | so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | SOFTWARE. 32 | -------------------------------------------------------------------------------- /vendor/github.com/pmezard/go-difflib/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Patrick Mezard 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | The names of its contributors may not be used to endorse or promote 14 | products derived from this software without specific prior written 15 | permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Örjan Persson. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/doc.go: -------------------------------------------------------------------------------- 1 | // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. 2 | // 3 | // Example Usage 4 | // 5 | // The following is a complete example using assert in a standard test function: 6 | // import ( 7 | // "testing" 8 | // "github.com/stretchr/testify/assert" 9 | // ) 10 | // 11 | // func TestSomething(t *testing.T) { 12 | // 13 | // var a string = "Hello" 14 | // var b string = "Hello" 15 | // 16 | // assert.Equal(t, a, b, "The two words should be the same.") 17 | // 18 | // } 19 | // 20 | // if you assert many times, use the format below: 21 | // 22 | // import ( 23 | // "testing" 24 | // "github.com/stretchr/testify/assert" 25 | // ) 26 | // 27 | // func TestSomething(t *testing.T) { 28 | // assert := assert.New(t) 29 | // 30 | // var a string = "Hello" 31 | // var b string = "Hello" 32 | // 33 | // assert.Equal(a, b, "The two words should be the same.") 34 | // } 35 | // 36 | // Assertions 37 | // 38 | // Assertions allow you to easily write test code, and are global funcs in the `assert` package. 39 | // All assertion functions take, as the first argument, the `*testing.T` object provided by the 40 | // testing framework. This allows the assertion funcs to write the failings and other details to 41 | // the correct place. 42 | // 43 | // Every assertion function also takes an optional string message as the final argument, 44 | // allowing custom error messages to be appended to the message the assertion method outputs. 45 | package assert 46 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/configor/configor.go: -------------------------------------------------------------------------------- 1 | package configor 2 | 3 | import ( 4 | "os" 5 | "regexp" 6 | ) 7 | 8 | type Configor struct { 9 | *Config 10 | } 11 | 12 | type Config struct { 13 | Environment string 14 | ENVPrefix string 15 | } 16 | 17 | // New initialize a Configor 18 | func New(config *Config) *Configor { 19 | if config == nil { 20 | config = &Config{} 21 | } 22 | return &Configor{Config: config} 23 | } 24 | 25 | // GetEnvironment get environment 26 | func (configor *Configor) GetEnvironment() string { 27 | if configor.Environment == "" { 28 | if env := os.Getenv("CONFIGOR_ENV"); env != "" { 29 | return env 30 | } 31 | 32 | if isTest, _ := regexp.MatchString("/_test/", os.Args[0]); isTest { 33 | return "test" 34 | } 35 | 36 | return "development" 37 | } 38 | return configor.Environment 39 | } 40 | 41 | // Load will unmarshal configurations to struct from files that you provide 42 | func (configor *Configor) Load(config interface{}, files ...string) error { 43 | for _, file := range configor.getConfigurationFiles(files...) { 44 | if err := processFile(config, file); err != nil { 45 | return err 46 | } 47 | } 48 | 49 | if prefix := configor.getENVPrefix(config); prefix == "-" { 50 | return processTags(config) 51 | } else { 52 | return processTags(config, prefix) 53 | } 54 | } 55 | 56 | // ENV return environment 57 | func ENV() string { 58 | return New(nil).GetEnvironment() 59 | } 60 | 61 | // Load will unmarshal configurations to struct from files that you provide 62 | func Load(config interface{}, files ...string) error { 63 | return New(nil).Load(config, files...) 64 | } 65 | -------------------------------------------------------------------------------- /Godeps/Godeps.json: -------------------------------------------------------------------------------- 1 | { 2 | "ImportPath": "github.com/beanstalkg/beanstalkg", 3 | "GoVersion": "go1.8", 4 | "GodepVersion": "v79", 5 | "Deps": [ 6 | { 7 | "ImportPath": "github.com/BurntSushi/toml", 8 | "Comment": "v0.3.0", 9 | "Rev": "b26d9c308763d68093482582cea63d69be07a0f0" 10 | }, 11 | { 12 | "ImportPath": "github.com/davecgh/go-spew/spew", 13 | "Comment": "v1.1.0", 14 | "Rev": "346938d642f2ec3594ed81d874461961cd0faa76" 15 | }, 16 | { 17 | "ImportPath": "github.com/jinzhu/configor", 18 | "Rev": "ff2ac2b1ce3d687c3a1da0d2847fbce42524a7f3" 19 | }, 20 | { 21 | "ImportPath": "github.com/op/go-logging", 22 | "Comment": "v1-7-g970db52", 23 | "Rev": "970db520ece77730c7e4724c61121037378659d9" 24 | }, 25 | { 26 | "ImportPath": "github.com/pmezard/go-difflib/difflib", 27 | "Comment": "v1.0.0", 28 | "Rev": "792786c7400a136282c1664665ae0a8db921c6c2" 29 | }, 30 | { 31 | "ImportPath": "github.com/satori/go.uuid", 32 | "Comment": "v1.1.0-8-g5bf94b6", 33 | "Rev": "5bf94b69c6b68ee1b541973bb8e1144db23a194b" 34 | }, 35 | { 36 | "ImportPath": "github.com/stretchr/objx", 37 | "Rev": "1a9d0bb9f541897e62256577b352fdbc1fb4fd94" 38 | }, 39 | { 40 | "ImportPath": "github.com/stretchr/testify/assert", 41 | "Comment": "v1.1.4-27-g4d4bfba", 42 | "Rev": "4d4bfba8f1d1027c4fdbe371823030df51419987" 43 | }, 44 | { 45 | "ImportPath": "github.com/stretchr/testify/mock", 46 | "Comment": "v1.1.4-27-g4d4bfba", 47 | "Rev": "4d4bfba8f1d1027c4fdbe371823030df51419987" 48 | }, 49 | { 50 | "ImportPath": "gopkg.in/yaml.v1", 51 | "Rev": "137fcee5b78f4093230fd810c434c7d6fc065f76" 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/value.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | // Value provides methods for extracting interface{} data in various 9 | // types. 10 | type Value struct { 11 | // data contains the raw data being managed by this Value 12 | data interface{} 13 | } 14 | 15 | // Data returns the raw data contained by this Value 16 | func (v *Value) Data() interface{} { 17 | return v.data 18 | } 19 | 20 | // String returns the value always as a string 21 | func (v *Value) String() string { 22 | switch { 23 | case v.IsStr(): 24 | return v.Str() 25 | case v.IsBool(): 26 | return strconv.FormatBool(v.Bool()) 27 | case v.IsFloat32(): 28 | return strconv.FormatFloat(float64(v.Float32()), 'f', -1, 32) 29 | case v.IsFloat64(): 30 | return strconv.FormatFloat(v.Float64(), 'f', -1, 64) 31 | case v.IsInt(): 32 | return strconv.FormatInt(int64(v.Int()), 10) 33 | case v.IsInt(): 34 | return strconv.FormatInt(int64(v.Int()), 10) 35 | case v.IsInt8(): 36 | return strconv.FormatInt(int64(v.Int8()), 10) 37 | case v.IsInt16(): 38 | return strconv.FormatInt(int64(v.Int16()), 10) 39 | case v.IsInt32(): 40 | return strconv.FormatInt(int64(v.Int32()), 10) 41 | case v.IsInt64(): 42 | return strconv.FormatInt(v.Int64(), 10) 43 | case v.IsUint(): 44 | return strconv.FormatUint(uint64(v.Uint()), 10) 45 | case v.IsUint8(): 46 | return strconv.FormatUint(uint64(v.Uint8()), 10) 47 | case v.IsUint16(): 48 | return strconv.FormatUint(uint64(v.Uint16()), 10) 49 | case v.IsUint32(): 50 | return strconv.FormatUint(uint64(v.Uint32()), 10) 51 | case v.IsUint64(): 52 | return strconv.FormatUint(v.Uint64(), 10) 53 | } 54 | 55 | return fmt.Sprintf("%#v", v.Data()) 56 | } 57 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/mock/doc.go: -------------------------------------------------------------------------------- 1 | // Package mock provides a system by which it is possible to mock your objects 2 | // and verify calls are happening as expected. 3 | // 4 | // Example Usage 5 | // 6 | // The mock package provides an object, Mock, that tracks activity on another object. It is usually 7 | // embedded into a test object as shown below: 8 | // 9 | // type MyTestObject struct { 10 | // // add a Mock object instance 11 | // mock.Mock 12 | // 13 | // // other fields go here as normal 14 | // } 15 | // 16 | // When implementing the methods of an interface, you wire your functions up 17 | // to call the Mock.Called(args...) method, and return the appropriate values. 18 | // 19 | // For example, to mock a method that saves the name and age of a person and returns 20 | // the year of their birth or an error, you might write this: 21 | // 22 | // func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { 23 | // args := o.Called(firstname, lastname, age) 24 | // return args.Int(0), args.Error(1) 25 | // } 26 | // 27 | // The Int, Error and Bool methods are examples of strongly typed getters that take the argument 28 | // index position. Given this argument list: 29 | // 30 | // (12, true, "Something") 31 | // 32 | // You could read them out strongly typed like this: 33 | // 34 | // args.Int(0) 35 | // args.Bool(1) 36 | // args.String(2) 37 | // 38 | // For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: 39 | // 40 | // return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) 41 | // 42 | // This may cause a panic if the object you are getting is nil (the type assertion will fail), in those 43 | // cases you should check for nil first. 44 | package mock 45 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/syslog.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, Örjan Persson. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //+build !windows,!plan9 6 | 7 | package logging 8 | 9 | import "log/syslog" 10 | 11 | // SyslogBackend is a simple logger to syslog backend. It automatically maps 12 | // the internal log levels to appropriate syslog log levels. 13 | type SyslogBackend struct { 14 | Writer *syslog.Writer 15 | } 16 | 17 | // NewSyslogBackend connects to the syslog daemon using UNIX sockets with the 18 | // given prefix. If prefix is not given, the prefix will be derived from the 19 | // launched command. 20 | func NewSyslogBackend(prefix string) (b *SyslogBackend, err error) { 21 | var w *syslog.Writer 22 | w, err = syslog.New(syslog.LOG_CRIT, prefix) 23 | return &SyslogBackend{w}, err 24 | } 25 | 26 | // NewSyslogBackendPriority is the same as NewSyslogBackend, but with custom 27 | // syslog priority, like syslog.LOG_LOCAL3|syslog.LOG_DEBUG etc. 28 | func NewSyslogBackendPriority(prefix string, priority syslog.Priority) (b *SyslogBackend, err error) { 29 | var w *syslog.Writer 30 | w, err = syslog.New(priority, prefix) 31 | return &SyslogBackend{w}, err 32 | } 33 | 34 | // Log implements the Backend interface. 35 | func (b *SyslogBackend) Log(level Level, calldepth int, rec *Record) error { 36 | line := rec.Formatted(calldepth + 1) 37 | switch level { 38 | case CRITICAL: 39 | return b.Writer.Crit(line) 40 | case ERROR: 41 | return b.Writer.Err(line) 42 | case WARNING: 43 | return b.Writer.Warning(line) 44 | case NOTICE: 45 | return b.Writer.Notice(line) 46 | case INFO: 47 | return b.Writer.Info(line) 48 | case DEBUG: 49 | return b.Writer.Debug(line) 50 | default: 51 | } 52 | panic("unhandled log level") 53 | } 54 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypasssafe.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is running on Google App Engine, compiled by GopherJS, or 17 | // "-tags safe" is added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build js appengine safe disableunsafe 20 | 21 | package spew 22 | 23 | import "reflect" 24 | 25 | const ( 26 | // UnsafeDisabled is a build-time constant which specifies whether or 27 | // not access to the unsafe package is available. 28 | UnsafeDisabled = true 29 | ) 30 | 31 | // unsafeReflectValue typically converts the passed reflect.Value into a one 32 | // that bypasses the typical safety restrictions preventing access to 33 | // unaddressable and unexported data. However, doing this relies on access to 34 | // the unsafe package. This is a stub version which simply returns the passed 35 | // reflect.Value when the unsafe package is not available. 36 | func unsafeReflectValue(v reflect.Value) reflect.Value { 37 | return v 38 | } 39 | -------------------------------------------------------------------------------- /architecture/job_test.go: -------------------------------------------------------------------------------- 1 | package architecture 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var ( 8 | validTransitionToTest = map[State][]State{ 9 | READY: {RESERVED, DELAYED, BURIED}, 10 | DELAYED: {RESERVED}, 11 | RESERVED: {READY}, 12 | BURIED: {RESERVED}, 13 | } 14 | 15 | invalidTransitionToTest = map[State][]State{ 16 | READY: {READY}, 17 | DELAYED: {READY, DELAYED, BURIED}, 18 | RESERVED: {RESERVED, DELAYED, BURIED}, 19 | BURIED: {READY, DELAYED, BURIED}, 20 | } 21 | ) 22 | 23 | // State's stringer implementation is valuable in grokking tests, but 24 | // it's not really needed elsewhere. I could use map lookups, but 25 | // these tests are fast enough as is. 26 | func (s State) String() string { 27 | switch s { 28 | case READY: 29 | return "READY" 30 | case DELAYED: 31 | return "DELAYED" 32 | case RESERVED: 33 | return "RESERVED" 34 | case BURIED: 35 | return "BURIED" 36 | } 37 | 38 | return "INVALID" 39 | } 40 | 41 | func TestJob_SetState(t *testing.T) { 42 | for toState, fromStates := range validTransitionToTest { 43 | for _, fromState := range fromStates { 44 | testJ := &Job{state: fromState} 45 | 46 | if err := testJ.SetState(toState); err != nil { 47 | t.Errorf("State transition failed: %s -> %s; %s", testJ.state, toState, err) 48 | } 49 | 50 | if testJ.state != toState { 51 | t.Errorf("SetState succeeded, but Job's state wasn't changed: %s -> %s", testJ.state, toState) 52 | } 53 | } 54 | } 55 | 56 | for toState, fromStates := range invalidTransitionToTest { 57 | for _, fromState := range fromStates { 58 | testJ := &Job{state: fromState} 59 | 60 | if err := testJ.SetState(toState); err == nil { 61 | t.Errorf("State transition succeeded; should have failed: %s -> %s", testJ.state, toState) 62 | } 63 | 64 | if testJ.state != fromState { 65 | t.Errorf("SetState failed, but Job's state changed: %s -> %s", fromState, testJ.state) 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /operation/tube_handler.go: -------------------------------------------------------------------------------- 1 | package operation 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/beanstalkg/beanstalkg/architecture" 7 | ) 8 | 9 | func NewTubeHandler( 10 | name string, 11 | commands chan architecture.Command, 12 | watchedTubeConnectionsReceiver chan chan architecture.Command, 13 | stop chan bool, 14 | queueCreator architecture.PriorityQueueCreator, 15 | ) { 16 | go func() { 17 | // create the tube 18 | tube := architecture.NewTube(name, queueCreator) 19 | ticker := time.NewTicker(architecture.QUEUE_FREQUENCY) 20 | for { 21 | select { 22 | case <-ticker.C: 23 | log.Debug("House Keeping Started for: ", name) 24 | tube.Process() 25 | tube.ProcessTimedClients() 26 | case incomingCommand := <-commands: 27 | switch incomingCommand.Name { 28 | case architecture.PUT: 29 | tube.Put(&incomingCommand) 30 | commands <- incomingCommand.Copy() 31 | case architecture.RESERVE: 32 | sendChan := make(chan architecture.Command) 33 | watchedTubeConnectionsReceiver <- sendChan 34 | tube.Reserve(&incomingCommand, sendChan) 35 | case architecture.RESERVE_WITH_TIMEOUT: 36 | log.Info("reserve-with-timeout", incomingCommand) 37 | sendChan := make(chan architecture.Command) 38 | watchedTubeConnectionsReceiver <- sendChan 39 | tube.ReserveWithTimeout(&incomingCommand, sendChan) 40 | case architecture.DELETE: 41 | tube.Delete(&incomingCommand) 42 | commands <- incomingCommand.Copy() 43 | case architecture.RELEASE: 44 | tube.Release(&incomingCommand) 45 | commands <- incomingCommand.Copy() 46 | case architecture.BURY: 47 | tube.Bury(&incomingCommand) 48 | commands <- incomingCommand.Copy() 49 | case architecture.KICK: 50 | tube.Kick(&incomingCommand) 51 | commands <- incomingCommand.Copy() 52 | case architecture.KICK_JOB: 53 | tube.KickJob(&incomingCommand) 54 | commands <- incomingCommand.Copy() 55 | } 56 | case <-stop: 57 | ticker.Stop() 58 | return 59 | } 60 | } 61 | }() 62 | } 63 | -------------------------------------------------------------------------------- /vendor/github.com/satori/go.uuid/README.md: -------------------------------------------------------------------------------- 1 | # UUID package for Go language 2 | 3 | [![Build Status](https://travis-ci.org/satori/go.uuid.png?branch=master)](https://travis-ci.org/satori/go.uuid) 4 | [![Coverage Status](https://coveralls.io/repos/github/satori/go.uuid/badge.svg?branch=master)](https://coveralls.io/github/satori/go.uuid) 5 | [![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.png)](http://godoc.org/github.com/satori/go.uuid) 6 | 7 | This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs. 8 | 9 | With 100% test coverage and benchmarks out of box. 10 | 11 | Supported versions: 12 | * Version 1, based on timestamp and MAC address (RFC 4122) 13 | * Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1) 14 | * Version 3, based on MD5 hashing (RFC 4122) 15 | * Version 4, based on random numbers (RFC 4122) 16 | * Version 5, based on SHA-1 hashing (RFC 4122) 17 | 18 | ## Installation 19 | 20 | Use the `go` command: 21 | 22 | $ go get github.com/satori/go.uuid 23 | 24 | ## Requirements 25 | 26 | UUID package requires Go >= 1.2. 27 | 28 | ## Example 29 | 30 | ```go 31 | package main 32 | 33 | import ( 34 | "fmt" 35 | "github.com/satori/go.uuid" 36 | ) 37 | 38 | func main() { 39 | // Creating UUID Version 4 40 | u1 := uuid.NewV4() 41 | fmt.Printf("UUIDv4: %s\n", u1) 42 | 43 | // Parsing UUID from string input 44 | u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8") 45 | if err != nil { 46 | fmt.Printf("Something gone wrong: %s", err) 47 | } 48 | fmt.Printf("Successfully parsed: %s", u2) 49 | } 50 | ``` 51 | 52 | ## Documentation 53 | 54 | [Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project. 55 | 56 | ## Links 57 | * [RFC 4122](http://tools.ietf.org/html/rfc4122) 58 | * [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01) 59 | 60 | ## Copyright 61 | 62 | Copyright (C) 2013-2016 by Maxim Bublis . 63 | 64 | UUID package released under MIT License. 65 | See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details. 66 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/multi.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, Örjan Persson. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package logging 6 | 7 | // TODO remove Level stuff from the multi logger. Do one thing. 8 | 9 | // multiLogger is a log multiplexer which can be used to utilize multiple log 10 | // backends at once. 11 | type multiLogger struct { 12 | backends []LeveledBackend 13 | } 14 | 15 | // MultiLogger creates a logger which contain multiple loggers. 16 | func MultiLogger(backends ...Backend) LeveledBackend { 17 | var leveledBackends []LeveledBackend 18 | for _, backend := range backends { 19 | leveledBackends = append(leveledBackends, AddModuleLevel(backend)) 20 | } 21 | return &multiLogger{leveledBackends} 22 | } 23 | 24 | // Log passes the log record to all backends. 25 | func (b *multiLogger) Log(level Level, calldepth int, rec *Record) (err error) { 26 | for _, backend := range b.backends { 27 | if backend.IsEnabledFor(level, rec.Module) { 28 | // Shallow copy of the record for the formatted cache on Record and get the 29 | // record formatter from the backend. 30 | r2 := *rec 31 | if e := backend.Log(level, calldepth+1, &r2); e != nil { 32 | err = e 33 | } 34 | } 35 | } 36 | return 37 | } 38 | 39 | // GetLevel returns the highest level enabled by all backends. 40 | func (b *multiLogger) GetLevel(module string) Level { 41 | var level Level 42 | for _, backend := range b.backends { 43 | if backendLevel := backend.GetLevel(module); backendLevel > level { 44 | level = backendLevel 45 | } 46 | } 47 | return level 48 | } 49 | 50 | // SetLevel propagates the same level to all backends. 51 | func (b *multiLogger) SetLevel(level Level, module string) { 52 | for _, backend := range b.backends { 53 | backend.SetLevel(level, module) 54 | } 55 | } 56 | 57 | // IsEnabledFor returns true if any of the backends are enabled for it. 58 | func (b *multiLogger) IsEnabledFor(level Level, module string) bool { 59 | for _, backend := range b.backends { 60 | if backend.IsEnabledFor(level, module) { 61 | return true 62 | } 63 | } 64 | return false 65 | } 66 | -------------------------------------------------------------------------------- /testintegration/put_test.go: -------------------------------------------------------------------------------- 1 | package testintegration 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | "github.com/onsi/gomega/gexec" 7 | "os/exec" 8 | "time" 9 | // "strconv" 10 | ) 11 | 12 | var _ = Describe("Put", func() { 13 | 14 | var bs_session *gexec.Session 15 | var conn *Conn 16 | 17 | BeforeEach(func() { 18 | var err error 19 | command := exec.Command("../beanstalkg") 20 | bs_session, err = gexec.Start(command, GinkgoWriter, GinkgoWriter) 21 | Ω(err).ShouldNot(HaveOccurred()) 22 | // wait for the server to become ready 23 | time.Sleep(500 * time.Millisecond) 24 | conn, err = dial("tcp", "127.0.0.1:11300") 25 | Ω(err).ShouldNot(HaveOccurred()) 26 | }) 27 | 28 | Describe("Put command", func() { 29 | Context("Used with default tube", func() { 30 | It("should correctly put first job", func() { 31 | r, err := conn.cmd([]byte("hello"), "put", 0, dur(1), dur(1)) 32 | Ω(err).ShouldNot(HaveOccurred()) 33 | var id string 34 | _, err = conn.readResp(r, false, "INSERTED %s", &id) 35 | Ω(err).ShouldNot(HaveOccurred()) 36 | // println("successfully put job with id " + id) 37 | Ω(id).Should(MatchRegexp("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")) 38 | }) 39 | }) 40 | }) 41 | 42 | Describe("Produce and Consume job", func() { 43 | Context("Used with default tube", func() { 44 | It("should correctly put job and then reserve it", func() { 45 | put_payload := "hello" 46 | // produce job 47 | r1, err := conn.cmd([]byte(put_payload), "put", 0, dur(1), dur(1)) 48 | Ω(err).ShouldNot(HaveOccurred()) 49 | var put_id string 50 | _, err = conn.readResp(r1, false, "INSERTED %s", &put_id) 51 | Ω(err).ShouldNot(HaveOccurred()) 52 | // consume job 53 | r2, err := conn.cmd(nil, "reserve") 54 | Ω(err).ShouldNot(HaveOccurred()) 55 | // var info int 56 | var reserved_id string 57 | var reserved_payload []byte 58 | reserved_payload, err = conn.readResp(r2, true, "RESERVED %s", &reserved_id) 59 | Ω(err).ShouldNot(HaveOccurred()) 60 | Ω(reserved_id).Should(BeIdenticalTo(put_id)) 61 | Ω(string(reserved_payload)).Should(BeIdenticalTo(put_payload)) 62 | }) 63 | }) 64 | }) 65 | 66 | AfterEach(func() { 67 | gexec.Kill() 68 | conn.Close() 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/mutations.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | // Exclude returns a new Map with the keys in the specified []string 4 | // excluded. 5 | func (d Map) Exclude(exclude []string) Map { 6 | 7 | excluded := make(Map) 8 | for k, v := range d { 9 | var shouldInclude bool = true 10 | for _, toExclude := range exclude { 11 | if k == toExclude { 12 | shouldInclude = false 13 | break 14 | } 15 | } 16 | if shouldInclude { 17 | excluded[k] = v 18 | } 19 | } 20 | 21 | return excluded 22 | } 23 | 24 | // Copy creates a shallow copy of the Obj. 25 | func (m Map) Copy() Map { 26 | copied := make(map[string]interface{}) 27 | for k, v := range m { 28 | copied[k] = v 29 | } 30 | return New(copied) 31 | } 32 | 33 | // Merge blends the specified map with a copy of this map and returns the result. 34 | // 35 | // Keys that appear in both will be selected from the specified map. 36 | // This method requires that the wrapped object be a map[string]interface{} 37 | func (m Map) Merge(merge Map) Map { 38 | return m.Copy().MergeHere(merge) 39 | } 40 | 41 | // Merge blends the specified map with this map and returns the current map. 42 | // 43 | // Keys that appear in both will be selected from the specified map. The original map 44 | // will be modified. This method requires that 45 | // the wrapped object be a map[string]interface{} 46 | func (m Map) MergeHere(merge Map) Map { 47 | 48 | for k, v := range merge { 49 | m[k] = v 50 | } 51 | 52 | return m 53 | 54 | } 55 | 56 | // Transform builds a new Obj giving the transformer a chance 57 | // to change the keys and values as it goes. This method requires that 58 | // the wrapped object be a map[string]interface{} 59 | func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map { 60 | newMap := make(map[string]interface{}) 61 | for k, v := range m { 62 | modifiedKey, modifiedVal := transformer(k, v) 63 | newMap[modifiedKey] = modifiedVal 64 | } 65 | return New(newMap) 66 | } 67 | 68 | // TransformKeys builds a new map using the specified key mapping. 69 | // 70 | // Unspecified keys will be unaltered. 71 | // This method requires that the wrapped object be a map[string]interface{} 72 | func (m Map) TransformKeys(mapping map[string]string) Map { 73 | return m.Transform(func(key string, value interface{}) (string, interface{}) { 74 | 75 | if newKey, ok := mapping[key]; ok { 76 | return newKey, value 77 | } 78 | 79 | return key, value 80 | }) 81 | } 82 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | _ "net/http/pprof" 6 | "os" 7 | 8 | "github.com/beanstalkg/beanstalkg/architecture" 9 | "github.com/beanstalkg/beanstalkg/backend" 10 | "github.com/beanstalkg/beanstalkg/operation" 11 | "github.com/op/go-logging" 12 | ) 13 | 14 | var log = logging.MustGetLogger("BEANSTALKG") 15 | 16 | func main() { 17 | cfg := getConfig() 18 | initLogging(cfg.Debug) 19 | service := ":" + cfg.Port 20 | tcpAddr, err := net.ResolveTCPAddr("tcp4", service) 21 | checkError(err) 22 | listener, err := net.ListenTCP("tcp", tcpAddr) 23 | checkError(err) 24 | stop := make(chan bool) 25 | 26 | tubeRegister := make(chan architecture.Command) 27 | // use this tube to send the channels for each individual tube to the clients when the do 'use' command 28 | useTubeConnectionReceiver := make(chan chan architecture.Command) 29 | watchedTubeConnectionsReceiver := make(chan chan architecture.Command) 30 | operation.NewTubeRegister(tubeRegister, useTubeConnectionReceiver, watchedTubeConnectionsReceiver, stop, backend.QueueCreator(cfg.Backend)) 31 | log.Info("BEANSTALKG listening on: ", cfg.Port) 32 | 33 | for { 34 | // log.Println("BEANSTALKG Waiting..") 35 | conn, err := listener.Accept() 36 | if err != nil { 37 | continue 38 | } 39 | operation.NewClientHandler(conn, tubeRegister, useTubeConnectionReceiver, watchedTubeConnectionsReceiver, stop) 40 | } 41 | } 42 | 43 | func checkError(err error) { 44 | if err != nil { 45 | log.Fatal("Fatal error:", err.Error()) 46 | } 47 | } 48 | 49 | var format = logging.MustStringFormatter( 50 | `%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`, 51 | ) 52 | 53 | func initLogging(debugMode bool) { 54 | backend1 := logging.NewLogBackend(os.Stderr, "", 0) 55 | backend2 := logging.NewLogBackend(os.Stdout, "", 0) 56 | 57 | backend2Formatter := logging.NewBackendFormatter(backend2, format) 58 | backend1Formatter := logging.NewBackendFormatter(backend1, format) 59 | 60 | // Only errors and more severe messages should be sent to backend1 61 | backend1Leveled := logging.AddModuleLevel(backend1Formatter) 62 | backend1Leveled.SetLevel(logging.ERROR, "") 63 | 64 | backend2Leveled := logging.AddModuleLevel(backend2Formatter) 65 | if debugMode { 66 | backend2Leveled.SetLevel(logging.DEBUG, "") 67 | } else { 68 | backend2Leveled.SetLevel(logging.INFO, "") 69 | } 70 | // Set the backends to be used. 71 | logging.SetBackend(backend1Leveled, backend2Leveled) 72 | } 73 | -------------------------------------------------------------------------------- /architecture/mock_priority_queue.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v1.0.0 2 | package architecture 3 | 4 | import mock "github.com/stretchr/testify/mock" 5 | 6 | // PriorityQueue is an autogenerated mock type for the PriorityQueue type 7 | type MockPriorityQueue struct { 8 | mock.Mock 9 | } 10 | 11 | // Delete provides a mock function with given fields: id 12 | func (_m *MockPriorityQueue) Delete(id string) PriorityQueueItem { 13 | ret := _m.Called(id) 14 | 15 | var r0 PriorityQueueItem 16 | if rf, ok := ret.Get(0).(func(string) PriorityQueueItem); ok { 17 | r0 = rf(id) 18 | } else { 19 | if ret.Get(0) != nil { 20 | r0 = ret.Get(0).(PriorityQueueItem) 21 | } 22 | } 23 | 24 | return r0 25 | } 26 | 27 | // Dequeue provides a mock function with given fields: 28 | func (_m *MockPriorityQueue) Dequeue() PriorityQueueItem { 29 | ret := _m.Called() 30 | 31 | var r0 PriorityQueueItem 32 | if rf, ok := ret.Get(0).(func() PriorityQueueItem); ok { 33 | r0 = rf() 34 | } else { 35 | if ret.Get(0) != nil { 36 | r0 = ret.Get(0).(PriorityQueueItem) 37 | } 38 | } 39 | 40 | return r0 41 | } 42 | 43 | // Enqueue provides a mock function with given fields: item 44 | func (_m *MockPriorityQueue) Enqueue(item PriorityQueueItem) { 45 | _m.Called(item) 46 | } 47 | 48 | // Find provides a mock function with given fields: id 49 | func (_m *MockPriorityQueue) Find(id string) PriorityQueueItem { 50 | ret := _m.Called(id) 51 | 52 | var r0 PriorityQueueItem 53 | if rf, ok := ret.Get(0).(func(string) PriorityQueueItem); ok { 54 | r0 = rf(id) 55 | } else { 56 | if ret.Get(0) != nil { 57 | r0 = ret.Get(0).(PriorityQueueItem) 58 | } 59 | } 60 | 61 | return r0 62 | } 63 | 64 | // Init provides a mock function with given fields: 65 | func (_m *MockPriorityQueue) Init(tubeName string) { 66 | _m.Called() 67 | } 68 | 69 | // Peek provides a mock function with given fields: 70 | func (_m *MockPriorityQueue) Peek() PriorityQueueItem { 71 | ret := _m.Called() 72 | 73 | var r0 PriorityQueueItem 74 | if rf, ok := ret.Get(0).(func() PriorityQueueItem); ok { 75 | r0 = rf() 76 | } else { 77 | if ret.Get(0) != nil { 78 | r0 = ret.Get(0).(PriorityQueueItem) 79 | } 80 | } 81 | 82 | return r0 83 | } 84 | 85 | // Size provides a mock function with given fields: 86 | func (_m *MockPriorityQueue) Size() int { 87 | ret := _m.Called() 88 | 89 | var r0 int 90 | if rf, ok := ret.Get(0).(func() int); ok { 91 | r0 = rf() 92 | } else { 93 | r0 = ret.Get(0).(int) 94 | } 95 | 96 | return r0 97 | } 98 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/doc.go: -------------------------------------------------------------------------------- 1 | // objx - Go package for dealing with maps, slices, JSON and other data. 2 | // 3 | // Overview 4 | // 5 | // Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes 6 | // a powerful `Get` method (among others) that allows you to easily and quickly get 7 | // access to data within the map, without having to worry too much about type assertions, 8 | // missing data, default values etc. 9 | // 10 | // Pattern 11 | // 12 | // Objx uses a preditable pattern to make access data from within `map[string]interface{}'s 13 | // easy. 14 | // 15 | // Call one of the `objx.` functions to create your `objx.Map` to get going: 16 | // 17 | // m, err := objx.FromJSON(json) 18 | // 19 | // NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, 20 | // the rest will be optimistic and try to figure things out without panicking. 21 | // 22 | // Use `Get` to access the value you're interested in. You can use dot and array 23 | // notation too: 24 | // 25 | // m.Get("places[0].latlng") 26 | // 27 | // Once you have saught the `Value` you're interested in, you can use the `Is*` methods 28 | // to determine its type. 29 | // 30 | // if m.Get("code").IsStr() { /* ... */ } 31 | // 32 | // Or you can just assume the type, and use one of the strong type methods to 33 | // extract the real value: 34 | // 35 | // m.Get("code").Int() 36 | // 37 | // If there's no value there (or if it's the wrong type) then a default value 38 | // will be returned, or you can be explicit about the default value. 39 | // 40 | // Get("code").Int(-1) 41 | // 42 | // If you're dealing with a slice of data as a value, Objx provides many useful 43 | // methods for iterating, manipulating and selecting that data. You can find out more 44 | // by exploring the index below. 45 | // 46 | // Reading data 47 | // 48 | // A simple example of how to use Objx: 49 | // 50 | // // use MustFromJSON to make an objx.Map from some JSON 51 | // m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) 52 | // 53 | // // get the details 54 | // name := m.Get("name").Str() 55 | // age := m.Get("age").Int() 56 | // 57 | // // get their nickname (or use their name if they 58 | // // don't have one) 59 | // nickname := m.Get("nickname").Str(name) 60 | // 61 | // Ranging 62 | // 63 | // Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For 64 | // example, to `range` the data, do what you would expect: 65 | // 66 | // m := objx.MustFromJSON(json) 67 | // for key, value := range m { 68 | // 69 | // /* ... do your magic ... */ 70 | // 71 | // } 72 | package objx 73 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ## Beanstalkg [![CircleCI](https://circleci.com/gh/vimukthi-git/beanstalkg.svg?style=svg)](https://circleci.com/gh/vimukthi-git/beanstalkg) [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/beanstalkg-chat/Lobby) 2 | 3 | Beanstalkg is a go implementation of [Beanstalkd](https://github.com/kr/beanstalkd) **a fast, general-purpose work queue**. 4 | Idea is to support the same set of features and protocol with the addition of 5 | high availability and failover built in. You can read the plan.md if interested in contributing. 6 | 7 | Right now it supports all the basic commands to run producers and workers. i.e "use", "put", "watch", "ignore", "reserve", "delete", "release", "bury", "reserve-with-timeout". 8 | 9 | I wish to complete rest of the commands soon but any help is always appreciated. 10 | 11 | ### Advantages compared to beanstalkd 12 | 13 | - Extensible design. For example you can replace the backend storage with anything you like, just implement a simple interface and plugin. 14 | - Implemented in golang. More readable code with support for concurrency using awesome `go routines`. 15 | - Support for clustering(coming soon :) 16 | 17 | 18 | ### User guide 19 | 20 | Beanstalkg is currently only released as a docker image for users. Latest release is v0.0.3. Assuming you already have a 21 | working docker engine installation, you can start a Beanstalkg instance with following steps, 22 | 23 | - Run command `docker run -p 11300:11300 beanstalkg/beanstalkg:v0.0.3`. This will start the beanstalkg server in the foreground. 24 | The server starts listening on port 11300. 25 | - Now you can connect to the server with any client library available to [beanstalkd](https://github.com/kr/beanstalkd/wiki/Client-Libraries). 26 | eg: Using [official go client](https://github.com/Beanstalkg/beanstalk) 27 | ``` 28 | // Produce jobs: 29 | c, err := beanstalk.Dial("tcp", "127.0.0.1:11300") 30 | id, err := c.Put([]byte("hello"), 1, 0, 120*time.Second) 31 | 32 | // Consume jobs: 33 | c, err := beanstalk.Dial("tcp", "127.0.0.1:11300") 34 | id, body, err := c.Reserve(5 * time.Second) 35 | 36 | ``` 37 | Some introductory slides can be found [here](https://www.slideshare.net/VimukthiWickramasing/beanstalkg-76573237) 38 | 39 | ### Developer guide 40 | 41 | Please install golang then with `GOPATH` set correctly run, 42 | 43 | - `go get github.com/Beanstalkg/beanstalkg` 44 | - Add `$GOAPTH/bin` to the $PATH and run `beanstalkg` 45 | 46 | ## Licensing 47 | 48 | beanstalkg is licensed under the MIT License. See [LICENSE](https://github.com/vimukthi-git/beanstalkg/blob/master/LICENSE) for the full license text. 49 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v1/writerc.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | // Set the writer error and return false. 4 | func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { 5 | emitter.error = yaml_WRITER_ERROR 6 | emitter.problem = problem 7 | return false 8 | } 9 | 10 | // Flush the output buffer. 11 | func yaml_emitter_flush(emitter *yaml_emitter_t) bool { 12 | if emitter.write_handler == nil { 13 | panic("write handler not set") 14 | } 15 | 16 | // Check if the buffer is empty. 17 | if emitter.buffer_pos == 0 { 18 | return true 19 | } 20 | 21 | // If the output encoding is UTF-8, we don't need to recode the buffer. 22 | if emitter.encoding == yaml_UTF8_ENCODING { 23 | if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 24 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 25 | } 26 | emitter.buffer_pos = 0 27 | return true 28 | } 29 | 30 | // Recode the buffer into the raw buffer. 31 | var low, high int 32 | if emitter.encoding == yaml_UTF16LE_ENCODING { 33 | low, high = 0, 1 34 | } else { 35 | high, low = 1, 0 36 | } 37 | 38 | pos := 0 39 | for pos < emitter.buffer_pos { 40 | // See the "reader.c" code for more details on UTF-8 encoding. Note 41 | // that we assume that the buffer contains a valid UTF-8 sequence. 42 | 43 | // Read the next UTF-8 character. 44 | octet := emitter.buffer[pos] 45 | 46 | var w int 47 | var value rune 48 | switch { 49 | case octet&0x80 == 0x00: 50 | w, value = 1, rune(octet&0x7F) 51 | case octet&0xE0 == 0xC0: 52 | w, value = 2, rune(octet&0x1F) 53 | case octet&0xF0 == 0xE0: 54 | w, value = 3, rune(octet&0x0F) 55 | case octet&0xF8 == 0xF0: 56 | w, value = 4, rune(octet&0x07) 57 | } 58 | for k := 1; k < w; k++ { 59 | octet = emitter.buffer[pos+k] 60 | value = (value << 6) + (rune(octet) & 0x3F) 61 | } 62 | pos += w 63 | 64 | // Write the character. 65 | if value < 0x10000 { 66 | var b [2]byte 67 | b[high] = byte(value >> 8) 68 | b[low] = byte(value & 0xFF) 69 | emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1]) 70 | } else { 71 | // Write the character using a surrogate pair (check "reader.c"). 72 | var b [4]byte 73 | value -= 0x10000 74 | b[high] = byte(0xD8 + (value >> 18)) 75 | b[low] = byte((value >> 10) & 0xFF) 76 | b[high+2] = byte(0xDC + ((value >> 8) & 0xFF)) 77 | b[low+2] = byte(value & 0xFF) 78 | emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3]) 79 | } 80 | } 81 | 82 | // Write the raw buffer. 83 | if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil { 84 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 85 | } 86 | emitter.buffer_pos = 0 87 | emitter.raw_buffer = emitter.raw_buffer[:0] 88 | return true 89 | } 90 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: beanstalkg/beanstalkg_build:v0.03 6 | 7 | working_directory: /go/src/github.com/vimukthi-git/beanstalkg 8 | 9 | environment: 10 | TEST_RESULTS: /tmp/test-results 11 | 12 | steps: 13 | - checkout 14 | 15 | - run: mkdir -p $TEST_RESULTS 16 | 17 | - run: 18 | name: Run unit tests 19 | environment: 20 | #CONTACTS_DB_URL: "postgres://ubuntu@localhost:5432/contacts?sslmode=disable" 21 | #CONTACTS_DB_MIGRATIONS: /go/src/github.com/circleci/cci-demo-go/db/migrations 22 | command: | 23 | trap "go-junit-report <${TEST_RESULTS}/go-test.out > ${TEST_RESULTS}/go-test-report.xml" EXIT 24 | go get github.com/vimukthi-git/beanstalkg 25 | go test -v ./... | tee ${TEST_RESULTS}/go-test.out 26 | 27 | - deploy: 28 | name: Conditionally run docker push. i.e if this is a release branch 29 | command: | 30 | release_regex='^v([0-9]+\.){0,2}(\*|[0-9]+)$' 31 | if [[ $CIRCLE_BRANCH =~ $release_regex ]]; then 32 | curl --user ${CIRCLE_API_TOKEN}: \ 33 | --data build_parameters[CIRCLE_JOB]=deploy_docker \ 34 | --data revision=$CIRCLE_SHA1 \ 35 | https://circleci.com/api/v1.1/project/github/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/tree/$CIRCLE_BRANCH 36 | fi 37 | 38 | #- run: 39 | #name: Run integration tests 40 | #command: | 41 | 42 | - store_artifacts: 43 | path: /tmp/test-results 44 | destination: raw-test-output 45 | 46 | - store_test_results: 47 | path: /tmp/test-results 48 | 49 | deploy_docker: 50 | docker: 51 | - image: beanstalkg/beanstalkg_build:v0.03 52 | working_directory: /go/src/github.com/vimukthi-git/beanstalkg 53 | steps: 54 | - setup_remote_docker 55 | - checkout 56 | - run: 57 | name: Install Docker client 58 | command: | 59 | set -x 60 | VER="17.03.0-ce" 61 | curl -L -o /tmp/docker-$VER.tgz https://get.docker.com/builds/Linux/x86_64/docker-$VER.tgz 62 | tar -xz -C /tmp -f /tmp/docker-$VER.tgz 63 | mv /tmp/docker/* /usr/bin 64 | - run: docker login -u $DOCKER_USER -p $DOCKER_PASS 65 | - run: 66 | name: Docker build 67 | command: | 68 | go get github.com/vimukthi-git/beanstalkg 69 | CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o beanstalkg . 70 | docker build -t "beanstalkg:${CIRCLE_BRANCH}" . 71 | docker tag beanstalkg:$CIRCLE_BRANCH beanstalkg/beanstalkg:$CIRCLE_BRANCH 72 | docker push beanstalkg/beanstalkg:$CIRCLE_BRANCH 73 | 74 | 75 | -------------------------------------------------------------------------------- /operation/tube_register.go: -------------------------------------------------------------------------------- 1 | package operation 2 | 3 | import ( 4 | "github.com/beanstalkg/beanstalkg/architecture" 5 | ) 6 | 7 | const DEFAULT_TUBE string = "default" 8 | 9 | type TubeRegister struct { 10 | tubeStopChannels map[string]chan bool 11 | tubeChannels map[string]chan architecture.Command 12 | commands chan architecture.Command 13 | useTubeConnectionReceiver chan chan architecture.Command 14 | watchedTubeConnectionsReceiver chan chan architecture.Command 15 | stop chan bool 16 | queueCreator architecture.PriorityQueueCreator 17 | } 18 | 19 | func (tr *TubeRegister) init() { 20 | tr.tubeChannels[DEFAULT_TUBE], tr.tubeStopChannels[DEFAULT_TUBE] = tr.createTubeHandler(DEFAULT_TUBE, 21 | tr.watchedTubeConnectionsReceiver) 22 | for { 23 | select { 24 | case c := <-tr.commands: 25 | switch c.Name { 26 | case architecture.USE: 27 | fallthrough 28 | case architecture.WATCH: 29 | tr.createTubeIfNotExists(c.Params["tube"]) 30 | // send the tube connection to the client 31 | tr.useTubeConnectionReceiver <- tr.tubeChannels[c.Params["tube"]] 32 | log.Debugf("TUBE_REGISTER sent tube for %s: %s", c.Name, c.Params["tube"]) 33 | } 34 | case <-tr.stop: 35 | // TODO send stop signal to all tube channels 36 | return 37 | } 38 | } 39 | } 40 | 41 | func (tr *TubeRegister) createTubeIfNotExists(name string) { 42 | if _, ok := tr.tubeChannels[name]; !ok { 43 | // create tube_handler if does not exist 44 | tr.tubeChannels[name], tr.tubeStopChannels[name] = 45 | tr.createTubeHandler(name, tr.watchedTubeConnectionsReceiver) 46 | } 47 | } 48 | 49 | // createTubeHandler creates a new tube_handler with required command channel and stop channel 50 | func (tr *TubeRegister) createTubeHandler( 51 | name string, watchedTubeConnectionsReceiver chan chan architecture.Command) ( 52 | chan architecture.Command, chan bool) { 53 | tubeChannel := make(chan architecture.Command) 54 | stop := make(chan bool) 55 | NewTubeHandler(name, tubeChannel, watchedTubeConnectionsReceiver, stop, tr.queueCreator) 56 | return tubeChannel, stop 57 | } 58 | 59 | func NewTubeRegister( 60 | commands chan architecture.Command, 61 | useTubeConnectionReceiver chan chan architecture.Command, 62 | watchedTubeConnectionsReceiver chan chan architecture.Command, 63 | stop chan bool, 64 | queueCreator architecture.PriorityQueueCreator, 65 | ) { 66 | // store the tube stop signalling channels 67 | tubeStopChannels := make(map[string]chan bool) 68 | // store the tube command sending channels 69 | tubeChannels := make(map[string]chan architecture.Command) 70 | tubeRegister := TubeRegister{ 71 | tubeStopChannels, 72 | tubeChannels, 73 | commands, 74 | useTubeConnectionReceiver, 75 | watchedTubeConnectionsReceiver, 76 | stop, 77 | queueCreator, 78 | } 79 | go tubeRegister.init() 80 | } 81 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/type_check.go: -------------------------------------------------------------------------------- 1 | package toml 2 | 3 | // tomlType represents any Go type that corresponds to a TOML type. 4 | // While the first draft of the TOML spec has a simplistic type system that 5 | // probably doesn't need this level of sophistication, we seem to be militating 6 | // toward adding real composite types. 7 | type tomlType interface { 8 | typeString() string 9 | } 10 | 11 | // typeEqual accepts any two types and returns true if they are equal. 12 | func typeEqual(t1, t2 tomlType) bool { 13 | if t1 == nil || t2 == nil { 14 | return false 15 | } 16 | return t1.typeString() == t2.typeString() 17 | } 18 | 19 | func typeIsHash(t tomlType) bool { 20 | return typeEqual(t, tomlHash) || typeEqual(t, tomlArrayHash) 21 | } 22 | 23 | type tomlBaseType string 24 | 25 | func (btype tomlBaseType) typeString() string { 26 | return string(btype) 27 | } 28 | 29 | func (btype tomlBaseType) String() string { 30 | return btype.typeString() 31 | } 32 | 33 | var ( 34 | tomlInteger tomlBaseType = "Integer" 35 | tomlFloat tomlBaseType = "Float" 36 | tomlDatetime tomlBaseType = "Datetime" 37 | tomlString tomlBaseType = "String" 38 | tomlBool tomlBaseType = "Bool" 39 | tomlArray tomlBaseType = "Array" 40 | tomlHash tomlBaseType = "Hash" 41 | tomlArrayHash tomlBaseType = "ArrayHash" 42 | ) 43 | 44 | // typeOfPrimitive returns a tomlType of any primitive value in TOML. 45 | // Primitive values are: Integer, Float, Datetime, String and Bool. 46 | // 47 | // Passing a lexer item other than the following will cause a BUG message 48 | // to occur: itemString, itemBool, itemInteger, itemFloat, itemDatetime. 49 | func (p *parser) typeOfPrimitive(lexItem item) tomlType { 50 | switch lexItem.typ { 51 | case itemInteger: 52 | return tomlInteger 53 | case itemFloat: 54 | return tomlFloat 55 | case itemDatetime: 56 | return tomlDatetime 57 | case itemString: 58 | return tomlString 59 | case itemMultilineString: 60 | return tomlString 61 | case itemRawString: 62 | return tomlString 63 | case itemRawMultilineString: 64 | return tomlString 65 | case itemBool: 66 | return tomlBool 67 | } 68 | p.bug("Cannot infer primitive type of lex item '%s'.", lexItem) 69 | panic("unreachable") 70 | } 71 | 72 | // typeOfArray returns a tomlType for an array given a list of types of its 73 | // values. 74 | // 75 | // In the current spec, if an array is homogeneous, then its type is always 76 | // "Array". If the array is not homogeneous, an error is generated. 77 | func (p *parser) typeOfArray(types []tomlType) tomlType { 78 | // Empty arrays are cool. 79 | if len(types) == 0 { 80 | return tomlArray 81 | } 82 | 83 | theType := types[0] 84 | for _, t := range types[1:] { 85 | if !typeEqual(theType, t) { 86 | p.panicf("Array contains values of type '%s' and '%s', but "+ 87 | "arrays must be homogeneous.", theType, t) 88 | } 89 | } 90 | return tomlArray 91 | } 92 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v1/sorter.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "reflect" 5 | "unicode" 6 | ) 7 | 8 | type keyList []reflect.Value 9 | 10 | func (l keyList) Len() int { return len(l) } 11 | func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 12 | func (l keyList) Less(i, j int) bool { 13 | a := l[i] 14 | b := l[j] 15 | ak := a.Kind() 16 | bk := b.Kind() 17 | for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { 18 | a = a.Elem() 19 | ak = a.Kind() 20 | } 21 | for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { 22 | b = b.Elem() 23 | bk = b.Kind() 24 | } 25 | af, aok := keyFloat(a) 26 | bf, bok := keyFloat(b) 27 | if aok && bok { 28 | if af != bf { 29 | return af < bf 30 | } 31 | if ak != bk { 32 | return ak < bk 33 | } 34 | return numLess(a, b) 35 | } 36 | if ak != reflect.String || bk != reflect.String { 37 | return ak < bk 38 | } 39 | ar, br := []rune(a.String()), []rune(b.String()) 40 | for i := 0; i < len(ar) && i < len(br); i++ { 41 | if ar[i] == br[i] { 42 | continue 43 | } 44 | al := unicode.IsLetter(ar[i]) 45 | bl := unicode.IsLetter(br[i]) 46 | if al && bl { 47 | return ar[i] < br[i] 48 | } 49 | if al || bl { 50 | return bl 51 | } 52 | var ai, bi int 53 | var an, bn int64 54 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 55 | an = an*10 + int64(ar[ai]-'0') 56 | } 57 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 58 | bn = bn*10 + int64(br[bi]-'0') 59 | } 60 | if an != bn { 61 | return an < bn 62 | } 63 | if ai != bi { 64 | return ai < bi 65 | } 66 | return ar[i] < br[i] 67 | } 68 | return len(ar) < len(br) 69 | } 70 | 71 | // keyFloat returns a float value for v if it is a number/bool 72 | // and whether it is a number/bool or not. 73 | func keyFloat(v reflect.Value) (f float64, ok bool) { 74 | switch v.Kind() { 75 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 76 | return float64(v.Int()), true 77 | case reflect.Float32, reflect.Float64: 78 | return v.Float(), true 79 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 80 | return float64(v.Uint()), true 81 | case reflect.Bool: 82 | if v.Bool() { 83 | return 1, true 84 | } 85 | return 0, true 86 | } 87 | return 0, false 88 | } 89 | 90 | // numLess returns whether a < b. 91 | // a and b must necessarily have the same kind. 92 | func numLess(a, b reflect.Value) bool { 93 | switch a.Kind() { 94 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 95 | return a.Int() < b.Int() 96 | case reflect.Float32, reflect.Float64: 97 | return a.Float() < b.Float() 98 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 99 | return a.Uint() < b.Uint() 100 | case reflect.Bool: 101 | return !a.Bool() && b.Bool() 102 | } 103 | panic("not a number") 104 | } 105 | -------------------------------------------------------------------------------- /backend/min_heap_test.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | type testHeapItem struct { 10 | key int64 11 | id string 12 | timestamp int64 13 | } 14 | 15 | func (t testHeapItem) Key() int64 { 16 | return t.key 17 | } 18 | 19 | func (t testHeapItem) Id() string { 20 | return t.id 21 | } 22 | 23 | func (t testHeapItem) Timestamp() int64 { 24 | return t.timestamp 25 | } 26 | 27 | func (t testHeapItem) Enqueued() { 28 | t.timestamp = time.Now().UnixNano() 29 | } 30 | 31 | func (t testHeapItem) Dequeued() { 32 | } 33 | 34 | /** 35 | 5 36 | INSERT 4 37 | INSERT 9 38 | DELETE 4 39 | */ 40 | func TestMinHeap_Insert(t *testing.T) { 41 | m := MinHeap{} 42 | m.Enqueue(testHeapItem{4, string(1), time.Now().UnixNano()}) 43 | fmt.Println(m.Min().Key()) 44 | if m.Min().Key() != 4 { 45 | t.Fail() 46 | } 47 | m.Enqueue(testHeapItem{9, string(2), time.Now().UnixNano()}) 48 | fmt.Println(m.Min()) 49 | if m.Min().Key() != 4 { 50 | t.Fail() 51 | } 52 | m.Delete(string(1)) 53 | if m.Size() != 1 { 54 | t.Fail() 55 | } 56 | fmt.Println(m.Min().Key()) 57 | if m.Min().Key() != 9 { 58 | t.Fail() 59 | } 60 | // m.Delete(string(2)) 61 | fmt.Println(m.Dequeue().Key(), string(3)) 62 | } 63 | 64 | func TestMinHeap_InsertCheckDelete(t *testing.T) { 65 | m := MinHeap{} 66 | m.Enqueue(testHeapItem{1, "one", time.Now().UnixNano()}) 67 | m.Enqueue(testHeapItem{1, "two", time.Now().UnixNano()}) 68 | m.Enqueue(testHeapItem{1, "three", time.Now().UnixNano()}) 69 | m.Enqueue(testHeapItem{1, "four", time.Now().UnixNano()}) 70 | fmt.Println(m) 71 | item := m.Dequeue().(testHeapItem) 72 | if item.Id() != "one" { 73 | t.Fail() 74 | } 75 | fmt.Println(item, m) 76 | item = m.Dequeue().(testHeapItem) 77 | if item.Id() != "two" { 78 | t.Fail() 79 | } 80 | fmt.Println(item, m) 81 | item = m.Dequeue().(testHeapItem) 82 | if item.Id() != "three" { 83 | t.Fail() 84 | } 85 | fmt.Println(item, m) 86 | item = m.Dequeue().(testHeapItem) 87 | if item.Id() != "four" { 88 | t.Fail() 89 | } 90 | fmt.Println(item, m) 91 | if m.Dequeue() != nil { 92 | t.Fail() 93 | } 94 | m.Enqueue(testHeapItem{1, "one", time.Now().UnixNano()}) 95 | item = m.Dequeue().(testHeapItem) 96 | if item.Id() != "one" { 97 | t.Fail() 98 | } 99 | //item = m.Dequeue().(testHeapItem) 100 | //fmt.Println(item, m) 101 | } 102 | 103 | func TestIntegration(t *testing.T) { 104 | // TODO create another tube struct to make this work 105 | //tube := architecture.Tube{"test", &MinHeap{}, &MinHeap{}, &MinHeap{}, &MinHeap{}, &MinHeap{}, make(map[string]*architecture.AwaitingClient)} 106 | ////m.Enqueue(testHeapItem{4, string(1)}) 107 | //fmt.Println(tube) 108 | //tube.delayed.Enqueue(testHeapItem{4, string(1), time.Now().UnixNano()}) 109 | //if tube.delayed.Dequeue().Key() != 4 { 110 | // t.Fail() 111 | //} 112 | //fmt.Println(tube.delayed) 113 | //if tube.delayed.Find(string(1)) != nil { 114 | // t.Fail() 115 | //} 116 | //if tube.delayed.Dequeue() != nil { 117 | // t.Fail() 118 | //} 119 | } 120 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/log_nix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | // Copyright 2013, Örjan Persson. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | package logging 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "io" 13 | "log" 14 | ) 15 | 16 | type color int 17 | 18 | const ( 19 | ColorBlack = iota + 30 20 | ColorRed 21 | ColorGreen 22 | ColorYellow 23 | ColorBlue 24 | ColorMagenta 25 | ColorCyan 26 | ColorWhite 27 | ) 28 | 29 | var ( 30 | colors = []string{ 31 | CRITICAL: ColorSeq(ColorMagenta), 32 | ERROR: ColorSeq(ColorRed), 33 | WARNING: ColorSeq(ColorYellow), 34 | NOTICE: ColorSeq(ColorGreen), 35 | DEBUG: ColorSeq(ColorCyan), 36 | } 37 | boldcolors = []string{ 38 | CRITICAL: ColorSeqBold(ColorMagenta), 39 | ERROR: ColorSeqBold(ColorRed), 40 | WARNING: ColorSeqBold(ColorYellow), 41 | NOTICE: ColorSeqBold(ColorGreen), 42 | DEBUG: ColorSeqBold(ColorCyan), 43 | } 44 | ) 45 | 46 | // LogBackend utilizes the standard log module. 47 | type LogBackend struct { 48 | Logger *log.Logger 49 | Color bool 50 | ColorConfig []string 51 | } 52 | 53 | // NewLogBackend creates a new LogBackend. 54 | func NewLogBackend(out io.Writer, prefix string, flag int) *LogBackend { 55 | return &LogBackend{Logger: log.New(out, prefix, flag)} 56 | } 57 | 58 | // Log implements the Backend interface. 59 | func (b *LogBackend) Log(level Level, calldepth int, rec *Record) error { 60 | if b.Color { 61 | col := colors[level] 62 | if len(b.ColorConfig) > int(level) && b.ColorConfig[level] != "" { 63 | col = b.ColorConfig[level] 64 | } 65 | 66 | buf := &bytes.Buffer{} 67 | buf.Write([]byte(col)) 68 | buf.Write([]byte(rec.Formatted(calldepth + 1))) 69 | buf.Write([]byte("\033[0m")) 70 | // For some reason, the Go logger arbitrarily decided "2" was the correct 71 | // call depth... 72 | return b.Logger.Output(calldepth+2, buf.String()) 73 | } 74 | 75 | return b.Logger.Output(calldepth+2, rec.Formatted(calldepth+1)) 76 | } 77 | 78 | // ConvertColors takes a list of ints representing colors for log levels and 79 | // converts them into strings for ANSI color formatting 80 | func ConvertColors(colors []int, bold bool) []string { 81 | converted := []string{} 82 | for _, i := range colors { 83 | if bold { 84 | converted = append(converted, ColorSeqBold(color(i))) 85 | } else { 86 | converted = append(converted, ColorSeq(color(i))) 87 | } 88 | } 89 | 90 | return converted 91 | } 92 | 93 | func ColorSeq(color color) string { 94 | return fmt.Sprintf("\033[%dm", int(color)) 95 | } 96 | 97 | func ColorSeqBold(color color) string { 98 | return fmt.Sprintf("\033[%d;1m", int(color)) 99 | } 100 | 101 | func doFmtVerbLevelColor(layout string, level Level, output io.Writer) { 102 | if layout == "bold" { 103 | output.Write([]byte(boldcolors[level])) 104 | } else if layout == "reset" { 105 | output.Write([]byte("\033[0m")) 106 | } else { 107 | output.Write([]byte(colors[level])) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/conversions.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "net/url" 10 | ) 11 | 12 | // JSON converts the contained object to a JSON string 13 | // representation 14 | func (m Map) JSON() (string, error) { 15 | 16 | result, err := json.Marshal(m) 17 | 18 | if err != nil { 19 | err = errors.New("objx: JSON encode failed with: " + err.Error()) 20 | } 21 | 22 | return string(result), err 23 | 24 | } 25 | 26 | // MustJSON converts the contained object to a JSON string 27 | // representation and panics if there is an error 28 | func (m Map) MustJSON() string { 29 | result, err := m.JSON() 30 | if err != nil { 31 | panic(err.Error()) 32 | } 33 | return result 34 | } 35 | 36 | // Base64 converts the contained object to a Base64 string 37 | // representation of the JSON string representation 38 | func (m Map) Base64() (string, error) { 39 | 40 | var buf bytes.Buffer 41 | 42 | jsonData, err := m.JSON() 43 | if err != nil { 44 | return "", err 45 | } 46 | 47 | encoder := base64.NewEncoder(base64.StdEncoding, &buf) 48 | encoder.Write([]byte(jsonData)) 49 | encoder.Close() 50 | 51 | return buf.String(), nil 52 | 53 | } 54 | 55 | // MustBase64 converts the contained object to a Base64 string 56 | // representation of the JSON string representation and panics 57 | // if there is an error 58 | func (m Map) MustBase64() string { 59 | result, err := m.Base64() 60 | if err != nil { 61 | panic(err.Error()) 62 | } 63 | return result 64 | } 65 | 66 | // SignedBase64 converts the contained object to a Base64 string 67 | // representation of the JSON string representation and signs it 68 | // using the provided key. 69 | func (m Map) SignedBase64(key string) (string, error) { 70 | 71 | base64, err := m.Base64() 72 | if err != nil { 73 | return "", err 74 | } 75 | 76 | sig := HashWithKey(base64, key) 77 | 78 | return base64 + SignatureSeparator + sig, nil 79 | 80 | } 81 | 82 | // MustSignedBase64 converts the contained object to a Base64 string 83 | // representation of the JSON string representation and signs it 84 | // using the provided key and panics if there is an error 85 | func (m Map) MustSignedBase64(key string) string { 86 | result, err := m.SignedBase64(key) 87 | if err != nil { 88 | panic(err.Error()) 89 | } 90 | return result 91 | } 92 | 93 | /* 94 | URL Query 95 | ------------------------------------------------ 96 | */ 97 | 98 | // URLValues creates a url.Values object from an Obj. This 99 | // function requires that the wrapped object be a map[string]interface{} 100 | func (m Map) URLValues() url.Values { 101 | 102 | vals := make(url.Values) 103 | 104 | for k, v := range m { 105 | //TODO: can this be done without sprintf? 106 | vals.Set(k, fmt.Sprintf("%v", v)) 107 | } 108 | 109 | return vals 110 | } 111 | 112 | // URLQuery gets an encoded URL query representing the given 113 | // Obj. This function requires that the wrapped object be a 114 | // map[string]interface{} 115 | func (m Map) URLQuery() (string, error) { 116 | return m.URLValues().Encode(), nil 117 | } 118 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v1/README.md: -------------------------------------------------------------------------------- 1 | # YAML support for the Go language 2 | 3 | Introduction 4 | ------------ 5 | 6 | The yaml package enables Go programs to comfortably encode and decode YAML 7 | values. It was developed within [Canonical](https://www.canonical.com) as 8 | part of the [juju](https://juju.ubuntu.com) project, and is based on a 9 | pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) 10 | C library to parse and generate YAML data quickly and reliably. 11 | 12 | Compatibility 13 | ------------- 14 | 15 | The yaml package is almost compatible with YAML 1.1, including support for 16 | anchors, tags, etc. There are still a few missing bits, such as document 17 | merging, base-60 floats (huh?), and multi-document unmarshalling. These 18 | features are not hard to add, and will be introduced as necessary. 19 | 20 | Installation and usage 21 | ---------------------- 22 | 23 | The import path for the package is *gopkg.in/yaml.v1*. 24 | 25 | To install it, run: 26 | 27 | go get gopkg.in/yaml.v1 28 | 29 | API documentation 30 | ----------------- 31 | 32 | If opened in a browser, the import path itself leads to the API documentation: 33 | 34 | * [https://gopkg.in/yaml.v1](https://gopkg.in/yaml.v1) 35 | 36 | API stability 37 | ------------- 38 | 39 | The package API for yaml v1 will remain stable as described in [gopkg.in](https://gopkg.in). 40 | 41 | 42 | License 43 | ------- 44 | 45 | The yaml package is licensed under the LGPL with an exception that allows it to be linked statically. Please see the LICENSE file for details. 46 | 47 | 48 | Example 49 | ------- 50 | 51 | ```Go 52 | package main 53 | 54 | import ( 55 | "fmt" 56 | "log" 57 | 58 | "gopkg.in/yaml.v1" 59 | ) 60 | 61 | var data = ` 62 | a: Easy! 63 | b: 64 | c: 2 65 | d: [3, 4] 66 | ` 67 | 68 | type T struct { 69 | A string 70 | B struct{C int; D []int ",flow"} 71 | } 72 | 73 | func main() { 74 | t := T{} 75 | 76 | err := yaml.Unmarshal([]byte(data), &t) 77 | if err != nil { 78 | log.Fatalf("error: %v", err) 79 | } 80 | fmt.Printf("--- t:\n%v\n\n", t) 81 | 82 | d, err := yaml.Marshal(&t) 83 | if err != nil { 84 | log.Fatalf("error: %v", err) 85 | } 86 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 87 | 88 | m := make(map[interface{}]interface{}) 89 | 90 | err = yaml.Unmarshal([]byte(data), &m) 91 | if err != nil { 92 | log.Fatalf("error: %v", err) 93 | } 94 | fmt.Printf("--- m:\n%v\n\n", m) 95 | 96 | d, err = yaml.Marshal(&m) 97 | if err != nil { 98 | log.Fatalf("error: %v", err) 99 | } 100 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 101 | } 102 | ``` 103 | 104 | This example will generate the following output: 105 | 106 | ``` 107 | --- t: 108 | {Easy! {2 [3 4]}} 109 | 110 | --- t dump: 111 | a: Easy! 112 | b: 113 | c: 2 114 | d: [3, 4] 115 | 116 | 117 | --- m: 118 | map[a:Easy! b:map[c:2 d:[3 4]]] 119 | 120 | --- m dump: 121 | a: Easy! 122 | b: 123 | c: 2 124 | d: 125 | - 3 126 | - 4 127 | ``` 128 | 129 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/README.md: -------------------------------------------------------------------------------- 1 | ## Golang logging library 2 | 3 | [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/op/go-logging) [![build](https://img.shields.io/travis/op/go-logging.svg?style=flat)](https://travis-ci.org/op/go-logging) 4 | 5 | Package logging implements a logging infrastructure for Go. Its output format 6 | is customizable and supports different logging backends like syslog, file and 7 | memory. Multiple backends can be utilized with different log levels per backend 8 | and logger. 9 | 10 | **_NOTE:_** backwards compatibility promise have been dropped for master. Please 11 | vendor this package or use `gopkg.in/op/go-logging.v1` for previous version. See 12 | [changelog](CHANGELOG.md) for details. 13 | 14 | ## Example 15 | 16 | Let's have a look at an [example](examples/example.go) which demonstrates most 17 | of the features found in this library. 18 | 19 | [![Example Output](examples/example.png)](examples/example.go) 20 | 21 | ```go 22 | package main 23 | 24 | import ( 25 | "os" 26 | 27 | "github.com/op/go-logging" 28 | ) 29 | 30 | var log = logging.MustGetLogger("example") 31 | 32 | // Example format string. Everything except the message has a custom color 33 | // which is dependent on the log level. Many fields have a custom output 34 | // formatting too, eg. the time returns the hour down to the milli second. 35 | var format = logging.MustStringFormatter( 36 | `%{color}%{time:15:04:05.000} %{shortfunc} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}`, 37 | ) 38 | 39 | // Password is just an example type implementing the Redactor interface. Any 40 | // time this is logged, the Redacted() function will be called. 41 | type Password string 42 | 43 | func (p Password) Redacted() interface{} { 44 | return logging.Redact(string(p)) 45 | } 46 | 47 | func main() { 48 | // For demo purposes, create two backend for os.Stderr. 49 | backend1 := logging.NewLogBackend(os.Stderr, "", 0) 50 | backend2 := logging.NewLogBackend(os.Stderr, "", 0) 51 | 52 | // For messages written to backend2 we want to add some additional 53 | // information to the output, including the used log level and the name of 54 | // the function. 55 | backend2Formatter := logging.NewBackendFormatter(backend2, format) 56 | 57 | // Only errors and more severe messages should be sent to backend1 58 | backend1Leveled := logging.AddModuleLevel(backend1) 59 | backend1Leveled.SetLevel(logging.ERROR, "") 60 | 61 | // Set the backends to be used. 62 | logging.SetBackend(backend1Leveled, backend2Formatter) 63 | 64 | log.Debugf("debug %s", Password("secret")) 65 | log.Info("info") 66 | log.Notice("notice") 67 | log.Warning("warning") 68 | log.Error("err") 69 | log.Critical("crit") 70 | } 71 | ``` 72 | 73 | ## Installing 74 | 75 | ### Using *go get* 76 | 77 | $ go get github.com/op/go-logging 78 | 79 | After this command *go-logging* is ready to use. Its source will be in: 80 | 81 | $GOPATH/src/pkg/github.com/op/go-logging 82 | 83 | You can use `go get -u` to update the package. 84 | 85 | ## Documentation 86 | 87 | For docs, see http://godoc.org/github.com/op/go-logging or run: 88 | 89 | $ godoc github.com/op/go-logging 90 | 91 | ## Additional resources 92 | 93 | * [wslog](https://godoc.org/github.com/cryptix/exp/wslog) -- exposes log messages through a WebSocket. 94 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/log_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | // Copyright 2013, Örjan Persson. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package logging 7 | 8 | import ( 9 | "bytes" 10 | "io" 11 | "log" 12 | "syscall" 13 | ) 14 | 15 | var ( 16 | kernel32DLL = syscall.NewLazyDLL("kernel32.dll") 17 | setConsoleTextAttributeProc = kernel32DLL.NewProc("SetConsoleTextAttribute") 18 | ) 19 | 20 | // Character attributes 21 | // Note: 22 | // -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan). 23 | // Clearing all foreground or background colors results in black; setting all creates white. 24 | // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes. 25 | const ( 26 | fgBlack = 0x0000 27 | fgBlue = 0x0001 28 | fgGreen = 0x0002 29 | fgCyan = 0x0003 30 | fgRed = 0x0004 31 | fgMagenta = 0x0005 32 | fgYellow = 0x0006 33 | fgWhite = 0x0007 34 | fgIntensity = 0x0008 35 | fgMask = 0x000F 36 | ) 37 | 38 | var ( 39 | colors = []uint16{ 40 | INFO: fgWhite, 41 | CRITICAL: fgMagenta, 42 | ERROR: fgRed, 43 | WARNING: fgYellow, 44 | NOTICE: fgGreen, 45 | DEBUG: fgCyan, 46 | } 47 | boldcolors = []uint16{ 48 | INFO: fgWhite | fgIntensity, 49 | CRITICAL: fgMagenta | fgIntensity, 50 | ERROR: fgRed | fgIntensity, 51 | WARNING: fgYellow | fgIntensity, 52 | NOTICE: fgGreen | fgIntensity, 53 | DEBUG: fgCyan | fgIntensity, 54 | } 55 | ) 56 | 57 | type file interface { 58 | Fd() uintptr 59 | } 60 | 61 | // LogBackend utilizes the standard log module. 62 | type LogBackend struct { 63 | Logger *log.Logger 64 | Color bool 65 | 66 | // f is set to a non-nil value if the underlying writer which logs writes to 67 | // implements the file interface. This makes us able to colorise the output. 68 | f file 69 | } 70 | 71 | // NewLogBackend creates a new LogBackend. 72 | func NewLogBackend(out io.Writer, prefix string, flag int) *LogBackend { 73 | b := &LogBackend{Logger: log.New(out, prefix, flag)} 74 | 75 | // Unfortunately, the API used only takes an io.Writer where the Windows API 76 | // need the actual fd to change colors. 77 | if f, ok := out.(file); ok { 78 | b.f = f 79 | } 80 | 81 | return b 82 | } 83 | 84 | func (b *LogBackend) Log(level Level, calldepth int, rec *Record) error { 85 | if b.Color && b.f != nil { 86 | buf := &bytes.Buffer{} 87 | setConsoleTextAttribute(b.f, colors[level]) 88 | buf.Write([]byte(rec.Formatted(calldepth + 1))) 89 | err := b.Logger.Output(calldepth+2, buf.String()) 90 | setConsoleTextAttribute(b.f, fgWhite) 91 | return err 92 | } 93 | return b.Logger.Output(calldepth+2, rec.Formatted(calldepth+1)) 94 | } 95 | 96 | // setConsoleTextAttribute sets the attributes of characters written to the 97 | // console screen buffer by the WriteFile or WriteConsole function. 98 | // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx. 99 | func setConsoleTextAttribute(f file, attribute uint16) bool { 100 | ok, _, _ := setConsoleTextAttributeProc.Call(f.Fd(), uintptr(attribute), 0) 101 | return ok != 0 102 | } 103 | 104 | func doFmtVerbLevelColor(layout string, level Level, output io.Writer) { 105 | // TODO not supported on Windows since the io.Writer here is actually a 106 | // bytes.Buffer. 107 | } 108 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/level.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, Örjan Persson. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package logging 6 | 7 | import ( 8 | "errors" 9 | "strings" 10 | "sync" 11 | ) 12 | 13 | // ErrInvalidLogLevel is used when an invalid log level has been used. 14 | var ErrInvalidLogLevel = errors.New("logger: invalid log level") 15 | 16 | // Level defines all available log levels for log messages. 17 | type Level int 18 | 19 | // Log levels. 20 | const ( 21 | CRITICAL Level = iota 22 | ERROR 23 | WARNING 24 | NOTICE 25 | INFO 26 | DEBUG 27 | ) 28 | 29 | var levelNames = []string{ 30 | "CRITICAL", 31 | "ERROR", 32 | "WARNING", 33 | "NOTICE", 34 | "INFO", 35 | "DEBUG", 36 | } 37 | 38 | // String returns the string representation of a logging level. 39 | func (p Level) String() string { 40 | return levelNames[p] 41 | } 42 | 43 | // LogLevel returns the log level from a string representation. 44 | func LogLevel(level string) (Level, error) { 45 | for i, name := range levelNames { 46 | if strings.EqualFold(name, level) { 47 | return Level(i), nil 48 | } 49 | } 50 | return ERROR, ErrInvalidLogLevel 51 | } 52 | 53 | // Leveled interface is the interface required to be able to add leveled 54 | // logging. 55 | type Leveled interface { 56 | GetLevel(string) Level 57 | SetLevel(Level, string) 58 | IsEnabledFor(Level, string) bool 59 | } 60 | 61 | // LeveledBackend is a log backend with additional knobs for setting levels on 62 | // individual modules to different levels. 63 | type LeveledBackend interface { 64 | Backend 65 | Leveled 66 | } 67 | 68 | type moduleLeveled struct { 69 | levels map[string]Level 70 | backend Backend 71 | formatter Formatter 72 | once sync.Once 73 | } 74 | 75 | // AddModuleLevel wraps a log backend with knobs to have different log levels 76 | // for different modules. 77 | func AddModuleLevel(backend Backend) LeveledBackend { 78 | var leveled LeveledBackend 79 | var ok bool 80 | if leveled, ok = backend.(LeveledBackend); !ok { 81 | leveled = &moduleLeveled{ 82 | levels: make(map[string]Level), 83 | backend: backend, 84 | } 85 | } 86 | return leveled 87 | } 88 | 89 | // GetLevel returns the log level for the given module. 90 | func (l *moduleLeveled) GetLevel(module string) Level { 91 | level, exists := l.levels[module] 92 | if exists == false { 93 | level, exists = l.levels[""] 94 | // no configuration exists, default to debug 95 | if exists == false { 96 | level = DEBUG 97 | } 98 | } 99 | return level 100 | } 101 | 102 | // SetLevel sets the log level for the given module. 103 | func (l *moduleLeveled) SetLevel(level Level, module string) { 104 | l.levels[module] = level 105 | } 106 | 107 | // IsEnabledFor will return true if logging is enabled for the given module. 108 | func (l *moduleLeveled) IsEnabledFor(level Level, module string) bool { 109 | return level <= l.GetLevel(module) 110 | } 111 | 112 | func (l *moduleLeveled) Log(level Level, calldepth int, rec *Record) (err error) { 113 | if l.IsEnabledFor(level, rec.Module) { 114 | // TODO get rid of traces of formatter here. BackendFormatter should be used. 115 | rec.formatter = l.getFormatterAndCacheCurrent() 116 | err = l.backend.Log(level, calldepth+1, rec) 117 | } 118 | return 119 | } 120 | 121 | func (l *moduleLeveled) getFormatterAndCacheCurrent() Formatter { 122 | l.once.Do(func() { 123 | if l.formatter == nil { 124 | l.formatter = getFormatter() 125 | } 126 | }) 127 | return l.formatter 128 | } 129 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/decode_meta.go: -------------------------------------------------------------------------------- 1 | package toml 2 | 3 | import "strings" 4 | 5 | // MetaData allows access to meta information about TOML data that may not 6 | // be inferrable via reflection. In particular, whether a key has been defined 7 | // and the TOML type of a key. 8 | type MetaData struct { 9 | mapping map[string]interface{} 10 | types map[string]tomlType 11 | keys []Key 12 | decoded map[string]bool 13 | context Key // Used only during decoding. 14 | } 15 | 16 | // IsDefined returns true if the key given exists in the TOML data. The key 17 | // should be specified hierarchially. e.g., 18 | // 19 | // // access the TOML key 'a.b.c' 20 | // IsDefined("a", "b", "c") 21 | // 22 | // IsDefined will return false if an empty key given. Keys are case sensitive. 23 | func (md *MetaData) IsDefined(key ...string) bool { 24 | if len(key) == 0 { 25 | return false 26 | } 27 | 28 | var hash map[string]interface{} 29 | var ok bool 30 | var hashOrVal interface{} = md.mapping 31 | for _, k := range key { 32 | if hash, ok = hashOrVal.(map[string]interface{}); !ok { 33 | return false 34 | } 35 | if hashOrVal, ok = hash[k]; !ok { 36 | return false 37 | } 38 | } 39 | return true 40 | } 41 | 42 | // Type returns a string representation of the type of the key specified. 43 | // 44 | // Type will return the empty string if given an empty key or a key that 45 | // does not exist. Keys are case sensitive. 46 | func (md *MetaData) Type(key ...string) string { 47 | fullkey := strings.Join(key, ".") 48 | if typ, ok := md.types[fullkey]; ok { 49 | return typ.typeString() 50 | } 51 | return "" 52 | } 53 | 54 | // Key is the type of any TOML key, including key groups. Use (MetaData).Keys 55 | // to get values of this type. 56 | type Key []string 57 | 58 | func (k Key) String() string { 59 | return strings.Join(k, ".") 60 | } 61 | 62 | func (k Key) maybeQuotedAll() string { 63 | var ss []string 64 | for i := range k { 65 | ss = append(ss, k.maybeQuoted(i)) 66 | } 67 | return strings.Join(ss, ".") 68 | } 69 | 70 | func (k Key) maybeQuoted(i int) string { 71 | quote := false 72 | for _, c := range k[i] { 73 | if !isBareKeyChar(c) { 74 | quote = true 75 | break 76 | } 77 | } 78 | if quote { 79 | return "\"" + strings.Replace(k[i], "\"", "\\\"", -1) + "\"" 80 | } 81 | return k[i] 82 | } 83 | 84 | func (k Key) add(piece string) Key { 85 | newKey := make(Key, len(k)+1) 86 | copy(newKey, k) 87 | newKey[len(k)] = piece 88 | return newKey 89 | } 90 | 91 | // Keys returns a slice of every key in the TOML data, including key groups. 92 | // Each key is itself a slice, where the first element is the top of the 93 | // hierarchy and the last is the most specific. 94 | // 95 | // The list will have the same order as the keys appeared in the TOML data. 96 | // 97 | // All keys returned are non-empty. 98 | func (md *MetaData) Keys() []Key { 99 | return md.keys 100 | } 101 | 102 | // Undecoded returns all keys that have not been decoded in the order in which 103 | // they appear in the original TOML document. 104 | // 105 | // This includes keys that haven't been decoded because of a Primitive value. 106 | // Once the Primitive value is decoded, the keys will be considered decoded. 107 | // 108 | // Also note that decoding into an empty interface will result in no decoding, 109 | // and so no keys will be considered decoded. 110 | // 111 | // In this sense, the Undecoded keys correspond to keys in the TOML document 112 | // that do not have a concrete type in your representation. 113 | func (md *MetaData) Undecoded() []Key { 114 | undecoded := make([]Key, 0, len(md.keys)) 115 | for _, key := range md.keys { 116 | if !md.decoded[key.String()] { 117 | undecoded = append(undecoded, key) 118 | } 119 | } 120 | return undecoded 121 | } 122 | -------------------------------------------------------------------------------- /backend/min_heap.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import ( 4 | "github.com/beanstalkg/beanstalkg/architecture" 5 | //"os" 6 | //"runtime/pprof" 7 | 8 | "github.com/op/go-logging" 9 | ) 10 | 11 | var log = logging.MustGetLogger("BEANSTALKG") 12 | 13 | type MinHeap struct { 14 | Store []architecture.PriorityQueueItem 15 | 16 | tubeName string 17 | } 18 | 19 | // +++++++++++++ START - PriorityQueue Interface methods +++++++++++++++++ 20 | 21 | func (h *MinHeap) Init(tubeName string) { 22 | h.tubeName = tubeName 23 | //for i := 0; i < 100000; i++ { 24 | // h.Store[i] = 1000000001 25 | //} 26 | } 27 | 28 | func (h *MinHeap) Enqueue(item architecture.PriorityQueueItem) { 29 | // h.Size = h.Size + 1 30 | h.DecreaseKey(item) 31 | item.Enqueued() 32 | } 33 | 34 | func (h *MinHeap) Peek() architecture.PriorityQueueItem { 35 | return h.Min() 36 | } 37 | 38 | func (h *MinHeap) Dequeue() architecture.PriorityQueueItem { 39 | if h.Size() == 1 { 40 | min := h.Store[0] 41 | h.Store = nil 42 | min.Dequeued() 43 | return min 44 | } else if h.Size() > 1 { 45 | min := h.Store[0] 46 | h.Store[0] = h.Store[h.Size()-1] 47 | h.Store = h.Store[:(h.Size() - 1)] 48 | h.MinHeapify(0) 49 | min.Dequeued() 50 | return min 51 | } 52 | return nil 53 | } 54 | 55 | func (h *MinHeap) Find(id string) architecture.PriorityQueueItem { 56 | for _, item := range h.Store { 57 | if item.Id() == id { 58 | return item 59 | } 60 | } 61 | return nil 62 | } 63 | 64 | func (h *MinHeap) Delete(id string) architecture.PriorityQueueItem { 65 | for i, item := range h.Store { 66 | if item.Id() == id { 67 | if len(h.Store) == 1 { 68 | h.Store = nil 69 | } else { 70 | // remove item in the middle 71 | h.Store = append(h.Store[:i], h.Store[i+1:]...) 72 | h.MinHeapify(i) 73 | } 74 | return item 75 | } 76 | } 77 | 78 | return nil 79 | } 80 | 81 | func (h *MinHeap) Size() int { 82 | return len(h.Store) 83 | } 84 | 85 | // +++++++++++++ END - PriorityQueue Interface methods +++++++++++++++++ 86 | 87 | func (h *MinHeap) DecreaseKey(item architecture.PriorityQueueItem) { 88 | // Index of next slot in slice. 89 | i := h.Size() 90 | 91 | h.Store = append(h.Store, item) 92 | 93 | // Re-sort slice to put the new item in the proper place. 94 | for i > 0 && h.Store[h.Parent(i)].Key() > h.Store[i].Key() { 95 | // Swap item locationss. 96 | h.Store[i], h.Store[h.Parent(i)] = h.Store[h.Parent(i)], h.Store[i] 97 | 98 | i = h.Parent(i) 99 | } 100 | } 101 | 102 | func (h *MinHeap) Parent(i int) int { 103 | return i >> 1 104 | } 105 | 106 | func (h *MinHeap) Left(i int) int { 107 | return 2*i + 1 108 | } 109 | 110 | func (h *MinHeap) Right(i int) int { 111 | return 2*i + 2 112 | } 113 | 114 | func (h *MinHeap) MinHeapify(i int) { 115 | // log.Println("i=", i, h.Store[i].Timestamp()) 116 | l := h.Left(i) 117 | r := h.Right(i) 118 | // log.Println("l=", l) 119 | // log.Println("r=", r) 120 | smallest := i 121 | if l < h.Size() { 122 | if left, parent := h.Store[l], h.Store[i]; left.Key() < parent.Key() || 123 | (left.Key() == parent.Key() && 124 | left.Timestamp() < parent.Timestamp()) { 125 | // log.Println("l=", l, h.Store[l].Timestamp()) 126 | smallest = l 127 | } 128 | } 129 | if r < h.Size() { 130 | if right, parent := h.Store[r], h.Store[smallest]; right.Key() < parent.Key() || 131 | (right.Key() == parent.Key() && 132 | right.Timestamp() < parent.Timestamp()) { 133 | // log.Println("r=", r, h.Store[r].Timestamp()) 134 | smallest = r 135 | } 136 | } 137 | // log.Println("smallest=", smallest) 138 | if smallest != i { 139 | // log.Println("smallest=", smallest, ", i=", i) 140 | h.Store[i], h.Store[smallest] = h.Store[smallest], h.Store[i] 141 | 142 | h.MinHeapify(smallest) 143 | } 144 | } 145 | 146 | func (h *MinHeap) Min() architecture.PriorityQueueItem { 147 | if h.Size() > 0 { 148 | return h.Store[0] 149 | } 150 | 151 | return nil 152 | } 153 | -------------------------------------------------------------------------------- /architecture/protocol_config.go: -------------------------------------------------------------------------------- 1 | package architecture 2 | 3 | type CommandParseOptions struct { 4 | ExpectedLength int 5 | WaitingForMore bool 6 | Params []string 7 | Name CommandName 8 | } 9 | 10 | type CommandReplyOptions struct { 11 | Result bool 12 | Message string 13 | Param string 14 | UseJobID bool 15 | } 16 | 17 | var cmdParseOptions map[CommandName]CommandParseOptions 18 | var cmdReplyOptions map[CommandName]CommandReplyOptions 19 | 20 | func init() { 21 | cmdParseOptions = map[CommandName]CommandParseOptions{ 22 | USE: { 23 | Name: USE, 24 | ExpectedLength: 2, 25 | WaitingForMore: false, 26 | Params: []string{"tube"}, 27 | }, 28 | PUT: { 29 | Name: PUT, 30 | ExpectedLength: 5, 31 | WaitingForMore: true, 32 | Params: []string{"pri", "delay", "ttr", "bytes"}, 33 | }, 34 | WATCH: { 35 | Name: WATCH, 36 | ExpectedLength: 2, 37 | WaitingForMore: false, 38 | Params: []string{"tube"}, 39 | }, 40 | IGNORE: { 41 | Name: IGNORE, 42 | ExpectedLength: 2, 43 | WaitingForMore: false, 44 | Params: []string{"tube"}, 45 | }, 46 | RESERVE: { 47 | Name: RESERVE, 48 | ExpectedLength: 1, 49 | WaitingForMore: false, 50 | Params: []string{}, 51 | }, 52 | RESERVE_WITH_TIMEOUT: { 53 | Name: RESERVE_WITH_TIMEOUT, 54 | ExpectedLength: 2, 55 | WaitingForMore: false, 56 | Params: []string{"timeout"}, 57 | }, 58 | DELETE: { 59 | Name: DELETE, 60 | ExpectedLength: 2, 61 | WaitingForMore: false, 62 | Params: []string{"id"}, 63 | }, 64 | RELEASE: { 65 | Name: RELEASE, 66 | ExpectedLength: 4, 67 | WaitingForMore: false, 68 | Params: []string{"id", "pri", "delay"}, 69 | }, 70 | BURY: { 71 | Name: BURY, 72 | ExpectedLength: 3, 73 | WaitingForMore: false, 74 | Params: []string{"id", "pri"}, 75 | }, 76 | TOUCH: { 77 | Name: TOUCH, 78 | ExpectedLength: 2, 79 | WaitingForMore: false, 80 | Params: []string{"id"}, 81 | }, 82 | QUIT: { 83 | Name: QUIT, 84 | ExpectedLength: 1, 85 | WaitingForMore: false, 86 | Params: []string{}, 87 | }, 88 | KICK: { 89 | Name: KICK, 90 | ExpectedLength: 2, 91 | WaitingForMore: false, 92 | Params: []string{"bound"}, 93 | }, 94 | KICK_JOB: { 95 | Name: KICK_JOB, 96 | ExpectedLength: 2, 97 | WaitingForMore: false, 98 | Params: []string{"id"}, 99 | }, 100 | } 101 | 102 | cmdReplyOptions = map[CommandName]CommandReplyOptions{ 103 | USE: { 104 | Result: false, 105 | Message: "USING", 106 | Param: "tube", 107 | UseJobID: false, 108 | }, 109 | PUT: { 110 | Result: false, 111 | Message: "INSERTED", 112 | Param: "", 113 | UseJobID: true, 114 | }, 115 | WATCH: { 116 | Result: false, 117 | Message: "WATCHING", 118 | Param: "count", 119 | UseJobID: false, 120 | }, 121 | IGNORE: { 122 | Result: false, 123 | Message: "WATCHING", 124 | Param: "count", 125 | UseJobID: false, 126 | }, 127 | DELETE: { 128 | Result: false, 129 | Message: "DELETED", 130 | Param: "", 131 | UseJobID: false, 132 | }, 133 | RELEASE: { 134 | Result: false, 135 | Message: "RELEASED", 136 | Param: "", 137 | UseJobID: false, 138 | }, 139 | BURY: { 140 | Result: false, 141 | Message: "BURIED", 142 | Param: "", 143 | UseJobID: false, 144 | }, 145 | TOUCH: { 146 | Result: false, 147 | Message: "INSERTED", 148 | Param: "", 149 | UseJobID: true, 150 | }, 151 | KICK: { 152 | Result: false, 153 | Message: "KICKED", 154 | Param: "", 155 | UseJobID: false, 156 | }, 157 | KICK_JOB: { 158 | Result: false, 159 | Message: "KICKED", 160 | Param: "", 161 | UseJobID: true, 162 | }, 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v1/resolve.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "math" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | // TODO: merge, timestamps, base 60 floats, omap. 10 | 11 | type resolveMapItem struct { 12 | value interface{} 13 | tag string 14 | } 15 | 16 | var resolveTable = make([]byte, 256) 17 | var resolveMap = make(map[string]resolveMapItem) 18 | 19 | func init() { 20 | t := resolveTable 21 | t[int('+')] = 'S' // Sign 22 | t[int('-')] = 'S' 23 | for _, c := range "0123456789" { 24 | t[int(c)] = 'D' // Digit 25 | } 26 | for _, c := range "yYnNtTfFoO~" { 27 | t[int(c)] = 'M' // In map 28 | } 29 | t[int('.')] = '.' // Float (potentially in map) 30 | 31 | var resolveMapList = []struct { 32 | v interface{} 33 | tag string 34 | l []string 35 | }{ 36 | {true, "!!bool", []string{"y", "Y", "yes", "Yes", "YES"}}, 37 | {true, "!!bool", []string{"true", "True", "TRUE"}}, 38 | {true, "!!bool", []string{"on", "On", "ON"}}, 39 | {false, "!!bool", []string{"n", "N", "no", "No", "NO"}}, 40 | {false, "!!bool", []string{"false", "False", "FALSE"}}, 41 | {false, "!!bool", []string{"off", "Off", "OFF"}}, 42 | {nil, "!!null", []string{"~", "null", "Null", "NULL"}}, 43 | {math.NaN(), "!!float", []string{".nan", ".NaN", ".NAN"}}, 44 | {math.Inf(+1), "!!float", []string{".inf", ".Inf", ".INF"}}, 45 | {math.Inf(+1), "!!float", []string{"+.inf", "+.Inf", "+.INF"}}, 46 | {math.Inf(-1), "!!float", []string{"-.inf", "-.Inf", "-.INF"}}, 47 | {"<<", "!!merge", []string{"<<"}}, 48 | } 49 | 50 | m := resolveMap 51 | for _, item := range resolveMapList { 52 | for _, s := range item.l { 53 | m[s] = resolveMapItem{item.v, item.tag} 54 | } 55 | } 56 | } 57 | 58 | const longTagPrefix = "tag:yaml.org,2002:" 59 | 60 | func shortTag(tag string) string { 61 | if strings.HasPrefix(tag, longTagPrefix) { 62 | return "!!" + tag[len(longTagPrefix):] 63 | } 64 | return tag 65 | } 66 | 67 | func resolvableTag(tag string) bool { 68 | switch tag { 69 | case "", "!!str", "!!bool", "!!int", "!!float", "!!null": 70 | return true 71 | } 72 | return false 73 | } 74 | 75 | func resolve(tag string, in string) (rtag string, out interface{}) { 76 | tag = shortTag(tag) 77 | if !resolvableTag(tag) { 78 | return tag, in 79 | } 80 | 81 | defer func() { 82 | if tag != "" && tag != rtag { 83 | panic("Can't decode " + rtag + " '" + in + "' as a " + tag) 84 | } 85 | }() 86 | 87 | if in == "" { 88 | return "!!null", nil 89 | } 90 | 91 | c := resolveTable[in[0]] 92 | if c == 0 { 93 | // It's a string for sure. Nothing to do. 94 | return "!!str", in 95 | } 96 | 97 | // Handle things we can lookup in a map. 98 | if item, ok := resolveMap[in]; ok { 99 | return item.tag, item.value 100 | } 101 | 102 | switch c { 103 | case 'M': 104 | // We've already checked the map above. 105 | 106 | case '.': 107 | // Not in the map, so maybe a normal float. 108 | floatv, err := strconv.ParseFloat(in, 64) 109 | if err == nil { 110 | return "!!float", floatv 111 | } 112 | // XXX Handle base 60 floats here (WTF!) 113 | 114 | case 'D', 'S': 115 | // Int, float, or timestamp. 116 | plain := strings.Replace(in, "_", "", -1) 117 | intv, err := strconv.ParseInt(plain, 0, 64) 118 | if err == nil { 119 | if intv == int64(int(intv)) { 120 | return "!!int", int(intv) 121 | } else { 122 | return "!!int", intv 123 | } 124 | } 125 | floatv, err := strconv.ParseFloat(plain, 64) 126 | if err == nil { 127 | return "!!float", floatv 128 | } 129 | if strings.HasPrefix(plain, "0b") { 130 | intv, err := strconv.ParseInt(plain[2:], 2, 64) 131 | if err == nil { 132 | return "!!int", int(intv) 133 | } 134 | } else if strings.HasPrefix(plain, "-0b") { 135 | intv, err := strconv.ParseInt(plain[3:], 2, 64) 136 | if err == nil { 137 | return "!!int", -int(intv) 138 | } 139 | } 140 | // XXX Handle timestamps here. 141 | 142 | default: 143 | panic("resolveTable item not yet handled: " + 144 | string([]byte{c}) + " (with " + in + ")") 145 | } 146 | return "!!str", in 147 | } 148 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/testify/assert/http_assertions.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "net/http/httptest" 7 | "net/url" 8 | "strings" 9 | ) 10 | 11 | // httpCode is a helper that returns HTTP code of the response. It returns -1 12 | // if building a new request fails. 13 | func httpCode(handler http.HandlerFunc, method, url string, values url.Values) int { 14 | w := httptest.NewRecorder() 15 | req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) 16 | if err != nil { 17 | return -1 18 | } 19 | handler(w, req) 20 | return w.Code 21 | } 22 | 23 | // HTTPSuccess asserts that a specified handler returns a success status code. 24 | // 25 | // assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) 26 | // 27 | // Returns whether the assertion was successful (true) or not (false). 28 | func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 29 | code := httpCode(handler, method, url, values) 30 | if code == -1 { 31 | return false 32 | } 33 | return code >= http.StatusOK && code <= http.StatusPartialContent 34 | } 35 | 36 | // HTTPRedirect asserts that a specified handler returns a redirect status code. 37 | // 38 | // assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} 39 | // 40 | // Returns whether the assertion was successful (true) or not (false). 41 | func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 42 | code := httpCode(handler, method, url, values) 43 | if code == -1 { 44 | return false 45 | } 46 | return code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect 47 | } 48 | 49 | // HTTPError asserts that a specified handler returns an error status code. 50 | // 51 | // assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} 52 | // 53 | // Returns whether the assertion was successful (true) or not (false). 54 | func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values) bool { 55 | code := httpCode(handler, method, url, values) 56 | if code == -1 { 57 | return false 58 | } 59 | return code >= http.StatusBadRequest 60 | } 61 | 62 | // HTTPBody is a helper that returns HTTP body of the response. It returns 63 | // empty string if building a new request fails. 64 | func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { 65 | w := httptest.NewRecorder() 66 | req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) 67 | if err != nil { 68 | return "" 69 | } 70 | handler(w, req) 71 | return w.Body.String() 72 | } 73 | 74 | // HTTPBodyContains asserts that a specified handler returns a 75 | // body that contains a string. 76 | // 77 | // assert.HTTPBodyContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") 78 | // 79 | // Returns whether the assertion was successful (true) or not (false). 80 | func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { 81 | body := HTTPBody(handler, method, url, values) 82 | 83 | contains := strings.Contains(body, fmt.Sprint(str)) 84 | if !contains { 85 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 86 | } 87 | 88 | return contains 89 | } 90 | 91 | // HTTPBodyNotContains asserts that a specified handler returns a 92 | // body that does not contain a string. 93 | // 94 | // assert.HTTPBodyNotContains(t, myHandler, "www.google.com", nil, "I'm Feeling Lucky") 95 | // 96 | // Returns whether the assertion was successful (true) or not (false). 97 | func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}) bool { 98 | body := HTTPBody(handler, method, url, values) 99 | 100 | contains := strings.Contains(body, fmt.Sprint(str)) 101 | if contains { 102 | Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) 103 | } 104 | 105 | return !contains 106 | } 107 | -------------------------------------------------------------------------------- /architecture/tube_test.go: -------------------------------------------------------------------------------- 1 | package architecture 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "github.com/stretchr/testify/mock" 6 | "testing" 7 | ) 8 | 9 | func TestTube_ProcessDelayedQueueWhenLessJobsReadyThanLimit(t *testing.T) { 10 | // === given 11 | testTube := getTestTube(t) 12 | delayed := testTube.delayed.(*MockPriorityQueue) 13 | ready := testTube.ready.(*MockPriorityQueue) 14 | 15 | // return a job two times when Peeking 16 | maxPeeks := 0 17 | delayed.On("Peek").Return(func() PriorityQueueItem { 18 | maxPeeks += 1 19 | if maxPeeks > 2 { 20 | return nil 21 | } 22 | return getDelayedJob() 23 | }) 24 | 25 | // return a job two times when Dequeueing 26 | maxDequeues := 0 27 | delayed.On("Dequeue").Return(func() PriorityQueueItem { 28 | maxDequeues += 1 29 | if maxDequeues > 2 { 30 | return nil 31 | } 32 | return getDelayedJob() 33 | }) 34 | 35 | // === when 36 | // jobs in the delayed queue(2) < limit(5) 37 | testTube.ProcessDelayedQueue(5) 38 | 39 | // === then 40 | ready.AssertNumberOfCalls(t, "Enqueue", 2) 41 | } 42 | 43 | func TestTube_ProcessDelayedQueueWhenMoreJobsReadyThanLimit(t *testing.T) { 44 | // === given 45 | testTube := getTestTube(t) 46 | delayed := testTube.delayed.(*MockPriorityQueue) 47 | ready := testTube.ready.(*MockPriorityQueue) 48 | 49 | // return a job infinite times when Peeking 50 | delayed.On("Peek").Return(func() PriorityQueueItem { 51 | return getDelayedJob() 52 | }) 53 | 54 | // return a job infinite times when Dequeueing 55 | delayed.On("Dequeue").Return(func() PriorityQueueItem { 56 | return getDelayedJob() 57 | }) 58 | 59 | // === when 60 | // jobs in the delayed queue(infinit) > limit(5) 61 | testTube.ProcessDelayedQueue(5) 62 | 63 | // === then 64 | ready.AssertNumberOfCalls(t, "Enqueue", 5) 65 | } 66 | 67 | func TestTube_ProcessReservedQueueWhenLessJobsReadyThanLimit(t *testing.T) { 68 | // === given 69 | testTube := getTestTube(t) 70 | reserved := testTube.reserved.(*MockPriorityQueue) 71 | ready := testTube.ready.(*MockPriorityQueue) 72 | // return a job two times when Peeking 73 | maxPeeks := 0 74 | reserved.On("Peek").Return(func() PriorityQueueItem { 75 | maxPeeks += 1 76 | if maxPeeks > 2 { 77 | return nil 78 | } 79 | return getReservedJob() 80 | }) 81 | 82 | // return a job two times when Dequeueing 83 | maxDequeues := 0 84 | reserved.On("Dequeue").Return(func() PriorityQueueItem { 85 | maxDequeues += 1 86 | if maxDequeues > 2 { 87 | return nil 88 | } 89 | return getReservedJob() 90 | }) 91 | 92 | // === when 93 | // jobs in the reserved queue(infinit) > limit(5) 94 | testTube.ProcessReservedQueue(5) 95 | 96 | // === then 97 | ready.AssertNumberOfCalls(t, "Enqueue", 2) 98 | } 99 | 100 | func TestTube_ProcessReservedQueueWhenMoreJobsReadyThanLimit(t *testing.T) { 101 | // === given 102 | testTube := getTestTube(t) 103 | reserved := testTube.reserved.(*MockPriorityQueue) 104 | ready := testTube.ready.(*MockPriorityQueue) 105 | 106 | // return a job infinite times when Peeking 107 | reserved.On("Peek").Return(func() PriorityQueueItem { 108 | return getReservedJob() 109 | }) 110 | 111 | // return a job infinite times when Dequeueing 112 | reserved.On("Dequeue").Return(func() PriorityQueueItem { 113 | return getReservedJob() 114 | }) 115 | 116 | // === when 117 | // jobs in the reserved queue(infinit) > limit(5) 118 | testTube.ProcessReservedQueue(5) 119 | 120 | // === then 121 | ready.AssertNumberOfCalls(t, "Enqueue", 5) 122 | } 123 | 124 | func getTestTube(t *testing.T) *Tube { 125 | ready := MockPriorityQueue{} 126 | reserved := MockPriorityQueue{} 127 | delayed := MockPriorityQueue{} 128 | buried := MockPriorityQueue{} 129 | 130 | // ready queue must accept the job 131 | ready.On("Enqueue", mock.AnythingOfTypeArgument("*architecture.Job")).Run(func(args mock.Arguments) { 132 | // jobs put to ready queue must have the correct state 133 | job := args.Get(0).(*Job) 134 | assert.Equal(t, READY, job.State()) 135 | }) 136 | 137 | return &Tube{ 138 | Name: "test_tube", 139 | ready: &ready, 140 | reserved: &reserved, 141 | delayed: &delayed, 142 | buried: &buried, 143 | } 144 | } 145 | 146 | func getDelayedJob() *Job { 147 | testJob := NewJob("dummy_job", 0, 0, 1, 1, "dummy data") 148 | testJob.SetState(DELAYED) 149 | return testJob 150 | } 151 | 152 | func getReservedJob() *Job { 153 | testJob := NewJob("dummy_job", 0, 0, 1, 1, "dummy data") 154 | testJob.SetState(RESERVED) 155 | return testJob 156 | } 157 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/configor/README.md: -------------------------------------------------------------------------------- 1 | # Configor 2 | 3 | Golang Configuration tool that support YAML, JSON, TOML, Shell Environment 4 | 5 | ## Usage 6 | 7 | ```go 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "github.com/jinzhu/configor" 13 | ) 14 | 15 | var Config = struct { 16 | APPName string `default:"app name"` 17 | 18 | DB struct { 19 | Name string 20 | User string `default:"root"` 21 | Password string `required:"true" env:"DBPassword"` 22 | Port uint `default:"3306"` 23 | } 24 | 25 | Contacts []struct { 26 | Name string 27 | Email string `required:"true"` 28 | } 29 | }{} 30 | 31 | func main() { 32 | configor.Load(&Config, "config.yml") 33 | fmt.Printf("config: %#v", Config) 34 | } 35 | ``` 36 | 37 | With configuration file *config.yml*: 38 | 39 | ```yaml 40 | appname: test 41 | 42 | db: 43 | name: test 44 | user: test 45 | password: test 46 | port: 1234 47 | 48 | contacts: 49 | - name: i test 50 | email: test@test.com 51 | ``` 52 | 53 | # Advanced Usage 54 | 55 | * Load mutiple configurations 56 | 57 | ```go 58 | // Earlier configurations have higher priority 59 | configor.Load(&Config, "application.yml", "database.json") 60 | ``` 61 | 62 | * Load configuration by environment 63 | 64 | Use `CONFIGOR_ENV` to set environment, if `CONFIGOR_ENV` not set, environment will be `development` by default, and it will be `test` when running tests with `go test` 65 | 66 | ```go 67 | // config.go 68 | configor.Load(&Config, "config.json") 69 | 70 | $ go run config.go 71 | // Will load `config.json`, `config.development.json` if it exists 72 | // `config.development.json` will overwrite `config.json`'s configuration 73 | // You could use this to share same configuration across different environments 74 | 75 | $ CONFIGOR_ENV=production go run config.go 76 | // Will load `config.json`, `config.production.json` if it exists 77 | // `config.production.json` will overwrite `config.json`'s configuration 78 | 79 | $ go test 80 | // Will load `config.json`, `config.test.json` if it exists 81 | // `config.test.json` will overwrite `config.json`'s configuration 82 | 83 | $ CONFIGOR_ENV=production go test 84 | // Will load `config.json`, `config.production.json` if it exists 85 | // `config.production.json` will overwrite `config.json`'s configuration 86 | ``` 87 | 88 | ```go 89 | // Set environment by config 90 | configor.New(&configor.Config{Environment: "production"}).Load(&Config, "config.json") 91 | ``` 92 | 93 | * Example Configuration 94 | 95 | ```go 96 | // config.go 97 | configor.Load(&Config, "config.yml") 98 | 99 | $ go run config.go 100 | // Will load `config.example.yml` automatically if `config.yml` not found and print warning message 101 | ``` 102 | 103 | * Load From Shell Environment 104 | 105 | ```go 106 | $ CONFIGOR_APPNAME="hello world" CONFIGOR_DB_NAME="hello world" go run config.go 107 | // Load configuration from shell environment, it's name is {{prefix}}_FieldName 108 | ``` 109 | 110 | ```go 111 | // You could overwrite the prefix with environment CONFIGOR_ENV_PREFIX, for example: 112 | $ CONFIGOR_ENV_PREFIX="WEB" WEB_APPNAME="hello world" WEB_DB_NAME="hello world" go run config.go 113 | 114 | // Set prefix by config 115 | configor.New(&configor.Config{ENVPrefix: "WEB"}).Load(&Config, "config.json") 116 | ``` 117 | 118 | * Anonymous Struct 119 | 120 | Add the `anonymous:"true"` tag to an anonymous, embedded struct to NOT include the struct name in the environment 121 | variable of any contained fields. For example: 122 | 123 | ```go 124 | type Details struct { 125 | Description string 126 | } 127 | 128 | type Config struct { 129 | Details `anonymous:"true"` 130 | } 131 | ``` 132 | 133 | With the `anonymous:"true"` tag specified, the environment variable for the `Description` field is `CONFIGOR_DESCRIPTION`. 134 | Without the `anonymous:"true"`tag specified, then environment variable would include the embedded struct name and be `CONFIGOR_DETAILS_DESCRIPTION`. 135 | 136 | * With flags 137 | 138 | ```go 139 | func main() { 140 | config := flag.String("file", "config.yml", "configuration file") 141 | flag.StringVar(&Config.APPName, "name", "", "app name") 142 | flag.StringVar(&Config.DB.Name, "db-name", "", "database name") 143 | flag.StringVar(&Config.DB.User, "db-user", "root", "database user") 144 | flag.Parse() 145 | 146 | os.Setenv("CONFIGOR_ENV_PREFIX", "-") 147 | configor.Load(&Config, *config) 148 | // configor.Load(&Config) // only load configurations from shell env & flag 149 | } 150 | ``` 151 | 152 | ## Supporting the project 153 | 154 | [![http://patreon.com/jinzhu](http://patreon_public_assets.s3.amazonaws.com/sized/becomeAPatronBanner.png)](http://patreon.com/jinzhu) 155 | 156 | 157 | ## Author 158 | 159 | **jinzhu** 160 | 161 | * 162 | * 163 | * 164 | 165 | ## License 166 | 167 | Released under the MIT License 168 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/accessors.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // arrayAccesRegexString is the regex used to extract the array number 11 | // from the access path 12 | const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$` 13 | 14 | // arrayAccesRegex is the compiled arrayAccesRegexString 15 | var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString) 16 | 17 | // Get gets the value using the specified selector and 18 | // returns it inside a new Obj object. 19 | // 20 | // If it cannot find the value, Get will return a nil 21 | // value inside an instance of Obj. 22 | // 23 | // Get can only operate directly on map[string]interface{} and []interface. 24 | // 25 | // Example 26 | // 27 | // To access the title of the third chapter of the second book, do: 28 | // 29 | // o.Get("books[1].chapters[2].title") 30 | func (m Map) Get(selector string) *Value { 31 | rawObj := access(m, selector, nil, false, false) 32 | return &Value{data: rawObj} 33 | } 34 | 35 | // Set sets the value using the specified selector and 36 | // returns the object on which Set was called. 37 | // 38 | // Set can only operate directly on map[string]interface{} and []interface 39 | // 40 | // Example 41 | // 42 | // To set the title of the third chapter of the second book, do: 43 | // 44 | // o.Set("books[1].chapters[2].title","Time to Go") 45 | func (m Map) Set(selector string, value interface{}) Map { 46 | access(m, selector, value, true, false) 47 | return m 48 | } 49 | 50 | // access accesses the object using the selector and performs the 51 | // appropriate action. 52 | func access(current, selector, value interface{}, isSet, panics bool) interface{} { 53 | 54 | switch selector.(type) { 55 | case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 56 | 57 | if array, ok := current.([]interface{}); ok { 58 | index := intFromInterface(selector) 59 | 60 | if index >= len(array) { 61 | if panics { 62 | panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array))) 63 | } 64 | return nil 65 | } 66 | 67 | return array[index] 68 | } 69 | 70 | return nil 71 | 72 | case string: 73 | 74 | selStr := selector.(string) 75 | selSegs := strings.SplitN(selStr, PathSeparator, 2) 76 | thisSel := selSegs[0] 77 | index := -1 78 | var err error 79 | 80 | // https://github.com/stretchr/objx/issues/12 81 | if strings.Contains(thisSel, "[") { 82 | 83 | arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel) 84 | 85 | if len(arrayMatches) > 0 { 86 | 87 | // Get the key into the map 88 | thisSel = arrayMatches[1] 89 | 90 | // Get the index into the array at the key 91 | index, err = strconv.Atoi(arrayMatches[2]) 92 | 93 | if err != nil { 94 | // This should never happen. If it does, something has gone 95 | // seriously wrong. Panic. 96 | panic("objx: Array index is not an integer. Must use array[int].") 97 | } 98 | 99 | } 100 | } 101 | 102 | if curMap, ok := current.(Map); ok { 103 | current = map[string]interface{}(curMap) 104 | } 105 | 106 | // get the object in question 107 | switch current.(type) { 108 | case map[string]interface{}: 109 | curMSI := current.(map[string]interface{}) 110 | if len(selSegs) <= 1 && isSet { 111 | curMSI[thisSel] = value 112 | return nil 113 | } else { 114 | current = curMSI[thisSel] 115 | } 116 | default: 117 | current = nil 118 | } 119 | 120 | if current == nil && panics { 121 | panic(fmt.Sprintf("objx: '%v' invalid on object.", selector)) 122 | } 123 | 124 | // do we need to access the item of an array? 125 | if index > -1 { 126 | if array, ok := current.([]interface{}); ok { 127 | if index < len(array) { 128 | current = array[index] 129 | } else { 130 | if panics { 131 | panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array))) 132 | } 133 | current = nil 134 | } 135 | } 136 | } 137 | 138 | if len(selSegs) > 1 { 139 | current = access(current, selSegs[1], value, isSet, panics) 140 | } 141 | 142 | } 143 | 144 | return current 145 | 146 | } 147 | 148 | // intFromInterface converts an interface object to the largest 149 | // representation of an unsigned integer using a type switch and 150 | // assertions 151 | func intFromInterface(selector interface{}) int { 152 | var value int 153 | switch selector.(type) { 154 | case int: 155 | value = selector.(int) 156 | case int8: 157 | value = int(selector.(int8)) 158 | case int16: 159 | value = int(selector.(int16)) 160 | case int32: 161 | value = int(selector.(int32)) 162 | case int64: 163 | value = int(selector.(int64)) 164 | case uint: 165 | value = int(selector.(uint)) 166 | case uint8: 167 | value = int(selector.(uint8)) 168 | case uint16: 169 | value = int(selector.(uint16)) 170 | case uint32: 171 | value = int(selector.(uint32)) 172 | case uint64: 173 | value = int(selector.(uint64)) 174 | default: 175 | panic("objx: array access argument is not an integer type (this should never happen)") 176 | } 177 | 178 | return value 179 | } 180 | -------------------------------------------------------------------------------- /architecture/job.go: -------------------------------------------------------------------------------- 1 | package architecture 2 | 3 | import ( 4 | "strconv" 5 | "time" 6 | 7 | "github.com/satori/go.uuid" 8 | ) 9 | 10 | type State int 11 | 12 | const ( // iota is reset to 0 13 | READY State = iota // = 0 14 | DELAYED // = 1 15 | RESERVED // = 2 16 | BURIED // = 3 17 | ) 18 | 19 | const NANO = 1000000000 20 | 21 | type Job struct { 22 | id string 23 | Pri int64 24 | Delay int64 // time set as delay in seconds 25 | StartedDelayAt int64 // timestamp of when it was set to delayed 26 | StartedTTRAt int64 // timestamp of when it was reserved 27 | TTR int64 // time set as ttr in seconds 28 | Bytes int64 29 | Data string 30 | 31 | // states 32 | state State 33 | 34 | timestamp int64 35 | } 36 | 37 | func NewJob(id string, pri, delay, ttr, bytes int64, data string) *Job { 38 | job := &Job{ 39 | id: id, 40 | Pri: pri, 41 | Delay: delay, 42 | TTR: ttr, 43 | Bytes: bytes, 44 | Data: data, 45 | } 46 | if job.Delay <= 0 { 47 | job.state = READY 48 | // add to the ready queue 49 | } else { 50 | job.state = DELAYED 51 | job.StartedDelayAt = time.Now().UnixNano() 52 | // add to the delayed queue 53 | } 54 | job.timestamp = time.Now().UnixNano() 55 | return job 56 | } 57 | 58 | /** 59 | 60 | put with delay release with delay 61 | ----------------> [DELAYED] <------------. 62 | | | 63 | | (time passes) | 64 | | | 65 | put v reserve | delete 66 | -----------------> [READY] ---------> [RESERVED] --------> *poof* 67 | ^ ^ | | 68 | | \ release | | 69 | | `-------------' | 70 | | | 71 | | kick | 72 | | | 73 | | bury | 74 | [BURIED] <---------------' 75 | | 76 | | delete 77 | `--------> *poof* 78 | */ 79 | func (job *Job) SetState(state State) error { 80 | // Ensure the desired to state is valid. 81 | validFrom, ok := validTransitionsTo[state] 82 | if !ok { 83 | return nil 84 | } 85 | 86 | // The to state is valid. Now ensure that we are coming from 87 | // a valid state. 88 | if validp, _ := validFrom[job.state]; !validp { 89 | return transitionErrors[state] 90 | } 91 | 92 | // All is well here. 93 | job.state = state 94 | 95 | // If a timer needs updating, do so. 96 | if f, ok := updateFuncs[state]; ok { 97 | f(job, time.Now().UnixNano()) 98 | } 99 | 100 | return nil 101 | } 102 | 103 | func (job *Job) State() State { 104 | return job.state 105 | } 106 | 107 | // Return proper key according to the present job state 108 | func (job *Job) Key() int64 { 109 | switch job.state { 110 | case READY: 111 | return job.Pri 112 | case DELAYED: 113 | // time remaining from Delay till it gets ready becomes priority 114 | // log.Println(job.Delay * NANO, time.Now().UnixNano(), job.StartedDelayAt) 115 | return job.Delay*NANO - (time.Now().UnixNano() - job.StartedDelayAt) 116 | case RESERVED: 117 | // time remaining from TTR till it gets ready becomes the priority 118 | return job.TTR*NANO - (time.Now().UnixNano() - job.StartedTTRAt) 119 | } 120 | return 0 121 | } 122 | 123 | func (job *Job) Id() string { 124 | return job.id 125 | } 126 | 127 | func (job *Job) Timestamp() int64 { 128 | return job.timestamp 129 | } 130 | 131 | func (job *Job) Enqueued() { 132 | job.timestamp = time.Now().UnixNano() 133 | } 134 | 135 | func (job *Job) Dequeued() { 136 | job.timestamp = time.Now().UnixNano() 137 | } 138 | 139 | // AwaitingClient stores an awaiting client send channel for a tube 140 | type AwaitingClient struct { 141 | id string 142 | SendChannel chan Command 143 | Request Command 144 | QueuedAt int64 145 | Timeout int64 146 | } 147 | 148 | func NewAwaitingClient(request Command, sendChannel chan Command) *AwaitingClient { 149 | a := new(AwaitingClient) 150 | a.id = uuid.NewV1().String() 151 | a.Request = request 152 | a.SendChannel = sendChannel 153 | a.QueuedAt = time.Now().UnixNano() 154 | a.Timeout = -1 155 | if _, ok := request.Params["timeout"]; ok { 156 | timeout, err := strconv.ParseInt(request.Params["timeout"], 10, 0) 157 | if err == nil { 158 | a.Timeout = timeout * NANO // convert to nano seconds 159 | } else { 160 | log.Error(err) 161 | } 162 | } 163 | // log.Println(a) 164 | return a 165 | } 166 | 167 | func (w *AwaitingClient) Key() int64 { 168 | return w.QueuedAt 169 | } 170 | 171 | func (w *AwaitingClient) Timeleft() int64 { 172 | timeleft := w.Timeout - (time.Now().UnixNano() - w.QueuedAt) 173 | // log.Println(timeleft, w.Timeout, time.Now().UnixNano(), w.QueuedAt) 174 | return timeleft 175 | } 176 | 177 | func (w *AwaitingClient) Id() string { 178 | return w.id 179 | } 180 | 181 | func (w *AwaitingClient) Timestamp() int64 { 182 | return w.QueuedAt 183 | } 184 | 185 | func (w *AwaitingClient) Enqueued() { 186 | w.QueuedAt = time.Now().UnixNano() 187 | } 188 | 189 | func (w *AwaitingClient) Dequeued() { 190 | } 191 | -------------------------------------------------------------------------------- /vendor/github.com/BurntSushi/toml/README.md: -------------------------------------------------------------------------------- 1 | ## TOML parser and encoder for Go with reflection 2 | 3 | TOML stands for Tom's Obvious, Minimal Language. This Go package provides a 4 | reflection interface similar to Go's standard library `json` and `xml` 5 | packages. This package also supports the `encoding.TextUnmarshaler` and 6 | `encoding.TextMarshaler` interfaces so that you can define custom data 7 | representations. (There is an example of this below.) 8 | 9 | Spec: https://github.com/toml-lang/toml 10 | 11 | Compatible with TOML version 12 | [v0.4.0](https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md) 13 | 14 | Documentation: https://godoc.org/github.com/BurntSushi/toml 15 | 16 | Installation: 17 | 18 | ```bash 19 | go get github.com/BurntSushi/toml 20 | ``` 21 | 22 | Try the toml validator: 23 | 24 | ```bash 25 | go get github.com/BurntSushi/toml/cmd/tomlv 26 | tomlv some-toml-file.toml 27 | ``` 28 | 29 | [![Build Status](https://travis-ci.org/BurntSushi/toml.svg?branch=master)](https://travis-ci.org/BurntSushi/toml) [![GoDoc](https://godoc.org/github.com/BurntSushi/toml?status.svg)](https://godoc.org/github.com/BurntSushi/toml) 30 | 31 | ### Testing 32 | 33 | This package passes all tests in 34 | [toml-test](https://github.com/BurntSushi/toml-test) for both the decoder 35 | and the encoder. 36 | 37 | ### Examples 38 | 39 | This package works similarly to how the Go standard library handles `XML` 40 | and `JSON`. Namely, data is loaded into Go values via reflection. 41 | 42 | For the simplest example, consider some TOML file as just a list of keys 43 | and values: 44 | 45 | ```toml 46 | Age = 25 47 | Cats = [ "Cauchy", "Plato" ] 48 | Pi = 3.14 49 | Perfection = [ 6, 28, 496, 8128 ] 50 | DOB = 1987-07-05T05:45:00Z 51 | ``` 52 | 53 | Which could be defined in Go as: 54 | 55 | ```go 56 | type Config struct { 57 | Age int 58 | Cats []string 59 | Pi float64 60 | Perfection []int 61 | DOB time.Time // requires `import time` 62 | } 63 | ``` 64 | 65 | And then decoded with: 66 | 67 | ```go 68 | var conf Config 69 | if _, err := toml.Decode(tomlData, &conf); err != nil { 70 | // handle error 71 | } 72 | ``` 73 | 74 | You can also use struct tags if your struct field name doesn't map to a TOML 75 | key value directly: 76 | 77 | ```toml 78 | some_key_NAME = "wat" 79 | ``` 80 | 81 | ```go 82 | type TOML struct { 83 | ObscureKey string `toml:"some_key_NAME"` 84 | } 85 | ``` 86 | 87 | ### Using the `encoding.TextUnmarshaler` interface 88 | 89 | Here's an example that automatically parses duration strings into 90 | `time.Duration` values: 91 | 92 | ```toml 93 | [[song]] 94 | name = "Thunder Road" 95 | duration = "4m49s" 96 | 97 | [[song]] 98 | name = "Stairway to Heaven" 99 | duration = "8m03s" 100 | ``` 101 | 102 | Which can be decoded with: 103 | 104 | ```go 105 | type song struct { 106 | Name string 107 | Duration duration 108 | } 109 | type songs struct { 110 | Song []song 111 | } 112 | var favorites songs 113 | if _, err := toml.Decode(blob, &favorites); err != nil { 114 | log.Fatal(err) 115 | } 116 | 117 | for _, s := range favorites.Song { 118 | fmt.Printf("%s (%s)\n", s.Name, s.Duration) 119 | } 120 | ``` 121 | 122 | And you'll also need a `duration` type that satisfies the 123 | `encoding.TextUnmarshaler` interface: 124 | 125 | ```go 126 | type duration struct { 127 | time.Duration 128 | } 129 | 130 | func (d *duration) UnmarshalText(text []byte) error { 131 | var err error 132 | d.Duration, err = time.ParseDuration(string(text)) 133 | return err 134 | } 135 | ``` 136 | 137 | ### More complex usage 138 | 139 | Here's an example of how to load the example from the official spec page: 140 | 141 | ```toml 142 | # This is a TOML document. Boom. 143 | 144 | title = "TOML Example" 145 | 146 | [owner] 147 | name = "Tom Preston-Werner" 148 | organization = "GitHub" 149 | bio = "GitHub Cofounder & CEO\nLikes tater tots and beer." 150 | dob = 1979-05-27T07:32:00Z # First class dates? Why not? 151 | 152 | [database] 153 | server = "192.168.1.1" 154 | ports = [ 8001, 8001, 8002 ] 155 | connection_max = 5000 156 | enabled = true 157 | 158 | [servers] 159 | 160 | # You can indent as you please. Tabs or spaces. TOML don't care. 161 | [servers.alpha] 162 | ip = "10.0.0.1" 163 | dc = "eqdc10" 164 | 165 | [servers.beta] 166 | ip = "10.0.0.2" 167 | dc = "eqdc10" 168 | 169 | [clients] 170 | data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it 171 | 172 | # Line breaks are OK when inside arrays 173 | hosts = [ 174 | "alpha", 175 | "omega" 176 | ] 177 | ``` 178 | 179 | And the corresponding Go types are: 180 | 181 | ```go 182 | type tomlConfig struct { 183 | Title string 184 | Owner ownerInfo 185 | DB database `toml:"database"` 186 | Servers map[string]server 187 | Clients clients 188 | } 189 | 190 | type ownerInfo struct { 191 | Name string 192 | Org string `toml:"organization"` 193 | Bio string 194 | DOB time.Time 195 | } 196 | 197 | type database struct { 198 | Server string 199 | Ports []int 200 | ConnMax int `toml:"connection_max"` 201 | Enabled bool 202 | } 203 | 204 | type server struct { 205 | IP string 206 | DC string 207 | } 208 | 209 | type clients struct { 210 | Data [][]interface{} 211 | Hosts []string 212 | } 213 | ``` 214 | 215 | Note that a case insensitive match will be tried if an exact match can't be 216 | found. 217 | 218 | A working example of the above can be found in `_examples/example.{go,toml}`. 219 | -------------------------------------------------------------------------------- /vendor/github.com/jinzhu/configor/utils.go: -------------------------------------------------------------------------------- 1 | package configor 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "path" 10 | "reflect" 11 | "strings" 12 | 13 | "github.com/BurntSushi/toml" 14 | yaml "gopkg.in/yaml.v1" 15 | ) 16 | 17 | func (configor *Configor) getENVPrefix(config interface{}) string { 18 | if configor.Config.ENVPrefix == "" { 19 | if prefix := os.Getenv("CONFIGOR_ENV_PREFIX"); prefix != "" { 20 | return prefix 21 | } 22 | return "Configor" 23 | } 24 | return configor.Config.ENVPrefix 25 | } 26 | 27 | func getConfigurationFileWithENVPrefix(file, env string) (string, error) { 28 | var ( 29 | envFile string 30 | extname = path.Ext(file) 31 | ) 32 | 33 | if extname == "" { 34 | envFile = fmt.Sprintf("%v.%v", file, env) 35 | } else { 36 | envFile = fmt.Sprintf("%v.%v%v", strings.TrimSuffix(file, extname), env, extname) 37 | } 38 | 39 | if fileInfo, err := os.Stat(envFile); err == nil && fileInfo.Mode().IsRegular() { 40 | return envFile, nil 41 | } 42 | return "", fmt.Errorf("failed to find file %v", file) 43 | } 44 | 45 | func (configor *Configor) getConfigurationFiles(files ...string) []string { 46 | var results []string 47 | 48 | for i := len(files) - 1; i >= 0; i-- { 49 | foundFile := false 50 | file := files[i] 51 | 52 | // check configuration 53 | if fileInfo, err := os.Stat(file); err == nil && fileInfo.Mode().IsRegular() { 54 | foundFile = true 55 | results = append(results, file) 56 | } 57 | 58 | // check configuration with env 59 | if file, err := getConfigurationFileWithENVPrefix(file, configor.GetEnvironment()); err == nil { 60 | foundFile = true 61 | results = append(results, file) 62 | } 63 | 64 | // check example configuration 65 | if !foundFile { 66 | if example, err := getConfigurationFileWithENVPrefix(file, "example"); err == nil { 67 | fmt.Printf("Failed to find configuration %v, using example file %v\n", file, example) 68 | results = append(results, example) 69 | } else { 70 | fmt.Printf("Failed to find configuration %v\n", file) 71 | } 72 | } 73 | } 74 | return results 75 | } 76 | 77 | func processFile(config interface{}, file string) error { 78 | data, err := ioutil.ReadFile(file) 79 | if err != nil { 80 | return err 81 | } 82 | 83 | switch { 84 | case strings.HasSuffix(file, ".yaml") || strings.HasSuffix(file, ".yml"): 85 | return yaml.Unmarshal(data, config) 86 | case strings.HasSuffix(file, ".toml"): 87 | return toml.Unmarshal(data, config) 88 | case strings.HasSuffix(file, ".json"): 89 | return json.Unmarshal(data, config) 90 | default: 91 | if toml.Unmarshal(data, config) != nil { 92 | if json.Unmarshal(data, config) != nil { 93 | if yaml.Unmarshal(data, config) != nil { 94 | return errors.New("failed to decode config") 95 | } 96 | } 97 | } 98 | return nil 99 | } 100 | } 101 | 102 | func getPrefixForStruct(prefixes []string, fieldStruct *reflect.StructField) []string { 103 | if fieldStruct.Anonymous && fieldStruct.Tag.Get("anonymous") == "true" { 104 | return prefixes 105 | } 106 | return append(prefixes, fieldStruct.Name) 107 | } 108 | 109 | func processTags(config interface{}, prefixes ...string) error { 110 | configValue := reflect.Indirect(reflect.ValueOf(config)) 111 | if configValue.Kind() != reflect.Struct { 112 | return errors.New("invalid config, should be struct") 113 | } 114 | 115 | configType := configValue.Type() 116 | for i := 0; i < configType.NumField(); i++ { 117 | var ( 118 | envNames []string 119 | fieldStruct = configType.Field(i) 120 | field = configValue.Field(i) 121 | envName = fieldStruct.Tag.Get("env") // read configuration from shell env 122 | ) 123 | 124 | if envName == "" { 125 | envNames = append(envNames, strings.Join(append(prefixes, fieldStruct.Name), "_")) // Configor_DB_Name 126 | envNames = append(envNames, strings.ToUpper(strings.Join(append(prefixes, fieldStruct.Name), "_"))) // CONFIGOR_DB_NAME 127 | } else { 128 | envNames = []string{envName} 129 | } 130 | 131 | // Load From Shell ENV 132 | for _, env := range envNames { 133 | if value := os.Getenv(env); value != "" { 134 | if err := yaml.Unmarshal([]byte(value), field.Addr().Interface()); err != nil { 135 | return err 136 | } 137 | break 138 | } 139 | } 140 | 141 | if isBlank := reflect.DeepEqual(field.Interface(), reflect.Zero(field.Type()).Interface()); isBlank { 142 | // Set default configuration if blank 143 | if value := fieldStruct.Tag.Get("default"); value != "" { 144 | if err := yaml.Unmarshal([]byte(value), field.Addr().Interface()); err != nil { 145 | return err 146 | } 147 | } else if fieldStruct.Tag.Get("required") == "true" { 148 | // return error if it is required but blank 149 | return errors.New(fieldStruct.Name + " is required, but blank") 150 | } 151 | } 152 | 153 | for field.Kind() == reflect.Ptr { 154 | field = field.Elem() 155 | } 156 | 157 | if field.Kind() == reflect.Struct { 158 | if err := processTags(field.Addr().Interface(), getPrefixForStruct(prefixes, &fieldStruct)...); err != nil { 159 | return err 160 | } 161 | } 162 | 163 | if field.Kind() == reflect.Slice { 164 | for i := 0; i < field.Len(); i++ { 165 | if reflect.Indirect(field.Index(i)).Kind() == reflect.Struct { 166 | if err := processTags(field.Index(i).Addr().Interface(), append(getPrefixForStruct(prefixes, &fieldStruct), fmt.Sprint(i))...); err != nil { 167 | return err 168 | } 169 | } 170 | } 171 | } 172 | } 173 | return nil 174 | } 175 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v1/yamlprivateh.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | const ( 4 | // The size of the input raw buffer. 5 | input_raw_buffer_size = 512 6 | 7 | // The size of the input buffer. 8 | // It should be possible to decode the whole raw buffer. 9 | input_buffer_size = input_raw_buffer_size * 3 10 | 11 | // The size of the output buffer. 12 | output_buffer_size = 128 13 | 14 | // The size of the output raw buffer. 15 | // It should be possible to encode the whole output buffer. 16 | output_raw_buffer_size = (output_buffer_size*2 + 2) 17 | 18 | // The size of other stacks and queues. 19 | initial_stack_size = 16 20 | initial_queue_size = 16 21 | initial_string_size = 16 22 | ) 23 | 24 | // Check if the character at the specified position is an alphabetical 25 | // character, a digit, '_', or '-'. 26 | func is_alpha(b []byte, i int) bool { 27 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' 28 | } 29 | 30 | // Check if the character at the specified position is a digit. 31 | func is_digit(b []byte, i int) bool { 32 | return b[i] >= '0' && b[i] <= '9' 33 | } 34 | 35 | // Get the value of a digit. 36 | func as_digit(b []byte, i int) int { 37 | return int(b[i]) - '0' 38 | } 39 | 40 | // Check if the character at the specified position is a hex-digit. 41 | func is_hex(b []byte, i int) bool { 42 | return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' 43 | } 44 | 45 | // Get the value of a hex-digit. 46 | func as_hex(b []byte, i int) int { 47 | bi := b[i] 48 | if bi >= 'A' && bi <= 'F' { 49 | return int(bi) - 'A' + 10 50 | } 51 | if bi >= 'a' && bi <= 'f' { 52 | return int(bi) - 'a' + 10 53 | } 54 | return int(bi) - '0' 55 | } 56 | 57 | // Check if the character is ASCII. 58 | func is_ascii(b []byte, i int) bool { 59 | return b[i] <= 0x7F 60 | } 61 | 62 | // Check if the character at the start of the buffer can be printed unescaped. 63 | func is_printable(b []byte, i int) bool { 64 | return ((b[i] == 0x0A) || // . == #x0A 65 | (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E 66 | (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF 67 | (b[i] > 0xC2 && b[i] < 0xED) || 68 | (b[i] == 0xED && b[i+1] < 0xA0) || 69 | (b[i] == 0xEE) || 70 | (b[i] == 0xEF && // #xE000 <= . <= #xFFFD 71 | !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF 72 | !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) 73 | } 74 | 75 | // Check if the character at the specified position is NUL. 76 | func is_z(b []byte, i int) bool { 77 | return b[i] == 0x00 78 | } 79 | 80 | // Check if the beginning of the buffer is a BOM. 81 | func is_bom(b []byte, i int) bool { 82 | return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF 83 | } 84 | 85 | // Check if the character at the specified position is space. 86 | func is_space(b []byte, i int) bool { 87 | return b[i] == ' ' 88 | } 89 | 90 | // Check if the character at the specified position is tab. 91 | func is_tab(b []byte, i int) bool { 92 | return b[i] == '\t' 93 | } 94 | 95 | // Check if the character at the specified position is blank (space or tab). 96 | func is_blank(b []byte, i int) bool { 97 | //return is_space(b, i) || is_tab(b, i) 98 | return b[i] == ' ' || b[i] == '\t' 99 | } 100 | 101 | // Check if the character at the specified position is a line break. 102 | func is_break(b []byte, i int) bool { 103 | return (b[i] == '\r' || // CR (#xD) 104 | b[i] == '\n' || // LF (#xA) 105 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 106 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 107 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) 108 | } 109 | 110 | func is_crlf(b []byte, i int) bool { 111 | return b[i] == '\r' && b[i+1] == '\n' 112 | } 113 | 114 | // Check if the character is a line break or NUL. 115 | func is_breakz(b []byte, i int) bool { 116 | //return is_break(b, i) || is_z(b, i) 117 | return ( // is_break: 118 | b[i] == '\r' || // CR (#xD) 119 | b[i] == '\n' || // LF (#xA) 120 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 121 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 122 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 123 | // is_z: 124 | b[i] == 0) 125 | } 126 | 127 | // Check if the character is a line break, space, or NUL. 128 | func is_spacez(b []byte, i int) bool { 129 | //return is_space(b, i) || is_breakz(b, i) 130 | return ( // is_space: 131 | b[i] == ' ' || 132 | // is_breakz: 133 | b[i] == '\r' || // CR (#xD) 134 | b[i] == '\n' || // LF (#xA) 135 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 136 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 137 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 138 | b[i] == 0) 139 | } 140 | 141 | // Check if the character is a line break, space, tab, or NUL. 142 | func is_blankz(b []byte, i int) bool { 143 | //return is_blank(b, i) || is_breakz(b, i) 144 | return ( // is_blank: 145 | b[i] == ' ' || b[i] == '\t' || 146 | // is_breakz: 147 | b[i] == '\r' || // CR (#xD) 148 | b[i] == '\n' || // LF (#xA) 149 | b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) 150 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) 151 | b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) 152 | b[i] == 0) 153 | } 154 | 155 | // Determine the width of the character. 156 | func width(b byte) int { 157 | // Don't replace these by a switch without first 158 | // confirming that it is being inlined. 159 | if b&0x80 == 0x00 { 160 | return 1 161 | } 162 | if b&0xE0 == 0xC0 { 163 | return 2 164 | } 165 | if b&0xF0 == 0xE0 { 166 | return 3 167 | } 168 | if b&0xF8 == 0xF0 { 169 | return 4 170 | } 171 | return 0 172 | 173 | } 174 | -------------------------------------------------------------------------------- /testintegration/util.go: -------------------------------------------------------------------------------- 1 | package testintegration 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "net" 9 | "net/textproto" 10 | "strconv" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | // A Conn represents a connection to a beanstalkd server. 16 | type Conn struct { 17 | c *textproto.Conn 18 | } 19 | 20 | var ( 21 | space = []byte{' '} 22 | crnl = []byte{'\r', '\n'} 23 | yamlHead = []byte{'-', '-', '-', '\n'} 24 | nl = []byte{'\n'} 25 | colonSpace = []byte{':', ' '} 26 | minusSpace = []byte{'-', ' '} 27 | ) 28 | 29 | // NewConn returns a new Conn using conn for I/O. 30 | func NewConn(conn io.ReadWriteCloser) *Conn { 31 | c := new(Conn) 32 | c.c = textproto.NewConn(conn) 33 | return c 34 | } 35 | 36 | // Dial connects to the given address on the given network using net.Dial 37 | // and then returns a new Conn for the connection. 38 | func dial(network, addr string) (*Conn, error) { 39 | c, err := net.Dial(network, addr) 40 | if err != nil { 41 | return nil, err 42 | } 43 | return NewConn(c), nil 44 | } 45 | 46 | // Close closes the underlying network connection. 47 | func (c *Conn) Close() error { 48 | return c.c.Close() 49 | } 50 | 51 | func (c *Conn) cmd(body []byte, op string, args ...interface{}) (req, error) { 52 | r := req{c.c.Next(), op} 53 | c.c.StartRequest(r.id) 54 | if body != nil { 55 | args = append(args, len(body)) 56 | } 57 | c.printLine(string(op), args...) 58 | if body != nil { 59 | c.c.W.Write(body) 60 | c.c.W.Write(crnl) 61 | } 62 | err := c.c.W.Flush() 63 | if err != nil { 64 | return req{}, ConnError{c, op, err} 65 | } 66 | c.c.EndRequest(r.id) 67 | return r, nil 68 | } 69 | 70 | // does not flush 71 | func (c *Conn) printLine(cmd string, args ...interface{}) { 72 | io.WriteString(c.c.W, cmd) 73 | for _, a := range args { 74 | c.c.W.Write(space) 75 | fmt.Fprint(c.c.W, a) 76 | } 77 | c.c.W.Write(crnl) 78 | } 79 | 80 | func (c *Conn) readResp(r req, readBody bool, f string, a ...interface{}) (body []byte, err error) { 81 | c.c.StartResponse(r.id) 82 | defer c.c.EndResponse(r.id) 83 | line, err := c.c.ReadLine() 84 | for strings.HasPrefix(line, "WATCHING ") || strings.HasPrefix(line, "USING ") { 85 | line, err = c.c.ReadLine() 86 | } 87 | if err != nil { 88 | return nil, ConnError{c, r.op, err} 89 | } 90 | toScan := line 91 | if readBody { 92 | var size int 93 | toScan, size, err = parseSize(toScan) 94 | if err != nil { 95 | return nil, ConnError{c, r.op, err} 96 | } 97 | body = make([]byte, size+2) // include trailing CR NL 98 | _, err = io.ReadFull(c.c.R, body) 99 | if err != nil { 100 | return nil, ConnError{c, r.op, err} 101 | } 102 | body = body[:size] // exclude trailing CR NL 103 | } 104 | 105 | err = scan(toScan, f, a...) 106 | if err != nil { 107 | return nil, ConnError{c, r.op, err} 108 | } 109 | return body, nil 110 | } 111 | 112 | func scan(input, format string, a ...interface{}) error { 113 | _, err := fmt.Sscanf(input, format, a...) 114 | if err != nil { 115 | return findRespError(input) 116 | } 117 | return nil 118 | } 119 | 120 | type req struct { 121 | id uint 122 | op string 123 | } 124 | 125 | // ConnError records an error message from the server and the operation 126 | // and connection that caused it. 127 | type ConnError struct { 128 | Conn *Conn 129 | Op string 130 | Err error 131 | } 132 | 133 | func (e ConnError) Error() string { 134 | return e.Op + ": " + e.Err.Error() 135 | } 136 | 137 | // Error messages returned by the server. 138 | var ( 139 | ErrBadFormat = errors.New("bad command format") 140 | ErrBuried = errors.New("buried") 141 | ErrDeadline = errors.New("deadline soon") 142 | ErrDraining = errors.New("draining") 143 | ErrInternal = errors.New("internal error") 144 | ErrJobTooBig = errors.New("job too big") 145 | ErrNoCRLF = errors.New("expected CR LF") 146 | ErrNotFound = errors.New("not found") 147 | ErrNotIgnored = errors.New("not ignored") 148 | ErrOOM = errors.New("server is out of memory") 149 | ErrTimeout = errors.New("timeout") 150 | ErrUnknown = errors.New("unknown command") 151 | ) 152 | 153 | var respError = map[string]error{ 154 | "BAD_FORMAT": ErrBadFormat, 155 | "BURIED": ErrBuried, 156 | "DEADLINE_SOON": ErrDeadline, 157 | "DRAINING": ErrDraining, 158 | "EXPECTED_CRLF": ErrNoCRLF, 159 | "INTERNAL_ERROR": ErrInternal, 160 | "JOB_TOO_BIG": ErrJobTooBig, 161 | "NOT_FOUND": ErrNotFound, 162 | "NOT_IGNORED": ErrNotIgnored, 163 | "OUT_OF_MEMORY": ErrOOM, 164 | "TIMED_OUT": ErrTimeout, 165 | "UNKNOWN_COMMAND": ErrUnknown, 166 | } 167 | 168 | type unknownRespError string 169 | 170 | func (e unknownRespError) Error() string { 171 | return "unknown response: " + string(e) 172 | } 173 | 174 | func findRespError(s string) error { 175 | if err := respError[s]; err != nil { 176 | return err 177 | } 178 | return unknownRespError(s) 179 | } 180 | 181 | func parseSize(s string) (string, int, error) { 182 | i := strings.LastIndex(s, " ") 183 | if i == -1 { 184 | return "", 0, findRespError(s) 185 | } 186 | n, err := strconv.Atoi(s[i+1:]) 187 | if err != nil { 188 | return "", 0, err 189 | } 190 | return s[:i], n, nil 191 | } 192 | 193 | func parseDict(dat []byte) map[string]string { 194 | if dat == nil { 195 | return nil 196 | } 197 | d := make(map[string]string) 198 | if bytes.HasPrefix(dat, yamlHead) { 199 | dat = dat[4:] 200 | } 201 | for _, s := range bytes.Split(dat, nl) { 202 | kv := bytes.SplitN(s, colonSpace, 2) 203 | if len(kv) != 2 { 204 | continue 205 | } 206 | d[string(kv[0])] = string(kv[1]) 207 | } 208 | return d 209 | } 210 | 211 | func parseList(dat []byte) []string { 212 | if dat == nil { 213 | return nil 214 | } 215 | l := []string{} 216 | if bytes.HasPrefix(dat, yamlHead) { 217 | dat = dat[4:] 218 | } 219 | for _, s := range bytes.Split(dat, nl) { 220 | if !bytes.HasPrefix(s, minusSpace) { 221 | continue 222 | } 223 | l = append(l, string(s[2:])) 224 | } 225 | return l 226 | } 227 | 228 | type dur time.Duration 229 | 230 | func (d dur) String() string { 231 | return strconv.FormatInt(int64(time.Duration(d)/time.Second), 10) 232 | } 233 | -------------------------------------------------------------------------------- /architecture/protocol.go: -------------------------------------------------------------------------------- 1 | package architecture 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/satori/go.uuid" 9 | // "log" 10 | "strconv" 11 | ) 12 | 13 | // TODO extract protocol error messages into a error helper 14 | 15 | type CommandName string 16 | 17 | const ( 18 | USE CommandName = "use" 19 | PUT = "put" 20 | WATCH = "watch" 21 | IGNORE = "ignore" 22 | RESERVE = "reserve" 23 | RESERVE_WITH_TIMEOUT = "reserve-with-timeout" 24 | DELETE = "delete" 25 | RELEASE = "release" 26 | BURY = "bury" 27 | TOUCH = "touch" 28 | QUIT = "quit" 29 | KICK = "kick" 30 | KICK_JOB = "kick-job" 31 | ) 32 | 33 | const ( 34 | BAD_FORMAT string = "BAD_FORMAT" 35 | UNKNOWN_COMMAND = "UNKNOWN_COMMAND" 36 | NOT_IGNORED = "NOT_IGNORED" 37 | NOT_FOUND = "NOT_FOUND" 38 | EXPECTED_CRLF = "EXPECTED_CRLF" 39 | JOB_TOO_BIG = "JOB_TOO_BIG" 40 | TIMED_OUT = "TIMED_OUT" 41 | ) 42 | 43 | const MAX_JOB_SIZE int64 = 65536 // 2^16 44 | 45 | type Command struct { 46 | Name CommandName 47 | RawCommand string 48 | Params map[string]string 49 | WaitingForMore bool 50 | MoreToSend bool 51 | Err error 52 | Job Job 53 | } 54 | 55 | func NewCommand() Command { 56 | return Command{ 57 | MoreToSend: false, 58 | Params: map[string]string{}, 59 | } 60 | } 61 | 62 | func NewDefaultCommand() Command { 63 | return Command{ 64 | Name: USE, 65 | RawCommand: "use default", 66 | Params: map[string]string{ 67 | "tube": "default", 68 | }, 69 | WaitingForMore: false, 70 | } 71 | } 72 | 73 | func (command *Command) Copy() Command { 74 | paramsCopy := map[string]string{} 75 | for k, v := range command.Params { 76 | paramsCopy[k] = v 77 | } 78 | return Command{ 79 | Name: command.Name, 80 | RawCommand: command.RawCommand, 81 | Params: paramsCopy, 82 | WaitingForMore: command.WaitingForMore, 83 | MoreToSend: command.MoreToSend, 84 | Err: command.Err, 85 | Job: command.Job, 86 | } 87 | } 88 | 89 | func (command *Command) createJobFromParams() error { 90 | pri, e1 := strconv.ParseInt(command.Params["pri"], 10, 0) 91 | if e1 != nil { 92 | return errors.New(BAD_FORMAT) 93 | } 94 | delay, e2 := strconv.ParseInt(command.Params["delay"], 10, 0) 95 | if e2 != nil { 96 | return errors.New(BAD_FORMAT) 97 | } 98 | ttr, e3 := strconv.ParseInt(command.Params["ttr"], 10, 0) 99 | if e3 != nil { 100 | return errors.New(BAD_FORMAT) 101 | } 102 | bytes, e4 := strconv.ParseInt(command.Params["bytes"], 10, 0) 103 | if e4 != nil { 104 | return errors.New(BAD_FORMAT) 105 | } 106 | 107 | if bytes > MAX_JOB_SIZE { 108 | return errors.New(JOB_TOO_BIG) 109 | } else if bytes != int64(len(command.Params["data"])) { 110 | return errors.New(EXPECTED_CRLF) 111 | } 112 | 113 | command.Job = *NewJob( 114 | uuid.NewV1().String(), 115 | pri, 116 | delay, 117 | ttr, 118 | bytes, 119 | command.Params["data"], 120 | ) 121 | // log.Println("PROTOCOL new job: ", command.Job) 122 | return nil 123 | } 124 | 125 | // Parse keeps track of the state of the command and it will be called multiple times for commands such as 'put' 126 | // where the length of command spans multiple lines 127 | func (command *Command) Parse(rawCommand string) (bool, error) { 128 | // check if this command has already been waiting for a second round 129 | if !command.WaitingForMore { 130 | // first round 131 | parts := strings.Split(rawCommand, " ") 132 | name := CommandName(strings.ToLower(parts[0])) 133 | 134 | // Check for valid command. 135 | opts, ok := cmdParseOptions[name] 136 | if !ok { 137 | // Unknown command. 138 | command.Err = errors.New(UNKNOWN_COMMAND) 139 | return true, command.Err 140 | } 141 | 142 | command.Name = name 143 | 144 | // Were we given the proper number of parameters? 145 | if len(parts) != opts.ExpectedLength { 146 | command.Err = errors.New(BAD_FORMAT) 147 | return true, command.Err 148 | } 149 | 150 | // Store command info. For future logging, maybe? 151 | command.Params = map[string]string{} 152 | command.RawCommand = rawCommand 153 | for i, paramName := range opts.Params { 154 | command.Params[paramName] = parts[i+1] 155 | } 156 | command.WaitingForMore = opts.WaitingForMore 157 | log.Debug("PROTOCOL command after parsing ", command) 158 | 159 | return !command.WaitingForMore, nil 160 | } 161 | 162 | // second round; PUT is the only valid command when WaitingForMore. 163 | // log.Println("GOT MORE", command) 164 | if command.Name == PUT { 165 | command.Params["data"] = rawCommand 166 | command.RawCommand += ("\r\n" + rawCommand) 167 | err := command.createJobFromParams() 168 | // log.Println("GOT MORE PUT", c, err) 169 | command.Err = err 170 | return true, err 171 | } 172 | 173 | // Unknown command, secondround. 174 | return true, nil 175 | } 176 | 177 | func (command *Command) Reply() (bool, string) { 178 | if err := command.Err; err != nil { 179 | return false, err.Error() 180 | } 181 | 182 | // cmdReplyOptions = cmdParseOptions - RESERVE, RESERVE_WITH_TIMEOUT 183 | if opts, ok := cmdReplyOptions[command.Name]; ok { 184 | // Take care of everything except PUT, RESERVE, RESERVE_WITH_TIMEOUT, and TOUCH 185 | if !opts.UseJobID { 186 | // DELETE, RELEASE, BURY 187 | if len(opts.Param) == 0 { 188 | return false, opts.Message 189 | } 190 | 191 | // USE, WATCH, IGNORE 192 | return false, strings.Join([]string{opts.Message, command.Params[opts.Param]}, " ") 193 | } 194 | 195 | // PUT, TOUCH 196 | return false, strings.Join([]string{opts.Message, command.Job.Id()}, " ") 197 | } 198 | 199 | // RESERVE, RESERVE_WITH_TIMEOUT 200 | if !command.MoreToSend { 201 | command.MoreToSend = true 202 | return true, fmt.Sprintf("RESERVED %s %d", command.Job.Id(), command.Job.Bytes) 203 | } 204 | 205 | return false, command.Job.Data 206 | } 207 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v1/encode.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "reflect" 5 | "sort" 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | type encoder struct { 11 | emitter yaml_emitter_t 12 | event yaml_event_t 13 | out []byte 14 | flow bool 15 | } 16 | 17 | func newEncoder() (e *encoder) { 18 | e = &encoder{} 19 | e.must(yaml_emitter_initialize(&e.emitter)) 20 | yaml_emitter_set_output_string(&e.emitter, &e.out) 21 | e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)) 22 | e.emit() 23 | e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true)) 24 | e.emit() 25 | return e 26 | } 27 | 28 | func (e *encoder) finish() { 29 | e.must(yaml_document_end_event_initialize(&e.event, true)) 30 | e.emit() 31 | e.emitter.open_ended = false 32 | e.must(yaml_stream_end_event_initialize(&e.event)) 33 | e.emit() 34 | } 35 | 36 | func (e *encoder) destroy() { 37 | yaml_emitter_delete(&e.emitter) 38 | } 39 | 40 | func (e *encoder) emit() { 41 | // This will internally delete the e.event value. 42 | if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT { 43 | e.must(false) 44 | } 45 | } 46 | 47 | func (e *encoder) must(ok bool) { 48 | if !ok { 49 | msg := e.emitter.problem 50 | if msg == "" { 51 | msg = "Unknown problem generating YAML content" 52 | } 53 | panic(msg) 54 | } 55 | } 56 | 57 | func (e *encoder) marshal(tag string, in reflect.Value) { 58 | var value interface{} 59 | if getter, ok := in.Interface().(Getter); ok { 60 | tag, value = getter.GetYAML() 61 | if value == nil { 62 | e.nilv() 63 | return 64 | } 65 | in = reflect.ValueOf(value) 66 | } 67 | switch in.Kind() { 68 | case reflect.Interface: 69 | if in.IsNil() { 70 | e.nilv() 71 | } else { 72 | e.marshal(tag, in.Elem()) 73 | } 74 | case reflect.Map: 75 | e.mapv(tag, in) 76 | case reflect.Ptr: 77 | if in.IsNil() { 78 | e.nilv() 79 | } else { 80 | e.marshal(tag, in.Elem()) 81 | } 82 | case reflect.Struct: 83 | e.structv(tag, in) 84 | case reflect.Slice: 85 | e.slicev(tag, in) 86 | case reflect.String: 87 | e.stringv(tag, in) 88 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 89 | if in.Type() == durationType { 90 | e.stringv(tag, reflect.ValueOf(in.Interface().(time.Duration).String())) 91 | } else { 92 | e.intv(tag, in) 93 | } 94 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 95 | e.uintv(tag, in) 96 | case reflect.Float32, reflect.Float64: 97 | e.floatv(tag, in) 98 | case reflect.Bool: 99 | e.boolv(tag, in) 100 | default: 101 | panic("Can't marshal type yet: " + in.Type().String()) 102 | } 103 | } 104 | 105 | func (e *encoder) mapv(tag string, in reflect.Value) { 106 | e.mappingv(tag, func() { 107 | keys := keyList(in.MapKeys()) 108 | sort.Sort(keys) 109 | for _, k := range keys { 110 | e.marshal("", k) 111 | e.marshal("", in.MapIndex(k)) 112 | } 113 | }) 114 | } 115 | 116 | func (e *encoder) structv(tag string, in reflect.Value) { 117 | sinfo, err := getStructInfo(in.Type()) 118 | if err != nil { 119 | panic(err) 120 | } 121 | e.mappingv(tag, func() { 122 | for _, info := range sinfo.FieldsList { 123 | var value reflect.Value 124 | if info.Inline == nil { 125 | value = in.Field(info.Num) 126 | } else { 127 | value = in.FieldByIndex(info.Inline) 128 | } 129 | if info.OmitEmpty && isZero(value) { 130 | continue 131 | } 132 | e.marshal("", reflect.ValueOf(info.Key)) 133 | e.flow = info.Flow 134 | e.marshal("", value) 135 | } 136 | }) 137 | } 138 | 139 | func (e *encoder) mappingv(tag string, f func()) { 140 | implicit := tag == "" 141 | style := yaml_BLOCK_MAPPING_STYLE 142 | if e.flow { 143 | e.flow = false 144 | style = yaml_FLOW_MAPPING_STYLE 145 | } 146 | e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 147 | e.emit() 148 | f() 149 | e.must(yaml_mapping_end_event_initialize(&e.event)) 150 | e.emit() 151 | } 152 | 153 | func (e *encoder) slicev(tag string, in reflect.Value) { 154 | implicit := tag == "" 155 | style := yaml_BLOCK_SEQUENCE_STYLE 156 | if e.flow { 157 | e.flow = false 158 | style = yaml_FLOW_SEQUENCE_STYLE 159 | } 160 | e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) 161 | e.emit() 162 | n := in.Len() 163 | for i := 0; i < n; i++ { 164 | e.marshal("", in.Index(i)) 165 | } 166 | e.must(yaml_sequence_end_event_initialize(&e.event)) 167 | e.emit() 168 | } 169 | 170 | func (e *encoder) stringv(tag string, in reflect.Value) { 171 | var style yaml_scalar_style_t 172 | s := in.String() 173 | if rtag, _ := resolve("", s); rtag != "!!str" { 174 | style = yaml_DOUBLE_QUOTED_SCALAR_STYLE 175 | } else { 176 | style = yaml_PLAIN_SCALAR_STYLE 177 | } 178 | e.emitScalar(s, "", tag, style) 179 | } 180 | 181 | func (e *encoder) boolv(tag string, in reflect.Value) { 182 | var s string 183 | if in.Bool() { 184 | s = "true" 185 | } else { 186 | s = "false" 187 | } 188 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 189 | } 190 | 191 | func (e *encoder) intv(tag string, in reflect.Value) { 192 | s := strconv.FormatInt(in.Int(), 10) 193 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 194 | } 195 | 196 | func (e *encoder) uintv(tag string, in reflect.Value) { 197 | s := strconv.FormatUint(in.Uint(), 10) 198 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 199 | } 200 | 201 | func (e *encoder) floatv(tag string, in reflect.Value) { 202 | // FIXME: Handle 64 bits here. 203 | s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32) 204 | switch s { 205 | case "+Inf": 206 | s = ".inf" 207 | case "-Inf": 208 | s = "-.inf" 209 | case "NaN": 210 | s = ".nan" 211 | } 212 | e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) 213 | } 214 | 215 | func (e *encoder) nilv() { 216 | e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) 217 | } 218 | 219 | func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { 220 | implicit := tag == "" 221 | if !implicit { 222 | style = yaml_PLAIN_SCALAR_STYLE 223 | } 224 | e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) 225 | e.emit() 226 | } 227 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/bypass.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 Dave Collins 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | // NOTE: Due to the following build constraints, this file will only be compiled 16 | // when the code is not running on Google App Engine, compiled by GopherJS, and 17 | // "-tags safe" is not added to the go build command line. The "disableunsafe" 18 | // tag is deprecated and thus should not be used. 19 | // +build !js,!appengine,!safe,!disableunsafe 20 | 21 | package spew 22 | 23 | import ( 24 | "reflect" 25 | "unsafe" 26 | ) 27 | 28 | const ( 29 | // UnsafeDisabled is a build-time constant which specifies whether or 30 | // not access to the unsafe package is available. 31 | UnsafeDisabled = false 32 | 33 | // ptrSize is the size of a pointer on the current arch. 34 | ptrSize = unsafe.Sizeof((*byte)(nil)) 35 | ) 36 | 37 | var ( 38 | // offsetPtr, offsetScalar, and offsetFlag are the offsets for the 39 | // internal reflect.Value fields. These values are valid before golang 40 | // commit ecccf07e7f9d which changed the format. The are also valid 41 | // after commit 82f48826c6c7 which changed the format again to mirror 42 | // the original format. Code in the init function updates these offsets 43 | // as necessary. 44 | offsetPtr = uintptr(ptrSize) 45 | offsetScalar = uintptr(0) 46 | offsetFlag = uintptr(ptrSize * 2) 47 | 48 | // flagKindWidth and flagKindShift indicate various bits that the 49 | // reflect package uses internally to track kind information. 50 | // 51 | // flagRO indicates whether or not the value field of a reflect.Value is 52 | // read-only. 53 | // 54 | // flagIndir indicates whether the value field of a reflect.Value is 55 | // the actual data or a pointer to the data. 56 | // 57 | // These values are valid before golang commit 90a7c3c86944 which 58 | // changed their positions. Code in the init function updates these 59 | // flags as necessary. 60 | flagKindWidth = uintptr(5) 61 | flagKindShift = uintptr(flagKindWidth - 1) 62 | flagRO = uintptr(1 << 0) 63 | flagIndir = uintptr(1 << 1) 64 | ) 65 | 66 | func init() { 67 | // Older versions of reflect.Value stored small integers directly in the 68 | // ptr field (which is named val in the older versions). Versions 69 | // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named 70 | // scalar for this purpose which unfortunately came before the flag 71 | // field, so the offset of the flag field is different for those 72 | // versions. 73 | // 74 | // This code constructs a new reflect.Value from a known small integer 75 | // and checks if the size of the reflect.Value struct indicates it has 76 | // the scalar field. When it does, the offsets are updated accordingly. 77 | vv := reflect.ValueOf(0xf00) 78 | if unsafe.Sizeof(vv) == (ptrSize * 4) { 79 | offsetScalar = ptrSize * 2 80 | offsetFlag = ptrSize * 3 81 | } 82 | 83 | // Commit 90a7c3c86944 changed the flag positions such that the low 84 | // order bits are the kind. This code extracts the kind from the flags 85 | // field and ensures it's the correct type. When it's not, the flag 86 | // order has been changed to the newer format, so the flags are updated 87 | // accordingly. 88 | upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag) 89 | upfv := *(*uintptr)(upf) 90 | flagKindMask := uintptr((1<>flagKindShift != uintptr(reflect.Int) { 92 | flagKindShift = 0 93 | flagRO = 1 << 5 94 | flagIndir = 1 << 6 95 | 96 | // Commit adf9b30e5594 modified the flags to separate the 97 | // flagRO flag into two bits which specifies whether or not the 98 | // field is embedded. This causes flagIndir to move over a bit 99 | // and means that flagRO is the combination of either of the 100 | // original flagRO bit and the new bit. 101 | // 102 | // This code detects the change by extracting what used to be 103 | // the indirect bit to ensure it's set. When it's not, the flag 104 | // order has been changed to the newer format, so the flags are 105 | // updated accordingly. 106 | if upfv&flagIndir == 0 { 107 | flagRO = 3 << 5 108 | flagIndir = 1 << 7 109 | } 110 | } 111 | } 112 | 113 | // unsafeReflectValue converts the passed reflect.Value into a one that bypasses 114 | // the typical safety restrictions preventing access to unaddressable and 115 | // unexported data. It works by digging the raw pointer to the underlying 116 | // value out of the protected value and generating a new unprotected (unsafe) 117 | // reflect.Value to it. 118 | // 119 | // This allows us to check for implementations of the Stringer and error 120 | // interfaces to be used for pretty printing ordinarily unaddressable and 121 | // inaccessible values such as unexported struct fields. 122 | func unsafeReflectValue(v reflect.Value) (rv reflect.Value) { 123 | indirects := 1 124 | vt := v.Type() 125 | upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr) 126 | rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag)) 127 | if rvf&flagIndir != 0 { 128 | vt = reflect.PtrTo(v.Type()) 129 | indirects++ 130 | } else if offsetScalar != 0 { 131 | // The value is in the scalar field when it's not one of the 132 | // reference types. 133 | switch vt.Kind() { 134 | case reflect.Uintptr: 135 | case reflect.Chan: 136 | case reflect.Func: 137 | case reflect.Map: 138 | case reflect.Ptr: 139 | case reflect.UnsafePointer: 140 | default: 141 | upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + 142 | offsetScalar) 143 | } 144 | } 145 | 146 | pv := reflect.NewAt(vt, upv) 147 | rv = pv 148 | for i := 0; i < indirects; i++ { 149 | rv = rv.Elem() 150 | } 151 | return rv 152 | } 153 | -------------------------------------------------------------------------------- /vendor/github.com/stretchr/objx/map.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "errors" 7 | "io/ioutil" 8 | "net/url" 9 | "strings" 10 | ) 11 | 12 | // MSIConvertable is an interface that defines methods for converting your 13 | // custom types to a map[string]interface{} representation. 14 | type MSIConvertable interface { 15 | // MSI gets a map[string]interface{} (msi) representing the 16 | // object. 17 | MSI() map[string]interface{} 18 | } 19 | 20 | // Map provides extended functionality for working with 21 | // untyped data, in particular map[string]interface (msi). 22 | type Map map[string]interface{} 23 | 24 | // Value returns the internal value instance 25 | func (m Map) Value() *Value { 26 | return &Value{data: m} 27 | } 28 | 29 | // Nil represents a nil Map. 30 | var Nil Map = New(nil) 31 | 32 | // New creates a new Map containing the map[string]interface{} in the data argument. 33 | // If the data argument is not a map[string]interface, New attempts to call the 34 | // MSI() method on the MSIConvertable interface to create one. 35 | func New(data interface{}) Map { 36 | if _, ok := data.(map[string]interface{}); !ok { 37 | if converter, ok := data.(MSIConvertable); ok { 38 | data = converter.MSI() 39 | } else { 40 | return nil 41 | } 42 | } 43 | return Map(data.(map[string]interface{})) 44 | } 45 | 46 | // MSI creates a map[string]interface{} and puts it inside a new Map. 47 | // 48 | // The arguments follow a key, value pattern. 49 | // 50 | // Panics 51 | // 52 | // Panics if any key arugment is non-string or if there are an odd number of arguments. 53 | // 54 | // Example 55 | // 56 | // To easily create Maps: 57 | // 58 | // m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) 59 | // 60 | // // creates an Map equivalent to 61 | // m := objx.New(map[string]interface{}{"name": "Mat", "age": 29, "subobj": map[string]interface{}{"active": true}}) 62 | func MSI(keyAndValuePairs ...interface{}) Map { 63 | 64 | newMap := make(map[string]interface{}) 65 | keyAndValuePairsLen := len(keyAndValuePairs) 66 | 67 | if keyAndValuePairsLen%2 != 0 { 68 | panic("objx: MSI must have an even number of arguments following the 'key, value' pattern.") 69 | } 70 | 71 | for i := 0; i < keyAndValuePairsLen; i = i + 2 { 72 | 73 | key := keyAndValuePairs[i] 74 | value := keyAndValuePairs[i+1] 75 | 76 | // make sure the key is a string 77 | keyString, keyStringOK := key.(string) 78 | if !keyStringOK { 79 | panic("objx: MSI must follow 'string, interface{}' pattern. " + keyString + " is not a valid key.") 80 | } 81 | 82 | newMap[keyString] = value 83 | 84 | } 85 | 86 | return New(newMap) 87 | } 88 | 89 | // ****** Conversion Constructors 90 | 91 | // MustFromJSON creates a new Map containing the data specified in the 92 | // jsonString. 93 | // 94 | // Panics if the JSON is invalid. 95 | func MustFromJSON(jsonString string) Map { 96 | o, err := FromJSON(jsonString) 97 | 98 | if err != nil { 99 | panic("objx: MustFromJSON failed with error: " + err.Error()) 100 | } 101 | 102 | return o 103 | } 104 | 105 | // FromJSON creates a new Map containing the data specified in the 106 | // jsonString. 107 | // 108 | // Returns an error if the JSON is invalid. 109 | func FromJSON(jsonString string) (Map, error) { 110 | 111 | var data interface{} 112 | err := json.Unmarshal([]byte(jsonString), &data) 113 | 114 | if err != nil { 115 | return Nil, err 116 | } 117 | 118 | return New(data), nil 119 | 120 | } 121 | 122 | // FromBase64 creates a new Obj containing the data specified 123 | // in the Base64 string. 124 | // 125 | // The string is an encoded JSON string returned by Base64 126 | func FromBase64(base64String string) (Map, error) { 127 | 128 | decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String)) 129 | 130 | decoded, err := ioutil.ReadAll(decoder) 131 | if err != nil { 132 | return nil, err 133 | } 134 | 135 | return FromJSON(string(decoded)) 136 | } 137 | 138 | // MustFromBase64 creates a new Obj containing the data specified 139 | // in the Base64 string and panics if there is an error. 140 | // 141 | // The string is an encoded JSON string returned by Base64 142 | func MustFromBase64(base64String string) Map { 143 | 144 | result, err := FromBase64(base64String) 145 | 146 | if err != nil { 147 | panic("objx: MustFromBase64 failed with error: " + err.Error()) 148 | } 149 | 150 | return result 151 | } 152 | 153 | // FromSignedBase64 creates a new Obj containing the data specified 154 | // in the Base64 string. 155 | // 156 | // The string is an encoded JSON string returned by SignedBase64 157 | func FromSignedBase64(base64String, key string) (Map, error) { 158 | parts := strings.Split(base64String, SignatureSeparator) 159 | if len(parts) != 2 { 160 | return nil, errors.New("objx: Signed base64 string is malformed.") 161 | } 162 | 163 | sig := HashWithKey(parts[0], key) 164 | if parts[1] != sig { 165 | return nil, errors.New("objx: Signature for base64 data does not match.") 166 | } 167 | 168 | return FromBase64(parts[0]) 169 | } 170 | 171 | // MustFromSignedBase64 creates a new Obj containing the data specified 172 | // in the Base64 string and panics if there is an error. 173 | // 174 | // The string is an encoded JSON string returned by Base64 175 | func MustFromSignedBase64(base64String, key string) Map { 176 | 177 | result, err := FromSignedBase64(base64String, key) 178 | 179 | if err != nil { 180 | panic("objx: MustFromSignedBase64 failed with error: " + err.Error()) 181 | } 182 | 183 | return result 184 | } 185 | 186 | // FromURLQuery generates a new Obj by parsing the specified 187 | // query. 188 | // 189 | // For queries with multiple values, the first value is selected. 190 | func FromURLQuery(query string) (Map, error) { 191 | 192 | vals, err := url.ParseQuery(query) 193 | 194 | if err != nil { 195 | return nil, err 196 | } 197 | 198 | m := make(map[string]interface{}) 199 | for k, vals := range vals { 200 | m[k] = vals[0] 201 | } 202 | 203 | return New(m), nil 204 | } 205 | 206 | // MustFromURLQuery generates a new Obj by parsing the specified 207 | // query. 208 | // 209 | // For queries with multiple values, the first value is selected. 210 | // 211 | // Panics if it encounters an error 212 | func MustFromURLQuery(query string) Map { 213 | 214 | o, err := FromURLQuery(query) 215 | 216 | if err != nil { 217 | panic("objx: MustFromURLQuery failed with error: " + err.Error()) 218 | } 219 | 220 | return o 221 | 222 | } 223 | -------------------------------------------------------------------------------- /vendor/github.com/davecgh/go-spew/spew/spew.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2016 Dave Collins 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | package spew 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | ) 23 | 24 | // Errorf is a wrapper for fmt.Errorf that treats each argument as if it were 25 | // passed with a default Formatter interface returned by NewFormatter. It 26 | // returns the formatted string as a value that satisfies error. See 27 | // NewFormatter for formatting details. 28 | // 29 | // This function is shorthand for the following syntax: 30 | // 31 | // fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 32 | func Errorf(format string, a ...interface{}) (err error) { 33 | return fmt.Errorf(format, convertArgs(a)...) 34 | } 35 | 36 | // Fprint is a wrapper for fmt.Fprint that treats each argument as if it were 37 | // passed with a default Formatter interface returned by NewFormatter. It 38 | // returns the number of bytes written and any write error encountered. See 39 | // NewFormatter for formatting details. 40 | // 41 | // This function is shorthand for the following syntax: 42 | // 43 | // fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) 44 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) { 45 | return fmt.Fprint(w, convertArgs(a)...) 46 | } 47 | 48 | // Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were 49 | // passed with a default Formatter interface returned by NewFormatter. It 50 | // returns the number of bytes written and any write error encountered. See 51 | // NewFormatter for formatting details. 52 | // 53 | // This function is shorthand for the following syntax: 54 | // 55 | // fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) 56 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { 57 | return fmt.Fprintf(w, format, convertArgs(a)...) 58 | } 59 | 60 | // Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it 61 | // passed with a default Formatter interface returned by NewFormatter. See 62 | // NewFormatter for formatting details. 63 | // 64 | // This function is shorthand for the following syntax: 65 | // 66 | // fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) 67 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { 68 | return fmt.Fprintln(w, convertArgs(a)...) 69 | } 70 | 71 | // Print is a wrapper for fmt.Print that treats each argument as if it were 72 | // passed with a default Formatter interface returned by NewFormatter. It 73 | // returns the number of bytes written and any write error encountered. See 74 | // NewFormatter for formatting details. 75 | // 76 | // This function is shorthand for the following syntax: 77 | // 78 | // fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) 79 | func Print(a ...interface{}) (n int, err error) { 80 | return fmt.Print(convertArgs(a)...) 81 | } 82 | 83 | // Printf is a wrapper for fmt.Printf that treats each argument as if it were 84 | // passed with a default Formatter interface returned by NewFormatter. It 85 | // returns the number of bytes written and any write error encountered. See 86 | // NewFormatter for formatting details. 87 | // 88 | // This function is shorthand for the following syntax: 89 | // 90 | // fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 91 | func Printf(format string, a ...interface{}) (n int, err error) { 92 | return fmt.Printf(format, convertArgs(a)...) 93 | } 94 | 95 | // Println is a wrapper for fmt.Println that treats each argument as if it were 96 | // passed with a default Formatter interface returned by NewFormatter. It 97 | // returns the number of bytes written and any write error encountered. See 98 | // NewFormatter for formatting details. 99 | // 100 | // This function is shorthand for the following syntax: 101 | // 102 | // fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) 103 | func Println(a ...interface{}) (n int, err error) { 104 | return fmt.Println(convertArgs(a)...) 105 | } 106 | 107 | // Sprint is a wrapper for fmt.Sprint that treats each argument as if it were 108 | // passed with a default Formatter interface returned by NewFormatter. It 109 | // returns the resulting string. See NewFormatter for formatting details. 110 | // 111 | // This function is shorthand for the following syntax: 112 | // 113 | // fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) 114 | func Sprint(a ...interface{}) string { 115 | return fmt.Sprint(convertArgs(a)...) 116 | } 117 | 118 | // Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were 119 | // passed with a default Formatter interface returned by NewFormatter. It 120 | // returns the resulting string. See NewFormatter for formatting details. 121 | // 122 | // This function is shorthand for the following syntax: 123 | // 124 | // fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) 125 | func Sprintf(format string, a ...interface{}) string { 126 | return fmt.Sprintf(format, convertArgs(a)...) 127 | } 128 | 129 | // Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it 130 | // were passed with a default Formatter interface returned by NewFormatter. It 131 | // returns the resulting string. See NewFormatter for formatting details. 132 | // 133 | // This function is shorthand for the following syntax: 134 | // 135 | // fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) 136 | func Sprintln(a ...interface{}) string { 137 | return fmt.Sprintln(convertArgs(a)...) 138 | } 139 | 140 | // convertArgs accepts a slice of arguments and returns a slice of the same 141 | // length with each argument converted to a default spew Formatter interface. 142 | func convertArgs(args []interface{}) (formatters []interface{}) { 143 | formatters = make([]interface{}, len(args)) 144 | for index, arg := range args { 145 | formatters[index] = NewFormatter(arg) 146 | } 147 | return formatters 148 | } 149 | -------------------------------------------------------------------------------- /vendor/github.com/op/go-logging/memory.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013, Örjan Persson. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !appengine 6 | 7 | package logging 8 | 9 | import ( 10 | "sync" 11 | "sync/atomic" 12 | "time" 13 | "unsafe" 14 | ) 15 | 16 | // TODO pick one of the memory backends and stick with it or share interface. 17 | 18 | // InitForTesting is a convenient method when using logging in a test. Once 19 | // called, the time will be frozen to January 1, 1970 UTC. 20 | func InitForTesting(level Level) *MemoryBackend { 21 | Reset() 22 | 23 | memoryBackend := NewMemoryBackend(10240) 24 | 25 | leveledBackend := AddModuleLevel(memoryBackend) 26 | leveledBackend.SetLevel(level, "") 27 | SetBackend(leveledBackend) 28 | 29 | timeNow = func() time.Time { 30 | return time.Unix(0, 0).UTC() 31 | } 32 | return memoryBackend 33 | } 34 | 35 | // Node is a record node pointing to an optional next node. 36 | type node struct { 37 | next *node 38 | Record *Record 39 | } 40 | 41 | // Next returns the next record node. If there's no node available, it will 42 | // return nil. 43 | func (n *node) Next() *node { 44 | return n.next 45 | } 46 | 47 | // MemoryBackend is a simple memory based logging backend that will not produce 48 | // any output but merly keep records, up to the given size, in memory. 49 | type MemoryBackend struct { 50 | size int32 51 | maxSize int32 52 | head, tail unsafe.Pointer 53 | } 54 | 55 | // NewMemoryBackend creates a simple in-memory logging backend. 56 | func NewMemoryBackend(size int) *MemoryBackend { 57 | return &MemoryBackend{maxSize: int32(size)} 58 | } 59 | 60 | // Log implements the Log method required by Backend. 61 | func (b *MemoryBackend) Log(level Level, calldepth int, rec *Record) error { 62 | var size int32 63 | 64 | n := &node{Record: rec} 65 | np := unsafe.Pointer(n) 66 | 67 | // Add the record to the tail. If there's no records available, tail and 68 | // head will both be nil. When we successfully set the tail and the previous 69 | // value was nil, it's safe to set the head to the current value too. 70 | for { 71 | tailp := b.tail 72 | swapped := atomic.CompareAndSwapPointer( 73 | &b.tail, 74 | tailp, 75 | np, 76 | ) 77 | if swapped == true { 78 | if tailp == nil { 79 | b.head = np 80 | } else { 81 | (*node)(tailp).next = n 82 | } 83 | size = atomic.AddInt32(&b.size, 1) 84 | break 85 | } 86 | } 87 | 88 | // Since one record was added, we might have overflowed the list. Remove 89 | // a record if that is the case. The size will fluctate a bit, but 90 | // eventual consistent. 91 | if b.maxSize > 0 && size > b.maxSize { 92 | for { 93 | headp := b.head 94 | head := (*node)(b.head) 95 | if head.next == nil { 96 | break 97 | } 98 | swapped := atomic.CompareAndSwapPointer( 99 | &b.head, 100 | headp, 101 | unsafe.Pointer(head.next), 102 | ) 103 | if swapped == true { 104 | atomic.AddInt32(&b.size, -1) 105 | break 106 | } 107 | } 108 | } 109 | return nil 110 | } 111 | 112 | // Head returns the oldest record node kept in memory. It can be used to 113 | // iterate over records, one by one, up to the last record. 114 | // 115 | // Note: new records can get added while iterating. Hence the number of records 116 | // iterated over might be larger than the maximum size. 117 | func (b *MemoryBackend) Head() *node { 118 | return (*node)(b.head) 119 | } 120 | 121 | type event int 122 | 123 | const ( 124 | eventFlush event = iota 125 | eventStop 126 | ) 127 | 128 | // ChannelMemoryBackend is very similar to the MemoryBackend, except that it 129 | // internally utilizes a channel. 130 | type ChannelMemoryBackend struct { 131 | maxSize int 132 | size int 133 | incoming chan *Record 134 | events chan event 135 | mu sync.Mutex 136 | running bool 137 | flushWg sync.WaitGroup 138 | stopWg sync.WaitGroup 139 | head, tail *node 140 | } 141 | 142 | // NewChannelMemoryBackend creates a simple in-memory logging backend which 143 | // utilizes a go channel for communication. 144 | // 145 | // Start will automatically be called by this function. 146 | func NewChannelMemoryBackend(size int) *ChannelMemoryBackend { 147 | backend := &ChannelMemoryBackend{ 148 | maxSize: size, 149 | incoming: make(chan *Record, 1024), 150 | events: make(chan event), 151 | } 152 | backend.Start() 153 | return backend 154 | } 155 | 156 | // Start launches the internal goroutine which starts processing data from the 157 | // input channel. 158 | func (b *ChannelMemoryBackend) Start() { 159 | b.mu.Lock() 160 | defer b.mu.Unlock() 161 | 162 | // Launch the goroutine unless it's already running. 163 | if b.running != true { 164 | b.running = true 165 | b.stopWg.Add(1) 166 | go b.process() 167 | } 168 | } 169 | 170 | func (b *ChannelMemoryBackend) process() { 171 | defer b.stopWg.Done() 172 | for { 173 | select { 174 | case rec := <-b.incoming: 175 | b.insertRecord(rec) 176 | case e := <-b.events: 177 | switch e { 178 | case eventStop: 179 | return 180 | case eventFlush: 181 | for len(b.incoming) > 0 { 182 | b.insertRecord(<-b.incoming) 183 | } 184 | b.flushWg.Done() 185 | } 186 | } 187 | } 188 | } 189 | 190 | func (b *ChannelMemoryBackend) insertRecord(rec *Record) { 191 | prev := b.tail 192 | b.tail = &node{Record: rec} 193 | if prev == nil { 194 | b.head = b.tail 195 | } else { 196 | prev.next = b.tail 197 | } 198 | 199 | if b.maxSize > 0 && b.size >= b.maxSize { 200 | b.head = b.head.next 201 | } else { 202 | b.size++ 203 | } 204 | } 205 | 206 | // Flush waits until all records in the buffered channel have been processed. 207 | func (b *ChannelMemoryBackend) Flush() { 208 | b.flushWg.Add(1) 209 | b.events <- eventFlush 210 | b.flushWg.Wait() 211 | } 212 | 213 | // Stop signals the internal goroutine to exit and waits until it have. 214 | func (b *ChannelMemoryBackend) Stop() { 215 | b.mu.Lock() 216 | if b.running == true { 217 | b.running = false 218 | b.events <- eventStop 219 | } 220 | b.mu.Unlock() 221 | b.stopWg.Wait() 222 | } 223 | 224 | // Log implements the Log method required by Backend. 225 | func (b *ChannelMemoryBackend) Log(level Level, calldepth int, rec *Record) error { 226 | b.incoming <- rec 227 | return nil 228 | } 229 | 230 | // Head returns the oldest record node kept in memory. It can be used to 231 | // iterate over records, one by one, up to the last record. 232 | // 233 | // Note: new records can get added while iterating. Hence the number of records 234 | // iterated over might be larger than the maximum size. 235 | func (b *ChannelMemoryBackend) Head() *node { 236 | return b.head 237 | } 238 | -------------------------------------------------------------------------------- /operation/client_handler.go: -------------------------------------------------------------------------------- 1 | package operation 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "net" 7 | "reflect" 8 | "strconv" 9 | 10 | "github.com/op/go-logging" 11 | "github.com/beanstalkg/beanstalkg/architecture" 12 | ) 13 | 14 | var log = logging.MustGetLogger("BEANSTALKG") 15 | 16 | type clientHandler struct { 17 | conn net.Conn 18 | registerConnection chan architecture.Command 19 | tubeConnectionReceiver chan chan architecture.Command 20 | usedTubeConnection chan architecture.Command 21 | watchedTubeConnectionsReceiver chan chan architecture.Command 22 | watchedTubeConnections map[string]chan architecture.Command 23 | reservedJobs map[string]string 24 | stop chan bool 25 | } 26 | 27 | func NewClientHandler( 28 | conn net.Conn, 29 | registerConnection chan architecture.Command, 30 | useTubeConnectionReceiver chan chan architecture.Command, 31 | watchedTubeConnectionsReceiver chan chan architecture.Command, 32 | stop chan bool, 33 | ) { 34 | go func() { 35 | defer conn.Close() 36 | client := clientHandler{ 37 | conn, 38 | registerConnection, 39 | useTubeConnectionReceiver, 40 | nil, 41 | watchedTubeConnectionsReceiver, 42 | nil, 43 | map[string]string{}, 44 | stop, 45 | } 46 | client.startSession() 47 | log.Info("CLIENT_HANDLER exit") 48 | return 49 | }() 50 | } 51 | 52 | func (client *clientHandler) handleReply(c architecture.Command) error { 53 | for { 54 | more, reply := c.Reply() 55 | _, err := client.conn.Write([]byte(reply + "\r\n")) 56 | if err != nil { 57 | log.Error(err) 58 | return err 59 | } 60 | if !more { 61 | break 62 | } 63 | } 64 | return nil 65 | } 66 | 67 | func (client *clientHandler) startSession() { 68 | // this command object will be replaced each time the client sends a new one 69 | c := architecture.NewDefaultCommand() 70 | // selects default tube first up 71 | client.registerConnection <- c.Copy() 72 | client.usedTubeConnection = <-client.tubeConnectionReceiver 73 | client.watchedTubeConnections = map[string]chan architecture.Command{ 74 | "default": client.usedTubeConnection, 75 | } 76 | // convert scan to a selectable 77 | scan := make(chan string) 78 | exit := make(chan bool) 79 | go func() { 80 | scanner := bufio.NewScanner(client.conn) 81 | for scanner.Scan() { 82 | scan <- scanner.Text() 83 | } 84 | exit <- true 85 | }() 86 | 87 | for { 88 | select { 89 | case rawCommand := <-scan: 90 | parsed, err := c.Parse(rawCommand) 91 | if err != nil { // check if parse error 92 | err = client.handleReply(c) 93 | c = architecture.NewCommand() 94 | if err != nil { 95 | return 96 | } 97 | } else if parsed { // check if the command has been parsed completely 98 | c = client.handleBasicCommand(c) 99 | err = client.handleReply(c) 100 | if err != nil { 101 | return 102 | } 103 | // we replace previous command once its parsing is finished 104 | c = architecture.NewCommand() 105 | } 106 | case <-client.stop: 107 | return 108 | case <-exit: 109 | return 110 | } 111 | } 112 | } 113 | 114 | func (client *clientHandler) handleBasicCommand(command architecture.Command) architecture.Command { 115 | switch command.Name { 116 | case architecture.USE: 117 | // send command to tube register 118 | client.registerConnection <- command.Copy() 119 | client.usedTubeConnection = <-client.tubeConnectionReceiver 120 | log.Info("CLIENT_HANDLER started using tube: ", command.Params["tube"]) 121 | case architecture.PUT: 122 | client.usedTubeConnection <- command.Copy() // send the command to tube 123 | command = <-client.usedTubeConnection // get the response 124 | case architecture.WATCH: 125 | client.registerConnection <- command.Copy() 126 | client.watchedTubeConnections[command.Params["tube"]] = <-client.tubeConnectionReceiver 127 | command.Params["count"] = strconv.FormatInt(int64(len(client.watchedTubeConnections)), 10) 128 | case architecture.IGNORE: 129 | if _, ok := client.watchedTubeConnections[command.Params["tube"]]; ok && 130 | len(client.watchedTubeConnections) > 1 { 131 | delete(client.watchedTubeConnections, command.Params["tube"]) 132 | command.Params["count"] = strconv.FormatInt(int64(len(client.watchedTubeConnections)), 10) 133 | } else { 134 | command.Err = errors.New(architecture.NOT_IGNORED) 135 | } 136 | case architecture.RESERVE: 137 | command = client.reserve(command) 138 | case architecture.RESERVE_WITH_TIMEOUT: 139 | command = client.reserve(command) 140 | case architecture.DELETE: 141 | if tube, ok := client.reservedJobs[command.Params["id"]]; ok { 142 | if con, ok := client.watchedTubeConnections[tube]; ok { 143 | con <- command.Copy() 144 | command = <-con 145 | } 146 | } else { 147 | command.Err = errors.New(architecture.NOT_FOUND) 148 | } 149 | case architecture.RELEASE: 150 | if tube, ok := client.reservedJobs[command.Params["id"]]; ok { 151 | if con, ok := client.watchedTubeConnections[tube]; ok { 152 | con <- command 153 | command = <-con 154 | } 155 | } else { 156 | command.Err = errors.New(architecture.NOT_FOUND) 157 | } 158 | case architecture.BURY: 159 | if tube, ok := client.reservedJobs[command.Params["id"]]; ok { 160 | if con, ok := client.watchedTubeConnections[tube]; ok { 161 | con <- command.Copy() 162 | command = <-con 163 | } 164 | } else { 165 | command.Err = errors.New(architecture.NOT_FOUND) 166 | } 167 | case architecture.KICK_JOB: 168 | client.usedTubeConnection <- command.Copy() 169 | command = <-client.usedTubeConnection 170 | case architecture.KICK: 171 | client.usedTubeConnection <- command.Copy() 172 | command = <-client.usedTubeConnection 173 | case architecture.TOUCH: 174 | 175 | case architecture.QUIT: 176 | client.conn.Close() 177 | } 178 | 179 | return command 180 | } 181 | 182 | func (client *clientHandler) reserve(command architecture.Command) architecture.Command { 183 | // iterate and create a list of watched connections to receive from 184 | receiveConnections := []chan architecture.Command{} 185 | receiveConnectionNames := []string{} 186 | for name, connection := range client.watchedTubeConnections { 187 | connection <- command.Copy() 188 | receiveConnections = append(receiveConnections, <-client.watchedTubeConnectionsReceiver) 189 | receiveConnectionNames = append(receiveConnectionNames, name) 190 | } 191 | // receive from one of the channels 192 | cases := make([]reflect.SelectCase, len(receiveConnections)) 193 | for i, ch := range receiveConnections { 194 | cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)} 195 | } 196 | chosen, value, _ := reflect.Select(cases) 197 | resultCommand := value.Interface().(architecture.Command) 198 | resultCommand.Params["tube"] = receiveConnectionNames[chosen] 199 | client.reservedJobs[resultCommand.Job.Id()] = resultCommand.Params["tube"] 200 | return resultCommand 201 | } 202 | --------------------------------------------------------------------------------