├── .gitignore ├── .goci.yml ├── .travis.yml ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── Makefile ├── README.md ├── config.example.yml ├── control.sh ├── cron ├── cron_test.go └── log_rotate.go ├── falcon ├── falcon_test.go ├── metrics.go └── senddata.go ├── funcs ├── aliveness.go ├── exchange.go ├── funcs_test.go ├── node.go ├── overview.go ├── queues.go └── utils.go ├── g ├── cfg.go ├── g.go ├── rabbit.go └── var.go ├── main.go ├── utils └── date.go ├── vendor ├── github.com │ ├── barryz │ │ └── cron │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── constantdelay.go │ │ │ ├── cron.go │ │ │ ├── doc.go │ │ │ ├── parser.go │ │ │ ├── semaphore.go │ │ │ └── spec.go │ ├── braintree │ │ └── manners │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── interfaces.go │ │ │ ├── server.go │ │ │ └── static.go │ ├── codegangsta │ │ └── inject │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── inject.go │ │ │ └── update_readme.sh │ ├── go-martini │ │ └── martini │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── env.go │ │ │ ├── go_version.go │ │ │ ├── logger.go │ │ │ ├── martini.go │ │ │ ├── recovery.go │ │ │ ├── response_writer.go │ │ │ ├── return_handler.go │ │ │ ├── router.go │ │ │ ├── static.go │ │ │ └── wercker.yml │ ├── martini-contrib │ │ ├── auth │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── basic.go │ │ │ ├── util.go │ │ │ └── wercker.yml │ │ └── render │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── render.go │ │ │ └── wercker.yml │ ├── oxtoacart │ │ └── bpool │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── bpool.go │ │ │ ├── bufferpool.go │ │ │ ├── bytepool.go │ │ │ └── sizedbufferpool.go │ ├── streadway │ │ └── amqp │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── CONTRIBUTING.md │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── allocator.go │ │ │ ├── auth.go │ │ │ ├── certs.sh │ │ │ ├── channel.go │ │ │ ├── confirms.go │ │ │ ├── connection.go │ │ │ ├── consumers.go │ │ │ ├── delivery.go │ │ │ ├── doc.go │ │ │ ├── fuzz.go │ │ │ ├── gen.sh │ │ │ ├── pre-commit │ │ │ ├── read.go │ │ │ ├── return.go │ │ │ ├── spec091.go │ │ │ ├── types.go │ │ │ ├── uri.go │ │ │ └── write.go │ └── toolkits │ │ └── file │ │ ├── downloader.go │ │ ├── file.go │ │ ├── reader.go │ │ └── writer.go └── gopkg.in │ └── yaml.v2 │ ├── .travis.yml │ ├── LICENSE │ ├── LICENSE.libyaml │ ├── NOTICE │ ├── README.md │ ├── apic.go │ ├── decode.go │ ├── emitterc.go │ ├── encode.go │ ├── go.mod │ ├── parserc.go │ ├── readerc.go │ ├── resolve.go │ ├── scannerc.go │ ├── sorter.go │ ├── writerc.go │ ├── yaml.go │ ├── yamlh.go │ └── yamlprivateh.go ├── version └── version.go └── witch ├── handler.go ├── server.go ├── system ├── launcher.go ├── statsdb.go ├── supervisor.go ├── system.go └── systemd.go ├── witch.go └── witch_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | cfg.json 3 | spiderQ 4 | var/* 5 | 6 | # Created by https://www.gitignore.io/api/vim,go 7 | 8 | ### Go ### 9 | # Binaries for programs and plugins 10 | *.exe 11 | *.exe~ 12 | *.dll 13 | *.so 14 | *.dylib 15 | 16 | # Test binary, build with `go test -c` 17 | *.test 18 | 19 | # Output of the go coverage tool, specifically when used with LiteIDE 20 | *.out 21 | 22 | ### Vim ### 23 | # swap 24 | .sw[a-p] 25 | .*.sw[a-p] 26 | # session 27 | Session.vim 28 | # temporary 29 | .netrwhist 30 | *~ 31 | # auto-generated tag files 32 | tags 33 | 34 | 35 | # End of https://www.gitignore.io/api/vim,go 36 | -------------------------------------------------------------------------------- /.goci.yml: -------------------------------------------------------------------------------- 1 | test: go test -v ./... 2 | repo_type: github 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | - 1.x 5 | - tip 6 | install: 7 | - go get -v github.com/golang/lint/golint 8 | - go build ./... 9 | script: 10 | - go vet ./... 11 | - $HOME/gopath/bin/golint . 12 | - go test -v --race ./... 13 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | branch = "master" 6 | name = "github.com/barryz/cron" 7 | packages = ["."] 8 | revision = "bebc2953afa66b36d2872f26cd77d185c6e15e44" 9 | 10 | [[projects]] 11 | branch = "master" 12 | name = "github.com/braintree/manners" 13 | packages = ["."] 14 | revision = "82a8879fc5fd0381fa8b2d8033b19bf255252088" 15 | 16 | [[projects]] 17 | branch = "master" 18 | name = "github.com/codegangsta/inject" 19 | packages = ["."] 20 | revision = "33e0aa1cb7c019ccc3fbe049a8262a6403d30504" 21 | 22 | [[projects]] 23 | name = "github.com/go-martini/martini" 24 | packages = ["."] 25 | revision = "fe605b5cd210047ae3bb73d2b69a5b912a9b423d" 26 | 27 | [[projects]] 28 | branch = "master" 29 | name = "github.com/martini-contrib/auth" 30 | packages = ["."] 31 | revision = "fa62c19b7ae87da370242054c8beb11af31d327f" 32 | 33 | [[projects]] 34 | branch = "master" 35 | name = "github.com/martini-contrib/render" 36 | packages = ["."] 37 | revision = "ec18f8345a1181146728238980606fb1d6f40e8c" 38 | 39 | [[projects]] 40 | branch = "master" 41 | name = "github.com/oxtoacart/bpool" 42 | packages = ["."] 43 | revision = "4e1c5567d7c2dd59fa4c7c83d34c2f3528b025d6" 44 | 45 | [[projects]] 46 | name = "github.com/streadway/amqp" 47 | packages = ["."] 48 | revision = "63795daa9a446c920826655f26ba31c81c860fd6" 49 | 50 | [[projects]] 51 | branch = "master" 52 | name = "github.com/toolkits/file" 53 | packages = ["."] 54 | revision = "a5b3c5147e07f934d9f9d00b901a20b2529f1a57" 55 | 56 | [[projects]] 57 | name = "gopkg.in/yaml.v2" 58 | packages = ["."] 59 | revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" 60 | version = "v2.2.1" 61 | 62 | [solve-meta] 63 | analyzer-name = "dep" 64 | analyzer-version = 1 65 | inputs-digest = "93e9c16c79ccadb5bd326616fca71a68dd24eda22613b37c3d9cd328b20ad3eb" 66 | solver-name = "gps-cdcl" 67 | solver-version = 1 68 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | branch = "master" 30 | name = "github.com/barryz/cron" 31 | 32 | [[constraint]] 33 | branch = "master" 34 | name = "github.com/braintree/manners" 35 | 36 | [[constraint]] 37 | branch = "master" 38 | name = "github.com/martini-contrib/auth" 39 | 40 | [[constraint]] 41 | branch = "master" 42 | name = "github.com/martini-contrib/render" 43 | 44 | [[constraint]] 45 | branch = "master" 46 | name = "github.com/toolkits/file" 47 | 48 | [prune] 49 | go-tests = true 50 | unused-packages = true 51 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build clean 2 | NOW=$(shell date '+%Y-%m-%d_%H:%M:%S') 3 | REV?=$(shell git rev-parse --short HEAD) 4 | LDFLAGS=-ldflags '-X github.com/barryz/rmqmonitor/version.Build=${NOW}@${REV} -w -s' 5 | BINARY="spiderQ" 6 | build: 7 | go build -o ./${BINARY} ${LDFLAGS} 8 | clean: 9 | if test -f ${BINARY}; then \ 10 | rm -f ${BINARY}; fi 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rmqmonitor 2 | 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/barryz/rmqmonitor)](https://goreportcard.com/report/github.com/barryz/rmqmonitor) 4 | ![GoCI](http://goci.ele.me/na/goci/eleme/goci/badge?type=job) 5 | [![Build Status](https://travis-ci.org/barryz/rmqmonitor.svg?branch=master)](https://travis-ci.org/barryz/rmqmonitor) 6 | [![Apache 2 licensed](https://img.shields.io/badge/license-Apache2-blue.svg)](https://raw.githubusercontent.com/oklog/run/master/LICENSE) 7 | 8 | rmqmonitor is an agent that used for [open-falcon](http://open-falcon.org/) to monitoring [RabbitMQ](https://www.rabbitmq.com/). 9 | 10 | ## Arch Requirement 11 | Linux 12 | 13 | ## Build 14 | 15 | ```bash 16 | $make build 17 | ``` 18 | 19 | ## Agent launch 20 | 21 | ```bash 22 | $/bin/bash control.sh start|stop|restart 23 | ``` 24 | It will create a temporary directory `var` in your current path. 25 | 26 | ## Metrics 27 | 28 | ***overview metrics***: 29 | 30 | | key | tag | type | note | 31 | |-----|-----|------|------| 32 | |rabbitmq.overview.publishRate| |GAUGE|rate of message publishing| 33 | |rabbitmq.overview.deliverRate| |GAUGE|rate of message delivering| 34 | |rabbitmq.overview.redeliverRate| |GAUGE|rate of message re-delivering| 35 | |rabbitmq.overview.ackRate| |GAUGE|rate of message acknowledging| 36 | |rabbitmq.overview.msgsTotal| |GAUGE|total messages(sum of unack and ready| 37 | |rabbitmq.overview.msgsReadyTotal| |GAUGE|ready messages(not deliver yet)| 38 | |rabbitmq.overview.msgsUnackTotal| |GAUGE|un-acknowledged messages| 39 | |rabbitmq.overview.publishTotal| |GAUGE|total messaees of publishing| 40 | |rabbitmq.overview.deliverTotal| |GAUGE|total messaees of delivering| 41 | |rabbitmq.overview.redeliverTotal| |GAUGE|total messaees of re-delivering| 42 | |rabbitmq.overview.channlesTotal| |GAUGE|total channels| 43 | |rabbitmq.overview.connectionsTotal| |GAUGE|total connections| 44 | |rabbitmq.overview.consumersTotal| |GAUGE|total counsumers| 45 | |rabbitmq.overview.queuesTotal| |GAUGE|total queues| 46 | |rabbitmq.overview.exchangesTotal| |GAUGE|total exchanges| 47 | |rabbitmq.overview.isAlive| |GAUGE|healthy status of cluster| 48 | |rabbitmq.overview.isPartition| |GAUGE|partition status of cluster| 49 | |rabbitmq.overview.memUsedPct| |GAUGE|memory usage percentage| 50 | |rabbitmq.overview.fdUsedPct| |GAUGE|percentage of fd usage| 51 | |rabbitmq.overview.erlProcsUsedPct| |GAUGE|percentage of erlang processes| 52 | |rabbitmq.overview.socketUsedPct| |GAUGE|percentage of socket usage| 53 | |rabbitmq.overview.statsDbEvent| |GAUGE|the events of queue produced by management database| 54 | |rabbitmq.overview.ioReadawait| |GAUGE|io_read_avg_wait_time| 55 | |rabbitmq.overview.ioWriteawait| |GAUGE|io_write_avg_wait_time| 56 | |rabbitmq.overview.ioSyncawait| |GAUGE|io_sync_avg_wait_time| 57 | |rabbitmq.overview.memConnreader| |GAUGE|memory usage for connections reader| 58 | |rabbitmq.overview.memConnwriter| |GAUGE|memory usage for connections writer| 59 | |rabbitmq.overview.memConnchannels| |GAUGE|memory usage for connections channels| 60 | |rabbitmq.overview.memMgmtdb| |GAUGE|memory usage for management db)| 61 | |rabbitmq.overview.memMnesia| |GAUGE|memory usage for mnesia database)| 62 | |rabbitmq.overview.runQueue| |GAUGE|total run_queues of Erlang| 63 | |rabbitmq.overview.getChannelCost | |GAUGE|latency for getting channels| 64 | |rabbitmq.overview.memAlarm| |GAUGE|memory alarm triggered| 65 | |rabbitmq.overview.diskAlarm| |GAUGE|disc alarm triggered| 66 | 67 | ***Queue Metrics*** 68 | 69 | | key | tag | type | note | 70 | |-----|-----|------|------| 71 | |rabbitmq.queue.publish|name=$queue-name,vhost=$vhost|GAUGE|rate of message publishing with specified queue| 72 | |rabbitmq.queue.delver_get|name=$queue-name,vhost=$vhost|GAUGE|rate of message delivering with specified queue| 73 | |rabbitmq.queue.redeliver|name=$queue-name,vhost=$vhost|GAUGE|rate of message re-delivering with specified queue| 74 | |rabbitmq.queue.ack|name=$queue-name,vhost=$vhost|GAUGE|rate of message acknowledging with specified queue| 75 | |rabbitmq.queue.consumers|name=$queue-name,vhost=$vhost|GAUGE|total consumers with specified queue| 76 | |rabbitmq.queue.consumer_utilisation|name=$queue-name,vhost=$vhost|GAUGE|consumers utilisation of the specified queue| 77 | |rabbitmq.queue.dpratio|name=$queue-name,vhost=$vhost|GAUGE|the radio of deliver and publish with specified queue| 78 | |rabbitmq.queue.memory|name=$queue-name,vhost=$vhost|GAUGE|total memories occupied with specified queue| 79 | |rabbitmq.queue.messages|name=$queue-name,vhost=$vhost|GAUGE|total messages with specified queue| 80 | |rabbitmq.queue.messages_ready|name=$queue-name,vhost=$vhost|GAUGE|ready messages with specified queue| 81 | |rabbitmq.queue.messages_unacked|name=$queue-name,vhost=$vhost|GAUGE|un-ack messageis with specified queue| 82 | |rabbitmq.queue.messages_status|name=$queue-name,vhost=$vhost|GAUGE|the status of the specified queue| 83 | 84 | 85 | ***Exchange Metrics*** 86 | 87 | | key | tag | type | note | 88 | |-----|-----|------|------| 89 | |rabbitmq.exchange.publish_in|name=$exchange-name,vhost=$vhost|GAUGE|publishing-inboud rate of the specified exchange| 90 | |rabbitmq.exchange.publish_out|name=$exchange-name,vhost=$vhost|GAUGE|publishing-outboud rate of the specified exchange| 91 | |rabbitmq.exchange.confirm|name=$exchange-name,vhost=$vhost|GAUGE|acknowledging rate of the specified exchange| 92 | 93 | --- 94 | 95 | ## Witch 96 | spiderQ will starting a web server to handle several instructions which to control RabbitMQ process state. 97 | 98 | The web server listening on port 5671 by default, it enable basicauth, and handle client's requests. 99 | 100 | ***RabbitMQ process management(graceful)*** 101 | 102 | ```bash 103 | curl -u noadmin:ADMIN -XPUT -d '{"name":"is_alive"}' http://127.0.0.1:5671/api/app/actions 104 | 105 | curl -u noadmin:ADMIN -XPUT -d '{"name":"start"}' http://127.0.0.1:5671/api/app/actions 106 | 107 | curl -u noadmin:ADMIN -XPUT -d '{"name":"stop"}' http://127.0.0.1:5671/api/app/actions 108 | 109 | curl -u noadmin:ADMIN -XPUT -d '{"name":"restart"}' http://127.0.0.1:5671/api/app/actions 110 | ``` 111 | 112 | ***Stop RabbitMQ process forcibly*** 113 | 114 | ```bash 115 | curl -u noadmin:ADMIN -XGET http://127.0.0.1:5671/api/app/fstop 116 | ``` 117 | 118 | ***Get the healthy status of single RabbitMQ node*** 119 | 120 | ```bash 121 | curl -u noadmin:ADMIN -XGET http://127.0.0.1:5671/api/stats 122 | ``` 123 | 124 | ***Start/Stop/Restart RabbitMQ statistics management database*** 125 | 126 | ```bash 127 | curl -u noadmin:ADMIN -XPUT -d '{"name":"reset"}' http://127.0.0.1:5671/api/stats/actions 128 | 129 | curl -u noadmin:ADMIN -XPUT -d '{"name":"crash"}' http://127.0.0.1:5671/api/stats/actions 130 | 131 | curl -u noadmin:ADMIN -XPUT -d '{"name":"terminate"}' http://127.0.0.1:5671/api/stats/actions 132 | ``` 133 | -------------------------------------------------------------------------------- /config.example.yml: -------------------------------------------------------------------------------- 1 | # debug mode 2 | debug: false 3 | # getting metric details 4 | details: true 5 | # hostname specified 6 | hostname: "" 7 | # agent collect interval 8 | interval: 10 9 | # push batch-size metrics per-operation 10 | batchsize: 100 11 | # enable functions 12 | enabled: 13 | collect: true 14 | witch: true 15 | log_rotate: true 16 | 17 | # http client settings (timeout in seconds) 18 | http: 19 | conn_timeout: 10 20 | response_timeout: 10 21 | 22 | # RabbitMQ settings 23 | rabbitmq: 24 | host: 127.0.0.1 25 | port: 15672 26 | user: admin 27 | password: admin 28 | 29 | # falcon settings 30 | falcon: 31 | api: "http://127.0.0.1:1988/v1/push" 32 | 33 | # scheduler config 34 | scheduler: 35 | log_rotate: "0 11 18 ? * 0-6" 36 | 37 | # witch config 38 | witch: 39 | listen: ":5671" 40 | control: "buildin" 41 | service: "" 42 | command: "sleep 120" 43 | process: "beam" 44 | pid_file: "var/run/witch.pid" 45 | auth: 46 | admin: "ADMIN" 47 | 48 | # filter for queues 49 | ignore_queue: 50 | - "test" 51 | - "celery" 52 | 53 | # status which indicate queue's running state 54 | qrunning: 55 | - "idle" 56 | - "running" 57 | -------------------------------------------------------------------------------- /control.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WORKSPACE=$(cd $(dirname $0)/; pwd) 4 | cd $WORKSPACE 5 | 6 | mkdir -p var 7 | 8 | module=agent 9 | app=spiderQ 10 | conf=cfg.json 11 | pidfile=var/app.pid 12 | logfile=var/app.log 13 | 14 | function check_pid() { 15 | if [ -f $pidfile ];then 16 | pid=`cat $pidfile` 17 | if [ -n $pid ]; then 18 | running=`ps -p $pid|grep -v "PID TTY" |wc -l` 19 | return $running 20 | fi 21 | fi 22 | return 0 23 | } 24 | 25 | function start() { 26 | check_pid 27 | running=$? 28 | if [ $running -gt 0 ];then 29 | echo -n "$app now is running already, pid=" 30 | cat $pidfile 31 | return 1 32 | fi 33 | 34 | if ! [ -f $conf ];then 35 | echo "Config file $conf doesn't exist, creating one." 36 | cp cfg.example.json $conf 37 | fi 38 | nohup ./$app -c $conf &> $logfile & 39 | sleep 1 40 | running=`ps -p $! | grep -v "PID TTY" | wc -l` 41 | if [ $running -gt 0 ];then 42 | echo $! > $pidfile 43 | echo "$app started..., pid=$!" 44 | else 45 | echo "$app failed to start." 46 | return 1 47 | fi 48 | } 49 | 50 | function stop() { 51 | pid=`cat $pidfile` 52 | skill -9 $pid &>/dev/null 53 | rm -f $pidfile 54 | echo "$app stoped..." 55 | } 56 | 57 | function restart() { 58 | stop 59 | sleep 1 60 | start 61 | } 62 | 63 | function status() { 64 | check_pid 65 | running=$? 66 | if [ $running -gt 0 ];then 67 | echo started 68 | else 69 | echo stoped 70 | fi 71 | } 72 | 73 | function tailf() { 74 | tail -f $logfile 75 | } 76 | 77 | function help() { 78 | echo "$0 build|pack|start|stop|restart|status|tail" 79 | } 80 | 81 | if [ "$1" == "" ]; then 82 | help 83 | elif [ "$1" == "stop" ];then 84 | stop 85 | elif [ "$1" == "start" ];then 86 | start 87 | elif [ "$1" == "restart" ];then 88 | restart 89 | elif [ "$1" == "status" ];then 90 | status 91 | elif [ "$1" == "tail" ];then 92 | tailf 93 | else 94 | help 95 | fi 96 | -------------------------------------------------------------------------------- /cron/cron_test.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestLogRotateStart(t *testing.T) { 9 | fmt.Println("testing") 10 | //g.ParseConfig("../cfg.json.example") 11 | // 12 | //go Start() 13 | // 14 | //select {} 15 | } 16 | -------------------------------------------------------------------------------- /cron/log_rotate.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os/exec" 7 | 8 | "github.com/barryz/rmqmonitor/g" 9 | "github.com/barryz/rmqmonitor/utils" 10 | 11 | "github.com/barryz/cron" 12 | ) 13 | 14 | func logRotateRun() { 15 | log.Println("[INFO] start to rotate rabbitmq log file ......") 16 | suffix := fmt.Sprintf(".%s", utils.GetYesterdayDate()) 17 | logRotateCommand := exec.Command("rabbitmqctl", "rotate_logs", suffix) 18 | output, err := logRotateCommand.CombinedOutput() 19 | if err != nil { 20 | log.Printf("[ERROR]: rotate rabbitmq log failed due to %s", err.Error()) 21 | return 22 | } 23 | log.Printf("[INFO] rotate rabbitmq log success, %s", string(output)) 24 | return 25 | } 26 | 27 | // Start start the cron tab 28 | func Start() { 29 | logrotateCron := cron.New() 30 | logrotateCron.AddFuncCC(g.Config().Cron.LogRotate, func() { logRotateRun() }, 1) 31 | logrotateCron.Start() 32 | } 33 | -------------------------------------------------------------------------------- /falcon/falcon_test.go: -------------------------------------------------------------------------------- 1 | package falcon 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/barryz/rmqmonitor/g" 7 | ) 8 | 9 | func TestGetStatsDB(t *testing.T) { 10 | g.ParseConfig("../config.example.yml") 11 | 12 | //ov, err := funcs.GetOverview() 13 | //if err != nil { 14 | // t.Fatalf("%s", err.Error()) 15 | //} 16 | // 17 | //updateCurrentStatsDB(ov.StatisticsDbNode) 18 | // 19 | //stats := GetCurrentStatsDB() 20 | //fmt.Printf("%s --- %s \n", stats.CurrentLocate, stats.PreviousLocate) 21 | } 22 | -------------------------------------------------------------------------------- /falcon/senddata.go: -------------------------------------------------------------------------------- 1 | package falcon 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "net/http" 9 | 10 | "github.com/barryz/rmqmonitor/g" 11 | ) 12 | 13 | func sendData(data []*MetaData) (resp []byte, err error) { 14 | debug := g.Config().Debug 15 | js, err := json.Marshal(data) 16 | if err != nil { 17 | return 18 | } 19 | 20 | if debug { 21 | log.Printf("agent api received %d metrics", len(data)) 22 | } 23 | 24 | res, err := http.Post(g.Config().Falcon.API, "Content-Type: application/json", bytes.NewBuffer(js)) 25 | if err != nil { 26 | err = fmt.Errorf("[ERROR]: sent data to falcon agent api fail due to %s", err.Error()) 27 | return 28 | } 29 | 30 | defer res.Body.Close() 31 | 32 | return 33 | } 34 | 35 | func sendDatas(m []*MetaData) { 36 | // batch-size specified. 37 | limit, lens := g.Config().Batchsize, len(m) 38 | if lens >= limit { 39 | offset := lens % limit 40 | for i := 0; i <= lens-1; i += limit { 41 | if (i + limit - 1) >= lens { 42 | _, err := sendData(m[i:(offset + i - 1)]) 43 | if err != nil { 44 | log.Println(err.Error()) 45 | break 46 | } 47 | } else { 48 | _, err := sendData(m[i:(limit + i - 1)]) 49 | if err != nil { 50 | log.Println(err.Error()) 51 | break 52 | } 53 | } 54 | } 55 | } else { 56 | _, err := sendData(m) 57 | if err != nil { 58 | log.Println(err.Error()) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /funcs/aliveness.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/barryz/rmqmonitor/g" 8 | ) 9 | 10 | // Aliveness ... 11 | type Aliveness struct { 12 | Status string `json:"status"` 13 | } 14 | 15 | // GetAlive ... 16 | func GetAlive() (aliveness *Aliveness, err error) { 17 | service := "aliveness-test/%2f" 18 | 19 | res, err := g.RabbitAPI(service) 20 | if err != nil { 21 | err = fmt.Errorf("[ERROR]: get rabbitmq aliveness fail due to %s", err.Error()) 22 | return 23 | } 24 | 25 | err = json.Unmarshal(res, &aliveness) 26 | if err != nil { 27 | err = fmt.Errorf("[ERROR]: unmarshal rabbitmq aliveness json data fail due to %s", err.Error()) 28 | return 29 | } 30 | 31 | return 32 | } 33 | 34 | // CheckAlive ... 35 | func CheckAlive() (ok bool) { 36 | service := "whoami" 37 | if _, err := g.RabbitAPI(service); err == nil { 38 | ok = true 39 | return 40 | } 41 | 42 | return 43 | } 44 | -------------------------------------------------------------------------------- /funcs/exchange.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/barryz/rmqmonitor/g" 9 | ) 10 | 11 | // VHostName name of vHost 12 | type VHostName struct { 13 | Name string `json:"name"` 14 | } 15 | 16 | // ExchangeInfo information for exchange 17 | type ExchangeInfo struct { 18 | Name string `json:"name"` 19 | VHost string `json:"vhost"` 20 | MsgStats struct { 21 | Confirm int64 `json:"confirm"` 22 | PublishIn int64 `json:"publish_in"` 23 | PublishOut int64 `json:"publish_out"` 24 | 25 | ConfirmRate struct { 26 | Rate float64 `json:"rate"` 27 | } `json:"confirm_details"` 28 | 29 | PublishInRate struct { 30 | Rate float64 `json:"rate"` 31 | } `json:"publish_in_details"` 32 | 33 | PublishOutRate struct { 34 | Rate float64 `json:"rate"` 35 | } `json:"publish_out_details"` 36 | } `json:"message_stats"` 37 | } 38 | 39 | func getVHosts() (vl []string, err error) { 40 | service := "vhosts" 41 | res, err := g.RabbitAPI(service) 42 | if err != nil { 43 | err = fmt.Errorf("[ERROR]: get rabbitmq vhost info fail due to %s", err.Error()) 44 | return 45 | } 46 | 47 | var vs []*VHostName 48 | err = json.Unmarshal(res, &vs) 49 | if err != nil { 50 | err = fmt.Errorf("[ERROR]: unmarshal rabbitmq vhost json data fail due to %s", err.Error()) 51 | return 52 | } 53 | 54 | vl = make([]string, len(vs)) 55 | for _, v := range vs { 56 | vl = append(vl, urlEncode(v.Name)) 57 | } 58 | return 59 | } 60 | 61 | // GetExchanges get all exchanges 62 | func GetExchanges() (exchs []*ExchangeInfo, err error) { 63 | vhosts, err := getVHosts() 64 | if err != nil { 65 | return 66 | } 67 | 68 | exchs = make([]*ExchangeInfo, 0) 69 | for _, v := range vhosts { 70 | var ( 71 | es []*ExchangeInfo 72 | err1 error 73 | ) 74 | service := fmt.Sprintf("exchanges/%s", v) 75 | res, err1 := g.RabbitAPI(service) 76 | if err1 != nil { 77 | err = err1 78 | return 79 | } 80 | 81 | err1 = json.Unmarshal(res, &es) 82 | if err1 != nil { 83 | err = err1 84 | return 85 | } 86 | 87 | for _, e := range es { 88 | if e.Name == "" { 89 | e.Name = "DEFAULT_EXCHANGE" 90 | } 91 | 92 | if strings.Contains(e.Name, "amq.") { 93 | continue 94 | } 95 | exchs = append(exchs, e) 96 | } 97 | } 98 | 99 | return 100 | } 101 | -------------------------------------------------------------------------------- /funcs/funcs_test.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/barryz/rmqmonitor/g" 7 | ) 8 | 9 | func TestGetExchanges(t *testing.T) { 10 | g.ParseConfig("../config.example.yml") 11 | //exchs, err := GetExchanges() 12 | //if err != nil { 13 | // t.Error(err.Error()) 14 | //} 15 | //for _, e := range exchs { 16 | // fmt.Printf("%s rate is %d, vhost is %s\n", e.Name, e.MsgStats.PublishIn, e.VHost) 17 | //} 18 | } 19 | -------------------------------------------------------------------------------- /funcs/node.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | 8 | "github.com/barryz/rmqmonitor/g" 9 | ) 10 | 11 | // MemStats ... 12 | type MemStats struct { 13 | Total int64 `json:"total"` 14 | ConnectionReaders int64 `json:"connection_readers"` 15 | ConnectionWriters int64 `json:"connection_writers"` 16 | ConnectionChannels int64 `json:"connection_channels"` 17 | ConnectionOther int64 `json:"connection_other"` 18 | QueueProcs int64 `json:"queue_procs"` 19 | QueueSlaveProcs int64 `json:"queue_slave_procs"` 20 | Plugins int64 `json:"plugins"` 21 | Mnesia int64 `json:"mnesia"` 22 | MgmtDB int64 `json:"mgmt_db"` 23 | MsgIndex int64 `json:"msg_index"` 24 | Code int64 `json:"code"` 25 | Atom int64 `json:"atom"` 26 | Binary int64 `json:"binary"` 27 | } 28 | 29 | // NodeStats ... 30 | type NodeStats struct { 31 | MemStats `json:"memory"` 32 | Partitions []string `json:"partitions"` 33 | Rawait float64 `json:"io_read_avg_time"` 34 | Wawait float64 `json:"io_write_avg_time"` 35 | Syncawait float64 `json:"io_sync_avg_time"` 36 | MemUsed int64 `json:"mem_used"` 37 | MemLimit int64 `json:"mem_limit"` 38 | SocketsUsed int64 `json:"sockets_used"` 39 | SocketsTotal int64 `json:"sockets_total"` 40 | FdUsed int64 `json:"fd_used"` 41 | FdTotal int64 `json:"fd_total"` 42 | ErlProcUsed int64 `json:"proc_used"` 43 | ErlProcTotal int64 `json:"proc_total"` 44 | RunQueues int64 `json:"run_queue"` 45 | MemAlarm bool `json:"mem_alarm"` 46 | DiskAlarm bool `json:"disk_free_alarm"` 47 | } 48 | 49 | // MemAlarmStatus memory alarm status 50 | func (n *NodeStats) MemAlarmStatus() int { 51 | if n.MemAlarm { 52 | return 0 53 | } 54 | return 1 55 | } 56 | 57 | // DiskAlarmStatus disc alarm status 58 | func (n *NodeStats) DiskAlarmStatus() int { 59 | if n.DiskAlarm { 60 | return 0 61 | } 62 | return 1 63 | } 64 | 65 | // GetNode ... 66 | func GetNode() (n *NodeStats, err error) { 67 | host := g.GetHost() 68 | if g.Config().Debug { 69 | log.Printf("[INFO]: Get hostname %s.", host) 70 | } 71 | 72 | service := "nodes/rabbit@" + host + "?memory=true" 73 | // service := "nodes/rabbit@" + "vm-test-barryz" + "?memory=true" 74 | res, err := g.RabbitAPI(service) 75 | if err != nil { 76 | err = fmt.Errorf("[ERROR]: get rabbitmq node info fail due to %s", err.Error()) 77 | return 78 | } 79 | 80 | err = json.Unmarshal(res, &n) 81 | if err != nil { 82 | err = fmt.Errorf("[ERROR]: unmarshal rabbitmq node info json data fail due to %s", err.Error()) 83 | return 84 | } 85 | return 86 | } 87 | -------------------------------------------------------------------------------- /funcs/overview.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/barryz/rmqmonitor/g" 9 | 10 | "github.com/streadway/amqp" 11 | ) 12 | 13 | // Rate ... 14 | type Rate struct { 15 | Rate float64 `json:"rate"` 16 | } 17 | 18 | // MsgStat ... 19 | type MsgStat struct { 20 | Publish int64 `json:"publish"` 21 | Ack int64 `json:"ack"` 22 | DeliverGet int64 `json:"deliver_get"` 23 | Redeliver int64 `json:"redeliver"` 24 | Confirm int64 `json:"confirm"` 25 | Deliver int64 `json:"deliver"` 26 | DeliverNoAck int64 `json:"deliver_no_ack"` 27 | Get int64 `json:"get"` 28 | GetNoAck int64 `json:"get_no_ack"` 29 | PublishRates Rate `json:"publish_details"` 30 | DeliverGetRates Rate `json:"deliver_get_details"` 31 | AckRates Rate `json:"ack_details"` 32 | ConfirmRates Rate `json:"confirm_details"` 33 | RedeliverRates Rate `json:"redeliver_details"` 34 | DeliverRates Rate `json:"deliver_details"` 35 | DeliverNoAckRates Rate `json:"deliver_no_ack_details"` 36 | GetNoAckRates Rate `json:"get_no_ack_details"` 37 | GetRates Rate `json:"get_details"` 38 | } 39 | 40 | // QueueTotal ... 41 | type QueueTotal struct { 42 | MsgsTotal int64 `json:"messages"` 43 | MsgsReadyTotal int64 `json:"messages_ready"` 44 | MsgsUnackedTotal int64 `json:"messages_unacknowledged"` 45 | } 46 | 47 | // ObjectTotal ... 48 | type ObjectTotal struct { 49 | Consumers int64 `json:"consumers"` 50 | Queues int64 `json:"queues"` 51 | Exchanges int64 `json:"exchanges"` 52 | Connections int64 `json:"connections"` 53 | Channels int64 `json:"channels"` 54 | } 55 | 56 | // OverView ... 57 | type OverView struct { 58 | MsgStat `json:"message_stats"` 59 | QueueTotal `json:"queue_totals"` 60 | ObjectTotal `json:"object_totals"` 61 | StatsDbEvents int `json:"statistics_db_event_queue"` 62 | StatisticsDbNode string `json:"statistics_db_node"` 63 | } 64 | 65 | // GetOverview ... 66 | func GetOverview() (result *OverView, err error) { 67 | service := "overview" 68 | res, err := g.RabbitAPI(service) 69 | if err != nil { 70 | err = fmt.Errorf("[ERROR]: get rabbitmq overview info fail due to %s", err.Error()) 71 | return 72 | } 73 | 74 | err = json.Unmarshal(res, &result) 75 | if err != nil { 76 | err = fmt.Errorf("[ERROR]: unmarshal rabbitmq overview json data fail due to %s", err.Error()) 77 | return 78 | } 79 | 80 | return 81 | } 82 | 83 | // GetChannelCost time cost of getting channel 84 | func GetChannelCost() (getChannelCost float64, err error) { 85 | uri := fmt.Sprintf("amqp://%s:%s@127.0.0.1:5672//", g.Config().Rabbit.User, g.Config().Rabbit.Password) 86 | 87 | conn, err := amqp.Dial(uri) 88 | if err != nil { 89 | err = fmt.Errorf("[ERROR]: get amqp connection fail due to %s", err.Error()) 90 | return 91 | } 92 | 93 | timeToStart := time.Now() 94 | ch, err := conn.Channel() 95 | getChannelCost = time.Now().Sub(timeToStart).Seconds() * 1000 96 | if err != nil { 97 | err = fmt.Errorf("[ERROR]: get amqp channel fail due to %s", err.Error()) 98 | return 99 | } 100 | 101 | ch.Close() 102 | conn.Close() 103 | 104 | return 105 | } 106 | -------------------------------------------------------------------------------- /funcs/queues.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/barryz/rmqmonitor/g" 9 | ) 10 | 11 | // QueueRate ... 12 | type QueueRate struct { 13 | Rate float64 `json:"rate"` 14 | } 15 | 16 | // QueueMsgStat ... 17 | type QueueMsgStat struct { 18 | Publish QueueRate `json:"publish_details"` 19 | DeliverGet QueueRate `json:"deliver_get_details"` 20 | Ack QueueRate `json:"ack_details"` 21 | Redeliver QueueRate `json:"redeliver_details"` 22 | } 23 | 24 | // QueueMap ... 25 | type QueueMap struct { 26 | Memory int64 `json:"memory"` 27 | Messages int64 `json:"messages"` 28 | MessagesReady int64 `json:"messages_ready"` 29 | MessagesUnacked int64 `json:"messages_unacknowledged"` 30 | ConsumerUtil interface{} `json:"consumer_utilisation"` 31 | Consumers int64 `json:"consumers"` 32 | Status string `json:"state"` 33 | Name string `json:"name"` 34 | Vhost string `json:"vhost"` 35 | AutoDelete bool `json:"auto_delete"` 36 | QueueMsgStat `json:"message_stats"` 37 | } 38 | 39 | func filterQueue(q *QueueMap) bool { 40 | isIgnore := false 41 | ignores := g.Config().Ignores 42 | 43 | for _, i := range ignores { 44 | if strings.Contains(strings.ToLower(q.Name), i) { 45 | isIgnore = true 46 | } else { 47 | continue 48 | } 49 | } 50 | 51 | if isIgnore { 52 | return true 53 | } 54 | 55 | return false 56 | } 57 | 58 | // GetQueues ... 59 | func GetQueues() (qm []*QueueMap, err error) { 60 | var ( 61 | queues []*QueueMap 62 | ) 63 | 64 | service := "queues" 65 | res, err := g.RabbitAPI(service) 66 | if err != nil { 67 | err = fmt.Errorf("[ERROR]: get rabbitmq queue info fail due to %s", err.Error()) 68 | return 69 | } 70 | 71 | err = json.Unmarshal(res, &queues) 72 | if err != nil { 73 | err = fmt.Errorf("[ERROR]: unmarshal rabbitmq queue json data fail due to %s", err.Error()) 74 | return 75 | } 76 | 77 | qm = make([]*QueueMap, len(queues)) 78 | for _, q := range queues { 79 | if !filterQueue(q) { 80 | qm = append(qm, q) 81 | } 82 | } 83 | 84 | return 85 | } 86 | -------------------------------------------------------------------------------- /funcs/utils.go: -------------------------------------------------------------------------------- 1 | package funcs 2 | 3 | import ( 4 | "net/url" 5 | ) 6 | 7 | func urlEncode(str string) string { 8 | return url.QueryEscape(str) 9 | } 10 | -------------------------------------------------------------------------------- /g/cfg.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "sync" 7 | 8 | "github.com/toolkits/file" 9 | "gopkg.in/yaml.v2" 10 | ) 11 | 12 | // EnableConfig configs which can be used 13 | type EnableConfig struct { 14 | Collect bool `yaml:"collect"` 15 | LogRotate bool `yaml:"log_rotate"` 16 | Witch bool `yaml:"witch"` 17 | } 18 | 19 | // RabbitConfig ... 20 | type RabbitConfig struct { 21 | Host string `yaml:"host"` 22 | Port int `yaml:"port"` 23 | User string `yaml:"user"` 24 | Password string `yaml:"password"` 25 | } 26 | 27 | // FalconConfig ... 28 | type FalconConfig struct { 29 | API string `yaml:"api"` 30 | } 31 | 32 | // HTTPConfig ... 33 | type HTTPConfig struct { 34 | ConnTimeout int `yaml:"conn_timeout"` 35 | RespTimeout int `yaml:"response_timeout"` 36 | } 37 | 38 | // SchedulerConfig ... 39 | type SchedulerConfig struct { 40 | LogRotate string `yaml:"log_rotate"` 41 | } 42 | 43 | // WitchConfig Program Config ... 44 | type WitchConfig struct { 45 | ListenAddr string `yaml:"listen"` 46 | Control string `yaml:"control"` 47 | Service string `yaml:"service"` 48 | Process string `yaml:"process"` 49 | Command string `yaml:"command"` 50 | PidFile string `yaml:"pid_file"` 51 | Auth map[string]string `yaml:"auth"` 52 | } 53 | 54 | // GlobalConfig ... 55 | type GlobalConfig struct { 56 | Debug bool `yaml:"debug"` 57 | Details bool `yaml:"details"` 58 | Hostname string `yaml:"hostname"` 59 | Batchsize int `yaml:"batchsize"` 60 | Interval int64 `yaml:"interval"` 61 | Rabbit *RabbitConfig `yaml:"rabbitmq"` 62 | Falcon *FalconConfig `yaml:"falcon"` 63 | HTTP *HTTPConfig `yaml:"http"` 64 | Cron *SchedulerConfig `yaml:"scheduler"` 65 | Enabled *EnableConfig `yaml:"enabled"` 66 | Ignores []string `yaml:"ignore_queue"` 67 | Qrunning []string `yaml:"qrunning"` 68 | Witch *WitchConfig `yaml:"witch"` 69 | } 70 | 71 | var ( 72 | config *GlobalConfig 73 | lock = new(sync.RWMutex) 74 | ) 75 | 76 | // Config ... 77 | func Config() *GlobalConfig { 78 | lock.RLock() 79 | defer lock.RUnlock() 80 | return config 81 | } 82 | 83 | // ParseConfig ... 84 | func ParseConfig(cfg string) { 85 | if cfg == "" { 86 | log.Println("use -c to specify configuration file") 87 | } 88 | 89 | var c GlobalConfig 90 | 91 | configContent, err := file.ToTrimString(cfg) 92 | if err != nil { 93 | log.Fatalln("[ERROR]: read config file:", cfg, "fail:", err) 94 | } 95 | 96 | err = yaml.Unmarshal([]byte(configContent), &c) 97 | if err != nil { 98 | log.Fatalln("[ERROR]: read config file:", cfg, "fail:", err) 99 | } 100 | 101 | if c.Hostname == "" { 102 | c.Hostname, err = os.Hostname() 103 | if err != nil { 104 | log.Fatalln("[ERROR]: get local hostname fail") 105 | os.Exit(1) 106 | } 107 | } 108 | 109 | config = &c 110 | 111 | log.Println("[INFO]: read config file:", cfg, "successfully") 112 | } 113 | -------------------------------------------------------------------------------- /g/g.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "log" 5 | "runtime" 6 | ) 7 | 8 | func init() { 9 | runtime.GOMAXPROCS(runtime.NumCPU() * 2) 10 | log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) 11 | } 12 | -------------------------------------------------------------------------------- /g/rabbit.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net" 7 | "net/http" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | // GetHost get hostname (syscall) 13 | func GetHost() string { 14 | return Config().Hostname 15 | } 16 | 17 | // GetAPIUrl return the RabbitMQ api url 18 | func GetAPIUrl(service string) string { 19 | port := Config().Rabbit.Port 20 | apiURL := fmt.Sprintf("http://%s:%s/api/%s", Config().Rabbit.Host, strconv.Itoa(port), service) 21 | return apiURL 22 | } 23 | 24 | // RabbitAPI ... 25 | func RabbitAPI(service string) ([]byte, error) { 26 | url := GetAPIUrl(service) 27 | user := Config().Rabbit.User 28 | connTimeout := Config().HTTP.ConnTimeout 29 | respTimeout := Config().HTTP.RespTimeout 30 | password := Config().Rabbit.Password 31 | 32 | // set connect/get/resp timeout 33 | client := &http.Client{ 34 | Transport: &http.Transport{ 35 | Dial: func(netw, addr string) (net.Conn, error) { 36 | c, err := net.DialTimeout(netw, addr, time.Second*time.Duration(connTimeout)) 37 | if err != nil { 38 | return nil, err 39 | } 40 | return c, nil 41 | 42 | }, 43 | DisableKeepAlives: true, // disable http keepalive 44 | ResponseHeaderTimeout: time.Second * time.Duration(respTimeout), 45 | }, 46 | } 47 | request, _ := http.NewRequest("GET", url, nil) 48 | request.Header.Set("Content-Type", "application/json") 49 | request.SetBasicAuth(user, password) 50 | response, err := client.Do(request) 51 | if err != nil { 52 | return []byte(""), fmt.Errorf("call rabbit api fail") 53 | } 54 | 55 | defer response.Body.Close() 56 | 57 | resultCode := response.StatusCode 58 | switch resultCode { 59 | case http.StatusOK: 60 | body, _ := ioutil.ReadAll(response.Body) 61 | return body, nil 62 | case http.StatusUnauthorized: 63 | return []byte(""), fmt.Errorf("call rabbitmq rest api auth fail") 64 | default: 65 | return []byte(""), fmt.Errorf("unknown error %d", resultCode) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /g/var.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import ( 4 | "github.com/barryz/rmqmonitor/utils" 5 | ) 6 | 7 | // StatsDB stats management database 8 | type StatsDB struct { 9 | CurrentLocate string `json:"current_locate"` 10 | PreviousLocate string `json:"previous_locate"` 11 | LastChangeTime string `json:"last_change"` 12 | LastCollectTime string `json:"last_collect"` 13 | } 14 | 15 | // NewStatsDB create an new stats management database cache 16 | func NewStatsDB() *StatsDB { 17 | return &StatsDB{} 18 | } 19 | 20 | // SetCurrentLocate setting current database location 21 | func (s *StatsDB) SetCurrentLocate(locate string) { 22 | if s.CurrentLocate != locate { 23 | s.PreviousLocate = s.CurrentLocate 24 | s.CurrentLocate = locate 25 | s.LastChangeTime = utils.GetCurrentDateTime() 26 | } else { 27 | // do nothing 28 | } 29 | 30 | s.LastCollectTime = utils.GetCurrentDateTime() 31 | 32 | } 33 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | "time" 11 | 12 | "github.com/barryz/rmqmonitor/cron" 13 | "github.com/barryz/rmqmonitor/falcon" 14 | "github.com/barryz/rmqmonitor/g" 15 | v "github.com/barryz/rmqmonitor/version" 16 | "github.com/barryz/rmqmonitor/witch" 17 | ) 18 | 19 | func collect() { 20 | go metricCollector(g.Config().Interval) 21 | } 22 | 23 | func rotateQLog() { 24 | go cron.Start() 25 | } 26 | 27 | func witchLaunch() { 28 | go witch.Launch() 29 | } 30 | 31 | func metricCollector(sec int64) { 32 | t := time.NewTicker(time.Second * time.Duration(sec)).C 33 | for { 34 | <-t 35 | falcon.Collector() 36 | } 37 | } 38 | 39 | func main() { 40 | cfg := flag.String("c", "cfg.json", "configuration file") 41 | ver := flag.Bool("v", false, "show agent version") 42 | 43 | flag.Parse() 44 | 45 | if *ver { 46 | fmt.Println(v.Build) 47 | os.Exit(0) 48 | } 49 | 50 | g.ParseConfig(*cfg) 51 | 52 | if g.Config().Enabled.Collect { 53 | collect() 54 | } 55 | 56 | if g.Config().Enabled.LogRotate { 57 | rotateQLog() 58 | } 59 | 60 | if g.Config().Enabled.Witch { 61 | witchLaunch() 62 | } 63 | 64 | signals := make(chan os.Signal, 1) 65 | signal.Notify(signals, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT) 66 | go func() { 67 | sig := <-signals 68 | log.Printf("Signal %v captured", sig) 69 | os.Exit(0) 70 | }() 71 | 72 | select {} 73 | } 74 | -------------------------------------------------------------------------------- /utils/date.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // GetYesterdayDate get the date of yesterday. 9 | func GetYesterdayDate() string { 10 | currentTs := time.Now() 11 | date2Yesterday := currentTs.AddDate(0, 0, -1) 12 | return fmt.Sprintf("%s", date2Yesterday.Format("2006-01-02")) 13 | } 14 | 15 | // GetCurrentDateTime get the current time 16 | func GetCurrentDateTime() string { 17 | currentTs := time.Now() 18 | return fmt.Sprintf("%s", currentTs.Format("2006-01-02 15:04:05")) 19 | } 20 | -------------------------------------------------------------------------------- /vendor/github.com/barryz/cron/.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/barryz/cron/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /vendor/github.com/barryz/cron/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 Rob Figueiredo 2 | All Rights Reserved. 3 | 4 | MIT LICENSE 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the "Software"), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, 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 in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/barryz/cron/README.md: -------------------------------------------------------------------------------- 1 | # cron 2 | crontab for golang 3 | 4 | ## peference 5 | ```bash 6 | TODO 7 | ``` 8 | 9 | ## usage 10 | ```go 11 | import ( 12 | "fmt" 13 | "time" 14 | 15 | ncron "github.com/niean/cron" 16 | ) 17 | 18 | func main() { 19 | // init cron 20 | c := ncron.New() 21 | 22 | // add cron job 23 | c.AddFunc("* * * * * *", func() { fmt.Println("Every second") }) 24 | c.AddFuncCC("* * * * * *", func() { fmt.Println("Every second, with max Concurrrent 2"); time.Sleep(10 * time.Second)}, 2) 25 | 26 | // start cron 27 | c.Start() 28 | 29 | // keep alive 30 | select {} 31 | } 32 | ``` 33 | 34 | ## reference 35 | 36 | -------------------------------------------------------------------------------- /vendor/github.com/barryz/cron/constantdelay.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import "time" 4 | 5 | // ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes". 6 | // It does not support jobs more frequent than once a second. 7 | type ConstantDelaySchedule struct { 8 | Delay time.Duration 9 | } 10 | 11 | // Every returns a crontab Schedule that activates once every duration. 12 | // Delays of less than a second are not supported (will round up to 1 second). 13 | // Any fields less than a Second are truncated. 14 | func Every(duration time.Duration) ConstantDelaySchedule { 15 | if duration < time.Second { 16 | duration = time.Second 17 | } 18 | return ConstantDelaySchedule{ 19 | Delay: duration - time.Duration(duration.Nanoseconds())%time.Second, 20 | } 21 | } 22 | 23 | // Next returns the next time this should be run. 24 | // This rounds so that the next activation time will be on the second. 25 | func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time { 26 | return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond) 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/barryz/cron/cron.go: -------------------------------------------------------------------------------- 1 | // This library implements a cron spec parser and runner. See the README for 2 | // more details. 3 | package cron 4 | 5 | import ( 6 | "sort" 7 | "time" 8 | ) 9 | 10 | // Cron keeps track of any number of entries, invoking the associated func as 11 | // specified by the schedule. It may be started, stopped, and the entries may 12 | // be inspected while running. 13 | type Cron struct { 14 | entries []*Entry 15 | stop chan struct{} 16 | add chan *Entry 17 | snapshot chan []*Entry 18 | running bool 19 | } 20 | 21 | // Job is an interface for submitted cron jobs. 22 | type Job interface { 23 | Run() 24 | } 25 | 26 | // The Schedule describes a job's duty cycle. 27 | type Schedule interface { 28 | // Return the next activation time, later than the given time. 29 | // Next is invoked initially, and then each time the job is run. 30 | Next(time.Time) time.Time 31 | } 32 | 33 | // Concurent control 34 | type SemaOfCron struct { 35 | Ctrl bool 36 | Sema *Semaphore 37 | } 38 | 39 | // Entry consists of a schedule and the func to execute on that schedule. 40 | type Entry struct { 41 | // The schedule on which this job should be run. 42 | Schedule Schedule 43 | 44 | // The next time the job will run. This is the zero time if Cron has not been 45 | // started or this entry's schedule is unsatisfiable 46 | Next time.Time 47 | 48 | // The last time this job was run. This is the zero time if the job has never 49 | // been run. 50 | Prev time.Time 51 | 52 | // The Job to run. 53 | Job Job 54 | 55 | // Conturrent 56 | Sema *SemaOfCron 57 | } 58 | 59 | // byTime is a wrapper for sorting the entry array by time 60 | // (with zero time at the end). 61 | type byTime []*Entry 62 | 63 | func (s byTime) Len() int { return len(s) } 64 | func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 65 | func (s byTime) Less(i, j int) bool { 66 | // Two zero times should return false. 67 | // Otherwise, zero is "greater" than any other time. 68 | // (To sort it at the end of the list.) 69 | if s[i].Next.IsZero() { 70 | return false 71 | } 72 | if s[j].Next.IsZero() { 73 | return true 74 | } 75 | return s[i].Next.Before(s[j].Next) 76 | } 77 | 78 | // New returns a new Cron job runner. 79 | func New() *Cron { 80 | return &Cron{ 81 | entries: nil, 82 | add: make(chan *Entry), 83 | stop: make(chan struct{}), 84 | snapshot: make(chan []*Entry), 85 | running: false, 86 | } 87 | } 88 | 89 | // A wrapper that turns a func() into a cron.Job 90 | type FuncJob func() 91 | 92 | func (f FuncJob) Run() { f() } 93 | 94 | // AddFunc adds a func to the Cron to be run on the given schedule. 95 | func (c *Cron) AddFunc(spec string, cmd func()) error { 96 | return c.AddJob(spec, FuncJob(cmd)) 97 | } 98 | 99 | // AddFunc adds a Job to the Cron to be run on the given schedule. 100 | func (c *Cron) AddJob(spec string, cmd Job) error { 101 | schedule, err := Parse(spec) 102 | if err != nil { 103 | return err 104 | } 105 | c.Schedule(schedule, cmd) 106 | return nil 107 | } 108 | 109 | // Schedule adds a Job to the Cron to be run on the given schedule. 110 | func (c *Cron) Schedule(schedule Schedule, cmd Job) { 111 | entry := &Entry{ 112 | Schedule: schedule, 113 | Job: cmd, 114 | Sema: &SemaOfCron{Ctrl: false}, 115 | } 116 | if !c.running { 117 | c.entries = append(c.entries, entry) 118 | return 119 | } 120 | 121 | c.add <- entry 122 | } 123 | 124 | // AddFunc adds a func to the Cron to be run on the given schedule. 125 | func (c *Cron) AddFuncCC(spec string, cmd func(), cc int) error { 126 | return c.AddJobCC(spec, FuncJob(cmd), cc) 127 | } 128 | 129 | // AddFunc adds a Job to the Cron to be run on the given schedule. 130 | func (c *Cron) AddJobCC(spec string, cmd Job, cc int) error { 131 | schedule, err := Parse(spec) 132 | if err != nil { 133 | return err 134 | } 135 | c.ScheduleCC(schedule, cmd, &SemaOfCron{Ctrl: true, Sema: NewSemaphore(cc)}) 136 | return nil 137 | } 138 | 139 | // Schedule with Concurrent Control 140 | func (c *Cron) ScheduleCC(schedule Schedule, cmd Job, sema *SemaOfCron) { 141 | entry := &Entry{ 142 | Schedule: schedule, 143 | Job: cmd, 144 | Sema: sema, 145 | } 146 | if !c.running { 147 | c.entries = append(c.entries, entry) 148 | return 149 | } 150 | 151 | c.add <- entry 152 | } 153 | 154 | // Entries returns a snapshot of the cron entries. 155 | func (c *Cron) Entries() []*Entry { 156 | if c.running { 157 | c.snapshot <- nil 158 | x := <-c.snapshot 159 | return x 160 | } 161 | return c.entrySnapshot() 162 | } 163 | 164 | // Start the cron scheduler in its own go-routine. 165 | func (c *Cron) Start() { 166 | c.running = true 167 | go c.run() 168 | } 169 | 170 | // Run the scheduler.. this is private just due to the need to synchronize 171 | // access to the 'running' state variable. 172 | func (c *Cron) run() { 173 | // Figure out the next activation times for each entry. 174 | now := time.Now().Local() 175 | for _, entry := range c.entries { 176 | entry.Next = entry.Schedule.Next(now) 177 | } 178 | 179 | for { 180 | // Determine the next entry to run. 181 | sort.Sort(byTime(c.entries)) 182 | 183 | var effective time.Time 184 | if len(c.entries) == 0 || c.entries[0].Next.IsZero() { 185 | // If there are no entries yet, just sleep - it still handles new entries 186 | // and stop requests. 187 | effective = now.AddDate(10, 0, 0) 188 | } else { 189 | effective = c.entries[0].Next 190 | } 191 | 192 | select { 193 | case now = <-time.After(effective.Sub(now)): 194 | // Run every entry whose next time was this effective time. 195 | for _, e := range c.entries { 196 | if e.Next != effective { 197 | break 198 | } 199 | 200 | // Do its Job 201 | if e.Sema.Ctrl { 202 | go func() { 203 | // Concurrent Control 204 | if !e.Sema.Sema.TryAcquire() { 205 | return 206 | } 207 | defer e.Sema.Sema.Release() 208 | 209 | e.Job.Run() 210 | }() 211 | } else { 212 | go e.Job.Run() 213 | } 214 | 215 | e.Prev = e.Next 216 | e.Next = e.Schedule.Next(effective) 217 | } 218 | continue 219 | 220 | case newEntry := <-c.add: 221 | c.entries = append(c.entries, newEntry) 222 | newEntry.Next = newEntry.Schedule.Next(now) 223 | 224 | case <-c.snapshot: 225 | c.snapshot <- c.entrySnapshot() 226 | 227 | case <-c.stop: 228 | return 229 | } 230 | 231 | // 'now' should be updated after newEntry and snapshot cases. 232 | now = time.Now().Local() 233 | } 234 | } 235 | 236 | // Stop the cron scheduler. 237 | func (c *Cron) Stop() { 238 | c.stop <- struct{}{} 239 | c.running = false 240 | } 241 | 242 | // entrySnapshot returns a copy of the current cron entry list. 243 | func (c *Cron) entrySnapshot() []*Entry { 244 | entries := []*Entry{} 245 | for _, e := range c.entries { 246 | entries = append(entries, &Entry{ 247 | Schedule: e.Schedule, 248 | Next: e.Next, 249 | Prev: e.Prev, 250 | Job: e.Job, 251 | }) 252 | } 253 | return entries 254 | } 255 | -------------------------------------------------------------------------------- /vendor/github.com/barryz/cron/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package cron implements a cron spec parser and job runner. 3 | 4 | Usage 5 | 6 | Callers may register Funcs to be invoked on a given schedule. Cron will run 7 | them in their own goroutines. 8 | 9 | c := cron.New() 10 | c.AddFunc("0 30 * * * *", func() { fmt.Println("Every hour on the half hour") }) 11 | c.AddFunc("@hourly", func() { fmt.Println("Every hour") }) 12 | c.AddFunc("@every 1h30m", func() { fmt.Println("Every hour thirty") }) 13 | c.Start() 14 | .. 15 | // Funcs are invoked in their own goroutine, asynchronously. 16 | ... 17 | // Funcs may also be added to a running Cron 18 | c.AddFunc("@daily", func() { fmt.Println("Every day") }) 19 | .. 20 | // Inspect the cron job entries' next and previous run times. 21 | inspect(c.Entries()) 22 | .. 23 | c.Stop() // Stop the scheduler (does not stop any jobs already running). 24 | 25 | CRON Expression Format 26 | 27 | A cron expression represents a set of times, using 6 space-separated fields. 28 | 29 | Field name | Mandatory? | Allowed values | Allowed special characters 30 | ---------- | ---------- | -------------- | -------------------------- 31 | Seconds | Yes | 0-59 | * / , - 32 | Minutes | Yes | 0-59 | * / , - 33 | Hours | Yes | 0-23 | * / , - 34 | Day of month | Yes | 1-31 | * / , - ? 35 | Month | Yes | 1-12 or JAN-DEC | * / , - 36 | Day of week | Yes | 0-6 or SUN-SAT | * / , - ? 37 | 38 | Note: Month and Day-of-week field values are case insensitive. "SUN", "Sun", 39 | and "sun" are equally accepted. 40 | 41 | Special Characters 42 | 43 | Asterisk ( * ) 44 | 45 | The asterisk indicates that the cron expression will match for all values of the 46 | field; e.g., using an asterisk in the 5th field (month) would indicate every 47 | month. 48 | 49 | Slash ( / ) 50 | 51 | Slashes are used to describe increments of ranges. For example 3-59/15 in the 52 | 1st field (minutes) would indicate the 3rd minute of the hour and every 15 53 | minutes thereafter. The form "*\/..." is equivalent to the form "first-last/...", 54 | that is, an increment over the largest possible range of the field. The form 55 | "N/..." is accepted as meaning "N-MAX/...", that is, starting at N, use the 56 | increment until the end of that specific range. It does not wrap around. 57 | 58 | Comma ( , ) 59 | 60 | Commas are used to separate items of a list. For example, using "MON,WED,FRI" in 61 | the 5th field (day of week) would mean Mondays, Wednesdays and Fridays. 62 | 63 | Hyphen ( - ) 64 | 65 | Hyphens are used to define ranges. For example, 9-17 would indicate every 66 | hour between 9am and 5pm inclusive. 67 | 68 | Question mark ( ? ) 69 | 70 | Question mark may be used instead of '*' for leaving either day-of-month or 71 | day-of-week blank. 72 | 73 | Predefined schedules 74 | 75 | You may use one of several pre-defined schedules in place of a cron expression. 76 | 77 | Entry | Description | Equivalent To 78 | ----- | ----------- | ------------- 79 | @yearly (or @annually) | Run once a year, midnight, Jan. 1st | 0 0 0 1 1 * 80 | @monthly | Run once a month, midnight, first of month | 0 0 0 1 * * 81 | @weekly | Run once a week, midnight on Sunday | 0 0 0 * * 0 82 | @daily (or @midnight) | Run once a day, midnight | 0 0 0 * * * 83 | @hourly | Run once an hour, beginning of hour | 0 0 * * * * 84 | 85 | Intervals 86 | 87 | You may also schedule a job to execute at fixed intervals. This is supported by 88 | formatting the cron spec like this: 89 | 90 | @every 91 | 92 | where "duration" is a string accepted by time.ParseDuration 93 | (http://golang.org/pkg/time/#ParseDuration). 94 | 95 | For example, "@every 1h30m10s" would indicate a schedule that activates every 96 | 1 hour, 30 minutes, 10 seconds. 97 | 98 | Note: The interval does not take the job runtime into account. For example, 99 | if a job takes 3 minutes to run, and it is scheduled to run every 5 minutes, 100 | it will have only 2 minutes of idle time between each run. 101 | 102 | Time zones 103 | 104 | All interpretation and scheduling is done in the machine's local time zone (as 105 | provided by the Go time package (http://www.golang.org/pkg/time). 106 | 107 | Be aware that jobs scheduled during daylight-savings leap-ahead transitions will 108 | not be run! 109 | 110 | Thread safety 111 | 112 | Since the Cron service runs concurrently with the calling code, some amount of 113 | care must be taken to ensure proper synchronization. 114 | 115 | All cron methods are designed to be correctly synchronized as long as the caller 116 | ensures that invocations have a clear happens-before ordering between them. 117 | 118 | Implementation 119 | 120 | Cron entries are stored in an array, sorted by their next activation time. Cron 121 | sleeps until the next job is due to be run. 122 | 123 | Upon waking: 124 | - it runs each entry that is active on that second 125 | - it calculates the next run times for the jobs that were run 126 | - it re-sorts the array of entries by next activation time. 127 | - it goes to sleep until the soonest job. 128 | */ 129 | package cron 130 | -------------------------------------------------------------------------------- /vendor/github.com/barryz/cron/parser.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | // Parse returns a new crontab schedule representing the given spec. 13 | // It returns a descriptive error if the spec is not valid. 14 | // 15 | // It accepts 16 | // - Full crontab specs, e.g. "* * * * * ?" 17 | // - Descriptors, e.g. "@midnight", "@every 1h30m" 18 | func Parse(spec string) (_ Schedule, err error) { 19 | // Convert panics into errors 20 | defer func() { 21 | if recovered := recover(); recovered != nil { 22 | err = fmt.Errorf("%v", recovered) 23 | } 24 | }() 25 | 26 | if spec[0] == '@' { 27 | return parseDescriptor(spec), nil 28 | } 29 | 30 | // Split on whitespace. We require 5 or 6 fields. 31 | // (second) (minute) (hour) (day of month) (month) (day of week, optional) 32 | fields := strings.Fields(spec) 33 | if len(fields) != 5 && len(fields) != 6 { 34 | log.Panicf("Expected 5 or 6 fields, found %d: %s", len(fields), spec) 35 | } 36 | 37 | // If a sixth field is not provided (DayOfWeek), then it is equivalent to star. 38 | if len(fields) == 5 { 39 | fields = append(fields, "*") 40 | } 41 | 42 | schedule := &SpecSchedule{ 43 | Second: getField(fields[0], seconds), 44 | Minute: getField(fields[1], minutes), 45 | Hour: getField(fields[2], hours), 46 | Dom: getField(fields[3], dom), 47 | Month: getField(fields[4], months), 48 | Dow: getField(fields[5], dow), 49 | } 50 | 51 | return schedule, nil 52 | } 53 | 54 | // getField returns an Int with the bits set representing all of the times that 55 | // the field represents. A "field" is a comma-separated list of "ranges". 56 | func getField(field string, r bounds) uint64 { 57 | // list = range {"," range} 58 | var bits uint64 59 | ranges := strings.FieldsFunc(field, func(r rune) bool { return r == ',' }) 60 | for _, expr := range ranges { 61 | bits |= getRange(expr, r) 62 | } 63 | return bits 64 | } 65 | 66 | // getRange returns the bits indicated by the given expression: 67 | // number | number "-" number [ "/" number ] 68 | func getRange(expr string, r bounds) uint64 { 69 | 70 | var ( 71 | start, end, step uint 72 | rangeAndStep = strings.Split(expr, "/") 73 | lowAndHigh = strings.Split(rangeAndStep[0], "-") 74 | singleDigit = len(lowAndHigh) == 1 75 | ) 76 | 77 | var extra_star uint64 78 | if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" { 79 | start = r.min 80 | end = r.max 81 | extra_star = starBit 82 | } else { 83 | start = parseIntOrName(lowAndHigh[0], r.names) 84 | switch len(lowAndHigh) { 85 | case 1: 86 | end = start 87 | case 2: 88 | end = parseIntOrName(lowAndHigh[1], r.names) 89 | default: 90 | log.Panicf("Too many hyphens: %s", expr) 91 | } 92 | } 93 | 94 | switch len(rangeAndStep) { 95 | case 1: 96 | step = 1 97 | case 2: 98 | step = mustParseInt(rangeAndStep[1]) 99 | 100 | // Special handling: "N/step" means "N-max/step". 101 | if singleDigit { 102 | end = r.max 103 | } 104 | default: 105 | log.Panicf("Too many slashes: %s", expr) 106 | } 107 | 108 | if start < r.min { 109 | log.Panicf("Beginning of range (%d) below minimum (%d): %s", start, r.min, expr) 110 | } 111 | if end > r.max { 112 | log.Panicf("End of range (%d) above maximum (%d): %s", end, r.max, expr) 113 | } 114 | if start > end { 115 | log.Panicf("Beginning of range (%d) beyond end of range (%d): %s", start, end, expr) 116 | } 117 | 118 | return getBits(start, end, step) | extra_star 119 | } 120 | 121 | // parseIntOrName returns the (possibly-named) integer contained in expr. 122 | func parseIntOrName(expr string, names map[string]uint) uint { 123 | if names != nil { 124 | if namedInt, ok := names[strings.ToLower(expr)]; ok { 125 | return namedInt 126 | } 127 | } 128 | return mustParseInt(expr) 129 | } 130 | 131 | // mustParseInt parses the given expression as an int or panics. 132 | func mustParseInt(expr string) uint { 133 | num, err := strconv.Atoi(expr) 134 | if err != nil { 135 | log.Panicf("Failed to parse int from %s: %s", expr, err) 136 | } 137 | if num < 0 { 138 | log.Panicf("Negative number (%d) not allowed: %s", num, expr) 139 | } 140 | 141 | return uint(num) 142 | } 143 | 144 | // getBits sets all bits in the range [min, max], modulo the given step size. 145 | func getBits(min, max, step uint) uint64 { 146 | var bits uint64 147 | 148 | // If step is 1, use shifts. 149 | if step == 1 { 150 | return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min) 151 | } 152 | 153 | // Else, use a simple loop. 154 | for i := min; i <= max; i += step { 155 | bits |= 1 << i 156 | } 157 | return bits 158 | } 159 | 160 | // all returns all bits within the given bounds. (plus the star bit) 161 | func all(r bounds) uint64 { 162 | return getBits(r.min, r.max, 1) | starBit 163 | } 164 | 165 | // parseDescriptor returns a pre-defined schedule for the expression, or panics 166 | // if none matches. 167 | func parseDescriptor(spec string) Schedule { 168 | switch spec { 169 | case "@yearly", "@annually": 170 | return &SpecSchedule{ 171 | Second: 1 << seconds.min, 172 | Minute: 1 << minutes.min, 173 | Hour: 1 << hours.min, 174 | Dom: 1 << dom.min, 175 | Month: 1 << months.min, 176 | Dow: all(dow), 177 | } 178 | 179 | case "@monthly": 180 | return &SpecSchedule{ 181 | Second: 1 << seconds.min, 182 | Minute: 1 << minutes.min, 183 | Hour: 1 << hours.min, 184 | Dom: 1 << dom.min, 185 | Month: all(months), 186 | Dow: all(dow), 187 | } 188 | 189 | case "@weekly": 190 | return &SpecSchedule{ 191 | Second: 1 << seconds.min, 192 | Minute: 1 << minutes.min, 193 | Hour: 1 << hours.min, 194 | Dom: all(dom), 195 | Month: all(months), 196 | Dow: 1 << dow.min, 197 | } 198 | 199 | case "@daily", "@midnight": 200 | return &SpecSchedule{ 201 | Second: 1 << seconds.min, 202 | Minute: 1 << minutes.min, 203 | Hour: 1 << hours.min, 204 | Dom: all(dom), 205 | Month: all(months), 206 | Dow: all(dow), 207 | } 208 | 209 | case "@hourly": 210 | return &SpecSchedule{ 211 | Second: 1 << seconds.min, 212 | Minute: 1 << minutes.min, 213 | Hour: all(hours), 214 | Dom: all(dom), 215 | Month: all(months), 216 | Dow: all(dow), 217 | } 218 | } 219 | 220 | const every = "@every " 221 | if strings.HasPrefix(spec, every) { 222 | duration, err := time.ParseDuration(spec[len(every):]) 223 | if err != nil { 224 | log.Panicf("Failed to parse duration %s: %s", spec, err) 225 | } 226 | return Every(duration) 227 | } 228 | 229 | log.Panicf("Unrecognized descriptor: %s", spec) 230 | return nil 231 | } 232 | -------------------------------------------------------------------------------- /vendor/github.com/barryz/cron/semaphore.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import () 4 | 5 | type Semaphore struct { 6 | bufSize int 7 | channel chan int8 8 | } 9 | 10 | func NewSemaphore(concurrencyNum int) *Semaphore { 11 | return &Semaphore{channel: make(chan int8, concurrencyNum), bufSize: concurrencyNum} 12 | } 13 | 14 | func (this *Semaphore) TryAcquire() bool { 15 | select { 16 | case this.channel <- int8(0): 17 | return true 18 | default: 19 | return false 20 | } 21 | } 22 | 23 | func (this *Semaphore) Acquire() { 24 | this.channel <- int8(0) 25 | } 26 | 27 | func (this *Semaphore) Release() { 28 | <-this.channel 29 | } 30 | 31 | func (this *Semaphore) AvailablePermits() int { 32 | return this.bufSize - len(this.channel) 33 | } 34 | -------------------------------------------------------------------------------- /vendor/github.com/barryz/cron/spec.go: -------------------------------------------------------------------------------- 1 | package cron 2 | 3 | import "time" 4 | 5 | // SpecSchedule specifies a duty cycle (to the second granularity), based on a 6 | // traditional crontab specification. It is computed initially and stored as bit sets. 7 | type SpecSchedule struct { 8 | Second, Minute, Hour, Dom, Month, Dow uint64 9 | } 10 | 11 | // bounds provides a range of acceptable values (plus a map of name to value). 12 | type bounds struct { 13 | min, max uint 14 | names map[string]uint 15 | } 16 | 17 | // The bounds for each field. 18 | var ( 19 | seconds = bounds{0, 59, nil} 20 | minutes = bounds{0, 59, nil} 21 | hours = bounds{0, 23, nil} 22 | dom = bounds{1, 31, nil} 23 | months = bounds{1, 12, map[string]uint{ 24 | "jan": 1, 25 | "feb": 2, 26 | "mar": 3, 27 | "apr": 4, 28 | "may": 5, 29 | "jun": 6, 30 | "jul": 7, 31 | "aug": 8, 32 | "sep": 9, 33 | "oct": 10, 34 | "nov": 11, 35 | "dec": 12, 36 | }} 37 | dow = bounds{0, 6, map[string]uint{ 38 | "sun": 0, 39 | "mon": 1, 40 | "tue": 2, 41 | "wed": 3, 42 | "thu": 4, 43 | "fri": 5, 44 | "sat": 6, 45 | }} 46 | ) 47 | 48 | const ( 49 | // Set the top bit if a star was included in the expression. 50 | starBit = 1 << 63 51 | ) 52 | 53 | // Next returns the next time this schedule is activated, greater than the given 54 | // time. If no time can be found to satisfy the schedule, return the zero time. 55 | func (s *SpecSchedule) Next(t time.Time) time.Time { 56 | // General approach: 57 | // For Month, Day, Hour, Minute, Second: 58 | // Check if the time value matches. If yes, continue to the next field. 59 | // If the field doesn't match the schedule, then increment the field until it matches. 60 | // While incrementing the field, a wrap-around brings it back to the beginning 61 | // of the field list (since it is necessary to re-verify previous field 62 | // values) 63 | 64 | // Start at the earliest possible time (the upcoming second). 65 | t = t.Add(1*time.Second - time.Duration(t.Nanosecond())*time.Nanosecond) 66 | 67 | // This flag indicates whether a field has been incremented. 68 | added := false 69 | 70 | // If no time is found within five years, return zero. 71 | yearLimit := t.Year() + 5 72 | 73 | WRAP: 74 | if t.Year() > yearLimit { 75 | return time.Time{} 76 | } 77 | 78 | // Find the first applicable month. 79 | // If it's this month, then do nothing. 80 | for 1< 0 152 | dowMatch bool = 1< 0 153 | ) 154 | 155 | if s.Dom&starBit > 0 || s.Dow&starBit > 0 { 156 | return domMatch && dowMatch 157 | } 158 | return domMatch || dowMatch 159 | } 160 | -------------------------------------------------------------------------------- /vendor/github.com/braintree/manners/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Braintree, a division of PayPal, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all 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 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/braintree/manners/README.md: -------------------------------------------------------------------------------- 1 | # Manners 2 | 3 | A *polite* webserver for Go. 4 | 5 | Manners allows you to shut your Go webserver down gracefully, without dropping any requests. It can act as a drop-in replacement for the standard library's http.ListenAndServe function: 6 | 7 | ```go 8 | func main() { 9 | handler := MyHTTPHandler() 10 | manners.ListenAndServe(":7000", handler) 11 | } 12 | ``` 13 | 14 | Then, when you want to shut the server down: 15 | 16 | ```go 17 | manners.Close() 18 | ``` 19 | 20 | (Note that this does not block until all the requests are finished. Rather, the call to manners.ListenAndServe will stop blocking when all the requests are finished.) 21 | 22 | Manners ensures that all requests are served by incrementing a WaitGroup when a request comes in and decrementing it when the request finishes. 23 | 24 | If your request handler spawns Goroutines that are not guaranteed to finish with the request, you can ensure they are also completed with the `StartRoutine` and `FinishRoutine` functions on the server. 25 | 26 | ### Known Issues 27 | 28 | Manners does not correctly shut down long-lived keepalive connections when issued a shutdown command. Clients on an idle keepalive connection may see a connection reset error rather than a close. See https://github.com/braintree/manners/issues/13 for details. 29 | 30 | ### Compatability 31 | 32 | Manners 0.3.0 and above uses standard library functionality introduced in Go 1.3. 33 | 34 | ### Installation 35 | 36 | `go get github.com/braintree/manners` 37 | -------------------------------------------------------------------------------- /vendor/github.com/braintree/manners/interfaces.go: -------------------------------------------------------------------------------- 1 | package manners 2 | 3 | type waitGroup interface { 4 | Add(int) 5 | Done() 6 | Wait() 7 | } 8 | -------------------------------------------------------------------------------- /vendor/github.com/braintree/manners/static.go: -------------------------------------------------------------------------------- 1 | package manners 2 | 3 | import ( 4 | "net" 5 | "net/http" 6 | "sync" 7 | ) 8 | 9 | var ( 10 | defaultServer *GracefulServer 11 | defaultServerLock = &sync.Mutex{} 12 | ) 13 | 14 | func init() { 15 | defaultServerLock.Lock() 16 | } 17 | 18 | // ListenAndServe provides a graceful version of the function provided by the 19 | // net/http package. Call Close() to stop the server. 20 | func ListenAndServe(addr string, handler http.Handler) error { 21 | defaultServer = NewWithServer(&http.Server{Addr: addr, Handler: handler}) 22 | defaultServerLock.Unlock() 23 | return defaultServer.ListenAndServe() 24 | } 25 | 26 | // ListenAndServeTLS provides a graceful version of the function provided by the 27 | // net/http package. Call Close() to stop the server. 28 | func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error { 29 | defaultServer = NewWithServer(&http.Server{Addr: addr, Handler: handler}) 30 | defaultServerLock.Unlock() 31 | return defaultServer.ListenAndServeTLS(certFile, keyFile) 32 | } 33 | 34 | // Serve provides a graceful version of the function provided by the net/http 35 | // package. Call Close() to stop the server. 36 | func Serve(l net.Listener, handler http.Handler) error { 37 | defaultServer = NewWithServer(&http.Server{Handler: handler}) 38 | defaultServerLock.Unlock() 39 | return defaultServer.Serve(l) 40 | } 41 | 42 | // Shuts down the default server used by ListenAndServe, ListenAndServeTLS and 43 | // Serve. It returns true if it's the first time Close is called. 44 | func Close() bool { 45 | defaultServerLock.Lock() 46 | return defaultServer.Close() 47 | } 48 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/.gitignore: -------------------------------------------------------------------------------- 1 | inject 2 | inject.test 3 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/README.md: -------------------------------------------------------------------------------- 1 | # inject 2 | -- 3 | import "github.com/codegangsta/inject" 4 | 5 | Package inject provides utilities for mapping and injecting dependencies in 6 | various ways. 7 | 8 | Language Translations: 9 | * [简体中文](translations/README_zh_cn.md) 10 | 11 | ## Usage 12 | 13 | #### func InterfaceOf 14 | 15 | ```go 16 | func InterfaceOf(value interface{}) reflect.Type 17 | ``` 18 | InterfaceOf dereferences a pointer to an Interface type. It panics if value is 19 | not an pointer to an interface. 20 | 21 | #### type Applicator 22 | 23 | ```go 24 | type Applicator interface { 25 | // Maps dependencies in the Type map to each field in the struct 26 | // that is tagged with 'inject'. Returns an error if the injection 27 | // fails. 28 | Apply(interface{}) error 29 | } 30 | ``` 31 | 32 | Applicator represents an interface for mapping dependencies to a struct. 33 | 34 | #### type Injector 35 | 36 | ```go 37 | type Injector interface { 38 | Applicator 39 | Invoker 40 | TypeMapper 41 | // SetParent sets the parent of the injector. If the injector cannot find a 42 | // dependency in its Type map it will check its parent before returning an 43 | // error. 44 | SetParent(Injector) 45 | } 46 | ``` 47 | 48 | Injector represents an interface for mapping and injecting dependencies into 49 | structs and function arguments. 50 | 51 | #### func New 52 | 53 | ```go 54 | func New() Injector 55 | ``` 56 | New returns a new Injector. 57 | 58 | #### type Invoker 59 | 60 | ```go 61 | type Invoker interface { 62 | // Invoke attempts to call the interface{} provided as a function, 63 | // providing dependencies for function arguments based on Type. Returns 64 | // a slice of reflect.Value representing the returned values of the function. 65 | // Returns an error if the injection fails. 66 | Invoke(interface{}) ([]reflect.Value, error) 67 | } 68 | ``` 69 | 70 | Invoker represents an interface for calling functions via reflection. 71 | 72 | #### type TypeMapper 73 | 74 | ```go 75 | type TypeMapper interface { 76 | // Maps the interface{} value based on its immediate type from reflect.TypeOf. 77 | Map(interface{}) TypeMapper 78 | // Maps the interface{} value based on the pointer of an Interface provided. 79 | // This is really only useful for mapping a value as an interface, as interfaces 80 | // cannot at this time be referenced directly without a pointer. 81 | MapTo(interface{}, interface{}) TypeMapper 82 | // Provides a possibility to directly insert a mapping based on type and value. 83 | // This makes it possible to directly map type arguments not possible to instantiate 84 | // with reflect like unidirectional channels. 85 | Set(reflect.Type, reflect.Value) TypeMapper 86 | // Returns the Value that is mapped to the current type. Returns a zeroed Value if 87 | // the Type has not been mapped. 88 | Get(reflect.Type) reflect.Value 89 | } 90 | ``` 91 | 92 | TypeMapper represents an interface for mapping interface{} values based on type. 93 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/inject.go: -------------------------------------------------------------------------------- 1 | // Package inject provides utilities for mapping and injecting dependencies in various ways. 2 | package inject 3 | 4 | import ( 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | // Injector represents an interface for mapping and injecting dependencies into structs 10 | // and function arguments. 11 | type Injector interface { 12 | Applicator 13 | Invoker 14 | TypeMapper 15 | // SetParent sets the parent of the injector. If the injector cannot find a 16 | // dependency in its Type map it will check its parent before returning an 17 | // error. 18 | SetParent(Injector) 19 | } 20 | 21 | // Applicator represents an interface for mapping dependencies to a struct. 22 | type Applicator interface { 23 | // Maps dependencies in the Type map to each field in the struct 24 | // that is tagged with 'inject'. Returns an error if the injection 25 | // fails. 26 | Apply(interface{}) error 27 | } 28 | 29 | // Invoker represents an interface for calling functions via reflection. 30 | type Invoker interface { 31 | // Invoke attempts to call the interface{} provided as a function, 32 | // providing dependencies for function arguments based on Type. Returns 33 | // a slice of reflect.Value representing the returned values of the function. 34 | // Returns an error if the injection fails. 35 | Invoke(interface{}) ([]reflect.Value, error) 36 | } 37 | 38 | // TypeMapper represents an interface for mapping interface{} values based on type. 39 | type TypeMapper interface { 40 | // Maps the interface{} value based on its immediate type from reflect.TypeOf. 41 | Map(interface{}) TypeMapper 42 | // Maps the interface{} value based on the pointer of an Interface provided. 43 | // This is really only useful for mapping a value as an interface, as interfaces 44 | // cannot at this time be referenced directly without a pointer. 45 | MapTo(interface{}, interface{}) TypeMapper 46 | // Provides a possibility to directly insert a mapping based on type and value. 47 | // This makes it possible to directly map type arguments not possible to instantiate 48 | // with reflect like unidirectional channels. 49 | Set(reflect.Type, reflect.Value) TypeMapper 50 | // Returns the Value that is mapped to the current type. Returns a zeroed Value if 51 | // the Type has not been mapped. 52 | Get(reflect.Type) reflect.Value 53 | } 54 | 55 | type injector struct { 56 | values map[reflect.Type]reflect.Value 57 | parent Injector 58 | } 59 | 60 | // InterfaceOf dereferences a pointer to an Interface type. 61 | // It panics if value is not an pointer to an interface. 62 | func InterfaceOf(value interface{}) reflect.Type { 63 | t := reflect.TypeOf(value) 64 | 65 | for t.Kind() == reflect.Ptr { 66 | t = t.Elem() 67 | } 68 | 69 | if t.Kind() != reflect.Interface { 70 | panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)") 71 | } 72 | 73 | return t 74 | } 75 | 76 | // New returns a new Injector. 77 | func New() Injector { 78 | return &injector{ 79 | values: make(map[reflect.Type]reflect.Value), 80 | } 81 | } 82 | 83 | // Invoke attempts to call the interface{} provided as a function, 84 | // providing dependencies for function arguments based on Type. 85 | // Returns a slice of reflect.Value representing the returned values of the function. 86 | // Returns an error if the injection fails. 87 | // It panics if f is not a function 88 | func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) { 89 | t := reflect.TypeOf(f) 90 | 91 | var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func 92 | for i := 0; i < t.NumIn(); i++ { 93 | argType := t.In(i) 94 | val := inj.Get(argType) 95 | if !val.IsValid() { 96 | return nil, fmt.Errorf("Value not found for type %v", argType) 97 | } 98 | 99 | in[i] = val 100 | } 101 | 102 | return reflect.ValueOf(f).Call(in), nil 103 | } 104 | 105 | // Maps dependencies in the Type map to each field in the struct 106 | // that is tagged with 'inject'. 107 | // Returns an error if the injection fails. 108 | func (inj *injector) Apply(val interface{}) error { 109 | v := reflect.ValueOf(val) 110 | 111 | for v.Kind() == reflect.Ptr { 112 | v = v.Elem() 113 | } 114 | 115 | if v.Kind() != reflect.Struct { 116 | return nil // Should not panic here ? 117 | } 118 | 119 | t := v.Type() 120 | 121 | for i := 0; i < v.NumField(); i++ { 122 | f := v.Field(i) 123 | structField := t.Field(i) 124 | if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") { 125 | ft := f.Type() 126 | v := inj.Get(ft) 127 | if !v.IsValid() { 128 | return fmt.Errorf("Value not found for type %v", ft) 129 | } 130 | 131 | f.Set(v) 132 | } 133 | 134 | } 135 | 136 | return nil 137 | } 138 | 139 | // Maps the concrete value of val to its dynamic type using reflect.TypeOf, 140 | // It returns the TypeMapper registered in. 141 | func (i *injector) Map(val interface{}) TypeMapper { 142 | i.values[reflect.TypeOf(val)] = reflect.ValueOf(val) 143 | return i 144 | } 145 | 146 | func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper { 147 | i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val) 148 | return i 149 | } 150 | 151 | // Maps the given reflect.Type to the given reflect.Value and returns 152 | // the Typemapper the mapping has been registered in. 153 | func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper { 154 | i.values[typ] = val 155 | return i 156 | } 157 | 158 | func (i *injector) Get(t reflect.Type) reflect.Value { 159 | val := i.values[t] 160 | 161 | if val.IsValid() { 162 | return val 163 | } 164 | 165 | // no concrete types found, try to find implementors 166 | // if t is an interface 167 | if t.Kind() == reflect.Interface { 168 | for k, v := range i.values { 169 | if k.Implements(t) { 170 | val = v 171 | break 172 | } 173 | } 174 | } 175 | 176 | // Still no type found, try to look it up on the parent 177 | if !val.IsValid() && i.parent != nil { 178 | val = i.parent.Get(t) 179 | } 180 | 181 | return val 182 | 183 | } 184 | 185 | func (i *injector) SetParent(parent Injector) { 186 | i.parent = parent 187 | } 188 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/inject/update_readme.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go get github.com/robertkrimen/godocdown/godocdown 3 | godocdown > README.md 4 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/.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 | *.test 24 | 25 | /.godeps 26 | /.envrc 27 | 28 | # Godeps 29 | Godeps/_workspace 30 | Godeps/Readme 31 | 32 | ### JetBrains template 33 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 34 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 35 | 36 | .idea/ 37 | 38 | ## File-based project format: 39 | *.iws 40 | 41 | ## Plugin-specific files: 42 | 43 | # IntelliJ 44 | /out/ 45 | 46 | # mpeltonen/sbt-idea plugin 47 | .idea_modules/ 48 | 49 | # JIRA plugin 50 | atlassian-ide-plugin.xml 51 | 52 | # Crashlytics plugin (for Android Studio and IntelliJ) 53 | com_crashlytics_export_strings.xml 54 | crashlytics.properties 55 | crashlytics-build.properties 56 | fabric.properties 57 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/env.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | // Envs 8 | const ( 9 | Dev string = "development" 10 | Prod string = "production" 11 | Test string = "test" 12 | ) 13 | 14 | // Env is the environment that Martini is executing in. The MARTINI_ENV is read on initialization to set this variable. 15 | var Env = Dev 16 | var Root string 17 | 18 | func setENV(e string) { 19 | if len(e) > 0 { 20 | Env = e 21 | } 22 | } 23 | 24 | func init() { 25 | setENV(os.Getenv("MARTINI_ENV")) 26 | var err error 27 | Root, err = os.Getwd() 28 | if err != nil { 29 | panic(err) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/go_version.go: -------------------------------------------------------------------------------- 1 | // +build !go1.1 2 | 3 | package martini 4 | 5 | func MartiniDoesNotSupportGo1Point0() { 6 | "Martini requires Go 1.1 or greater." 7 | } 8 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/logger.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "time" 7 | ) 8 | 9 | // Logger returns a middleware handler that logs the request as it goes in and the response as it goes out. 10 | func Logger() Handler { 11 | return func(res http.ResponseWriter, req *http.Request, c Context, log *log.Logger) { 12 | start := time.Now() 13 | 14 | addr := req.Header.Get("X-Real-IP") 15 | if addr == "" { 16 | addr = req.Header.Get("X-Forwarded-For") 17 | if addr == "" { 18 | addr = req.RemoteAddr 19 | } 20 | } 21 | 22 | log.Printf("Started %s %s for %s", req.Method, req.URL.Path, addr) 23 | 24 | rw := res.(ResponseWriter) 25 | c.Next() 26 | 27 | log.Printf("Completed %v %s in %v\n", rw.Status(), http.StatusText(rw.Status()), time.Since(start)) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/martini.go: -------------------------------------------------------------------------------- 1 | // Package martini is a powerful package for quickly writing modular web applications/services in Golang. 2 | // 3 | // For a full guide visit http://github.com/go-martini/martini 4 | // 5 | // package main 6 | // 7 | // import "github.com/go-martini/martini" 8 | // 9 | // func main() { 10 | // m := martini.Classic() 11 | // 12 | // m.Get("/", func() string { 13 | // return "Hello world!" 14 | // }) 15 | // 16 | // m.Run() 17 | // } 18 | package martini 19 | 20 | import ( 21 | "log" 22 | "net/http" 23 | "os" 24 | "reflect" 25 | 26 | "github.com/codegangsta/inject" 27 | ) 28 | 29 | // Martini represents the top level web application. inject.Injector methods can be invoked to map services on a global level. 30 | type Martini struct { 31 | inject.Injector 32 | handlers []Handler 33 | action Handler 34 | logger *log.Logger 35 | } 36 | 37 | // New creates a bare bones Martini instance. Use this method if you want to have full control over the middleware that is used. 38 | func New() *Martini { 39 | m := &Martini{Injector: inject.New(), action: func() {}, logger: log.New(os.Stdout, "[martini] ", 0)} 40 | m.Map(m.logger) 41 | m.Map(defaultReturnHandler()) 42 | return m 43 | } 44 | 45 | // Handlers sets the entire middleware stack with the given Handlers. This will clear any current middleware handlers. 46 | // Will panic if any of the handlers is not a callable function 47 | func (m *Martini) Handlers(handlers ...Handler) { 48 | m.handlers = make([]Handler, 0) 49 | for _, handler := range handlers { 50 | m.Use(handler) 51 | } 52 | } 53 | 54 | // Action sets the handler that will be called after all the middleware has been invoked. This is set to martini.Router in a martini.Classic(). 55 | func (m *Martini) Action(handler Handler) { 56 | validateHandler(handler) 57 | m.action = handler 58 | } 59 | 60 | // Logger sets the logger 61 | func (m *Martini) Logger(logger *log.Logger) { 62 | m.logger = logger 63 | m.Map(m.logger) 64 | } 65 | 66 | // Use adds a middleware Handler to the stack. Will panic if the handler is not a callable func. Middleware Handlers are invoked in the order that they are added. 67 | func (m *Martini) Use(handler Handler) { 68 | validateHandler(handler) 69 | 70 | m.handlers = append(m.handlers, handler) 71 | } 72 | 73 | // ServeHTTP is the HTTP Entry point for a Martini instance. Useful if you want to control your own HTTP server. 74 | func (m *Martini) ServeHTTP(res http.ResponseWriter, req *http.Request) { 75 | m.createContext(res, req).run() 76 | } 77 | 78 | // Run the http server on a given host and port. 79 | func (m *Martini) RunOnAddr(addr string) { 80 | // TODO: Should probably be implemented using a new instance of http.Server in place of 81 | // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use. 82 | // This would also allow to improve testing when a custom host and port are passed. 83 | 84 | logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger) 85 | logger.Printf("listening on %s (%s)\n", addr, Env) 86 | logger.Fatalln(http.ListenAndServe(addr, m)) 87 | } 88 | 89 | // Run the http server. Listening on os.GetEnv("PORT") or 3000 by default. 90 | func (m *Martini) Run() { 91 | port := os.Getenv("PORT") 92 | if len(port) == 0 { 93 | port = "3000" 94 | } 95 | 96 | host := os.Getenv("HOST") 97 | 98 | m.RunOnAddr(host + ":" + port) 99 | } 100 | 101 | func (m *Martini) createContext(res http.ResponseWriter, req *http.Request) *context { 102 | c := &context{inject.New(), m.handlers, m.action, NewResponseWriter(res), 0} 103 | c.SetParent(m) 104 | c.MapTo(c, (*Context)(nil)) 105 | c.MapTo(c.rw, (*http.ResponseWriter)(nil)) 106 | c.Map(req) 107 | return c 108 | } 109 | 110 | // ClassicMartini represents a Martini with some reasonable defaults. Embeds the router functions for convenience. 111 | type ClassicMartini struct { 112 | *Martini 113 | Router 114 | } 115 | 116 | // Classic creates a classic Martini with some basic default middleware - martini.Logger, martini.Recovery and martini.Static. 117 | // Classic also maps martini.Routes as a service. 118 | func Classic() *ClassicMartini { 119 | r := NewRouter() 120 | m := New() 121 | m.Use(Logger()) 122 | m.Use(Recovery()) 123 | m.Use(Static("public")) 124 | m.MapTo(r, (*Routes)(nil)) 125 | m.Action(r.Handle) 126 | return &ClassicMartini{m, r} 127 | } 128 | 129 | // Handler can be any callable function. Martini attempts to inject services into the handler's argument list. 130 | // Martini will panic if an argument could not be fullfilled via dependency injection. 131 | type Handler interface{} 132 | 133 | func validateHandler(handler Handler) { 134 | if reflect.TypeOf(handler).Kind() != reflect.Func { 135 | panic("martini handler must be a callable func") 136 | } 137 | } 138 | 139 | // Context represents a request context. Services can be mapped on the request level from this interface. 140 | type Context interface { 141 | inject.Injector 142 | // Next is an optional function that Middleware Handlers can call to yield the until after 143 | // the other Handlers have been executed. This works really well for any operations that must 144 | // happen after an http request 145 | Next() 146 | // Written returns whether or not the response for this context has been written. 147 | Written() bool 148 | } 149 | 150 | type context struct { 151 | inject.Injector 152 | handlers []Handler 153 | action Handler 154 | rw ResponseWriter 155 | index int 156 | } 157 | 158 | func (c *context) handler() Handler { 159 | if c.index < len(c.handlers) { 160 | return c.handlers[c.index] 161 | } 162 | if c.index == len(c.handlers) { 163 | return c.action 164 | } 165 | panic("invalid index for context handler") 166 | } 167 | 168 | func (c *context) Next() { 169 | c.index += 1 170 | c.run() 171 | } 172 | 173 | func (c *context) Written() bool { 174 | return c.rw.Written() 175 | } 176 | 177 | func (c *context) run() { 178 | for c.index <= len(c.handlers) { 179 | _, err := c.Invoke(c.handler()) 180 | if err != nil { 181 | panic(err) 182 | } 183 | c.index += 1 184 | 185 | if c.Written() { 186 | return 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/recovery.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "runtime" 10 | 11 | "github.com/codegangsta/inject" 12 | ) 13 | 14 | const ( 15 | panicHtml = ` 16 | PANIC: %s 17 | 37 | 38 |

PANIC

39 |
%s
40 |
%s
41 | 42 | ` 43 | ) 44 | 45 | var ( 46 | dunno = []byte("???") 47 | centerDot = []byte("·") 48 | dot = []byte(".") 49 | slash = []byte("/") 50 | ) 51 | 52 | // stack returns a nicely formated stack frame, skipping skip frames 53 | func stack(skip int) []byte { 54 | buf := new(bytes.Buffer) // the returned data 55 | // As we loop, we open files and read them. These variables record the currently 56 | // loaded file. 57 | var lines [][]byte 58 | var lastFile string 59 | for i := skip; ; i++ { // Skip the expected number of frames 60 | pc, file, line, ok := runtime.Caller(i) 61 | if !ok { 62 | break 63 | } 64 | // Print this much at least. If we can't find the source, it won't show. 65 | fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) 66 | if file != lastFile { 67 | data, err := ioutil.ReadFile(file) 68 | if err != nil { 69 | continue 70 | } 71 | lines = bytes.Split(data, []byte{'\n'}) 72 | lastFile = file 73 | } 74 | fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) 75 | } 76 | return buf.Bytes() 77 | } 78 | 79 | // source returns a space-trimmed slice of the n'th line. 80 | func source(lines [][]byte, n int) []byte { 81 | n-- // in stack trace, lines are 1-indexed but our array is 0-indexed 82 | if n < 0 || n >= len(lines) { 83 | return dunno 84 | } 85 | return bytes.TrimSpace(lines[n]) 86 | } 87 | 88 | // function returns, if possible, the name of the function containing the PC. 89 | func function(pc uintptr) []byte { 90 | fn := runtime.FuncForPC(pc) 91 | if fn == nil { 92 | return dunno 93 | } 94 | name := []byte(fn.Name()) 95 | // The name includes the path name to the package, which is unnecessary 96 | // since the file name is already included. Plus, it has center dots. 97 | // That is, we see 98 | // runtime/debug.*T·ptrmethod 99 | // and want 100 | // *T.ptrmethod 101 | // Also the package path might contains dot (e.g. code.google.com/...), 102 | // so first eliminate the path prefix 103 | if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 { 104 | name = name[lastslash+1:] 105 | } 106 | if period := bytes.Index(name, dot); period >= 0 { 107 | name = name[period+1:] 108 | } 109 | name = bytes.Replace(name, centerDot, dot, -1) 110 | return name 111 | } 112 | 113 | // Recovery returns a middleware that recovers from any panics and writes a 500 if there was one. 114 | // While Martini is in development mode, Recovery will also output the panic as HTML. 115 | func Recovery() Handler { 116 | return func(c Context, log *log.Logger) { 117 | defer func() { 118 | if err := recover(); err != nil { 119 | stack := stack(3) 120 | log.Printf("PANIC: %s\n%s", err, stack) 121 | 122 | // Lookup the current responsewriter 123 | val := c.Get(inject.InterfaceOf((*http.ResponseWriter)(nil))) 124 | res := val.Interface().(http.ResponseWriter) 125 | 126 | // respond with panic message while in development mode 127 | var body []byte 128 | if Env == Dev { 129 | res.Header().Set("Content-Type", "text/html") 130 | body = []byte(fmt.Sprintf(panicHtml, err, err, stack)) 131 | } else { 132 | body = []byte("500 Internal Server Error") 133 | } 134 | 135 | res.WriteHeader(http.StatusInternalServerError) 136 | if nil != body { 137 | res.Write(body) 138 | } 139 | } 140 | }() 141 | 142 | c.Next() 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/response_writer.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "net" 7 | "net/http" 8 | ) 9 | 10 | // ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about 11 | // the response. It is recommended that middleware handlers use this construct to wrap a responsewriter 12 | // if the functionality calls for it. 13 | type ResponseWriter interface { 14 | http.ResponseWriter 15 | http.Flusher 16 | http.Hijacker 17 | // Status returns the status code of the response or 0 if the response has not been written. 18 | Status() int 19 | // Written returns whether or not the ResponseWriter has been written. 20 | Written() bool 21 | // Size returns the size of the response body. 22 | Size() int 23 | // Before allows for a function to be called before the ResponseWriter has been written to. This is 24 | // useful for setting headers or any other operations that must happen before a response has been written. 25 | Before(BeforeFunc) 26 | } 27 | 28 | // BeforeFunc is a function that is called before the ResponseWriter has been written to. 29 | type BeforeFunc func(ResponseWriter) 30 | 31 | // NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter 32 | func NewResponseWriter(rw http.ResponseWriter) ResponseWriter { 33 | newRw := responseWriter{rw, 0, 0, nil} 34 | if cn, ok := rw.(http.CloseNotifier); ok { 35 | return &closeNotifyResponseWriter{newRw, cn} 36 | } 37 | return &newRw 38 | } 39 | 40 | type responseWriter struct { 41 | http.ResponseWriter 42 | status int 43 | size int 44 | beforeFuncs []BeforeFunc 45 | } 46 | 47 | func (rw *responseWriter) WriteHeader(s int) { 48 | rw.callBefore() 49 | rw.ResponseWriter.WriteHeader(s) 50 | rw.status = s 51 | } 52 | 53 | func (rw *responseWriter) Write(b []byte) (int, error) { 54 | if !rw.Written() { 55 | // The status will be StatusOK if WriteHeader has not been called yet 56 | rw.WriteHeader(http.StatusOK) 57 | } 58 | size, err := rw.ResponseWriter.Write(b) 59 | rw.size += size 60 | return size, err 61 | } 62 | 63 | func (rw *responseWriter) Status() int { 64 | return rw.status 65 | } 66 | 67 | func (rw *responseWriter) Size() int { 68 | return rw.size 69 | } 70 | 71 | func (rw *responseWriter) Written() bool { 72 | return rw.status != 0 73 | } 74 | 75 | func (rw *responseWriter) Before(before BeforeFunc) { 76 | rw.beforeFuncs = append(rw.beforeFuncs, before) 77 | } 78 | 79 | func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { 80 | hijacker, ok := rw.ResponseWriter.(http.Hijacker) 81 | if !ok { 82 | return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") 83 | } 84 | return hijacker.Hijack() 85 | } 86 | 87 | func (rw *responseWriter) callBefore() { 88 | for i := len(rw.beforeFuncs) - 1; i >= 0; i-- { 89 | rw.beforeFuncs[i](rw) 90 | } 91 | } 92 | 93 | func (rw *responseWriter) Flush() { 94 | flusher, ok := rw.ResponseWriter.(http.Flusher) 95 | if ok { 96 | flusher.Flush() 97 | } 98 | } 99 | 100 | type closeNotifyResponseWriter struct { 101 | responseWriter 102 | closeNotifier http.CloseNotifier 103 | } 104 | 105 | func (rw *closeNotifyResponseWriter) CloseNotify() <-chan bool { 106 | return rw.closeNotifier.CloseNotify() 107 | } 108 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/return_handler.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "github.com/codegangsta/inject" 5 | "net/http" 6 | "reflect" 7 | ) 8 | 9 | // ReturnHandler is a service that Martini provides that is called 10 | // when a route handler returns something. The ReturnHandler is 11 | // responsible for writing to the ResponseWriter based on the values 12 | // that are passed into this function. 13 | type ReturnHandler func(Context, []reflect.Value) 14 | 15 | func defaultReturnHandler() ReturnHandler { 16 | return func(ctx Context, vals []reflect.Value) { 17 | rv := ctx.Get(inject.InterfaceOf((*http.ResponseWriter)(nil))) 18 | res := rv.Interface().(http.ResponseWriter) 19 | var responseVal reflect.Value 20 | if len(vals) > 1 && vals[0].Kind() == reflect.Int { 21 | res.WriteHeader(int(vals[0].Int())) 22 | responseVal = vals[1] 23 | } else if len(vals) > 0 { 24 | responseVal = vals[0] 25 | } 26 | if canDeref(responseVal) { 27 | responseVal = responseVal.Elem() 28 | } 29 | if isByteSlice(responseVal) { 30 | res.Write(responseVal.Bytes()) 31 | } else { 32 | res.Write([]byte(responseVal.String())) 33 | } 34 | } 35 | } 36 | 37 | func isByteSlice(val reflect.Value) bool { 38 | return val.Kind() == reflect.Slice && val.Type().Elem().Kind() == reflect.Uint8 39 | } 40 | 41 | func canDeref(val reflect.Value) bool { 42 | return val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr 43 | } 44 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/static.go: -------------------------------------------------------------------------------- 1 | package martini 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "net/url" 7 | "path" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | // StaticOptions is a struct for specifying configuration options for the martini.Static middleware. 13 | type StaticOptions struct { 14 | // Prefix is the optional prefix used to serve the static directory content 15 | Prefix string 16 | // SkipLogging will disable [Static] log messages when a static file is served. 17 | SkipLogging bool 18 | // IndexFile defines which file to serve as index if it exists. 19 | IndexFile string 20 | // Expires defines which user-defined function to use for producing a HTTP Expires Header 21 | // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching 22 | Expires func() string 23 | // Fallback defines a default URL to serve when the requested resource was 24 | // not found. 25 | Fallback string 26 | // Exclude defines a pattern for URLs this handler should never process. 27 | Exclude string 28 | } 29 | 30 | func prepareStaticOptions(options []StaticOptions) StaticOptions { 31 | var opt StaticOptions 32 | if len(options) > 0 { 33 | opt = options[0] 34 | } 35 | 36 | // Defaults 37 | if len(opt.IndexFile) == 0 { 38 | opt.IndexFile = "index.html" 39 | } 40 | // Normalize the prefix if provided 41 | if opt.Prefix != "" { 42 | // Ensure we have a leading '/' 43 | if opt.Prefix[0] != '/' { 44 | opt.Prefix = "/" + opt.Prefix 45 | } 46 | // Remove any trailing '/' 47 | opt.Prefix = strings.TrimRight(opt.Prefix, "/") 48 | } 49 | return opt 50 | } 51 | 52 | // Static returns a middleware handler that serves static files in the given directory. 53 | func Static(directory string, staticOpt ...StaticOptions) Handler { 54 | if !filepath.IsAbs(directory) { 55 | directory = filepath.Join(Root, directory) 56 | } 57 | dir := http.Dir(directory) 58 | opt := prepareStaticOptions(staticOpt) 59 | 60 | return func(res http.ResponseWriter, req *http.Request, log *log.Logger) { 61 | if req.Method != "GET" && req.Method != "HEAD" { 62 | return 63 | } 64 | if opt.Exclude != "" && strings.HasPrefix(req.URL.Path, opt.Exclude) { 65 | return 66 | } 67 | file := req.URL.Path 68 | // if we have a prefix, filter requests by stripping the prefix 69 | if opt.Prefix != "" { 70 | if !strings.HasPrefix(file, opt.Prefix) { 71 | return 72 | } 73 | file = file[len(opt.Prefix):] 74 | if file != "" && file[0] != '/' { 75 | return 76 | } 77 | } 78 | f, err := dir.Open(file) 79 | if err != nil { 80 | // try any fallback before giving up 81 | if opt.Fallback != "" { 82 | file = opt.Fallback // so that logging stays true 83 | f, err = dir.Open(opt.Fallback) 84 | } 85 | 86 | if err != nil { 87 | // discard the error? 88 | return 89 | } 90 | } 91 | defer f.Close() 92 | 93 | fi, err := f.Stat() 94 | if err != nil { 95 | return 96 | } 97 | 98 | // try to serve index file 99 | if fi.IsDir() { 100 | // redirect if missing trailing slash 101 | if !strings.HasSuffix(req.URL.Path, "/") { 102 | dest := url.URL{ 103 | Path: req.URL.Path + "/", 104 | RawQuery: req.URL.RawQuery, 105 | Fragment: req.URL.Fragment, 106 | } 107 | http.Redirect(res, req, dest.String(), http.StatusFound) 108 | return 109 | } 110 | 111 | file = path.Join(file, opt.IndexFile) 112 | f, err = dir.Open(file) 113 | if err != nil { 114 | return 115 | } 116 | defer f.Close() 117 | 118 | fi, err = f.Stat() 119 | if err != nil || fi.IsDir() { 120 | return 121 | } 122 | } 123 | 124 | if !opt.SkipLogging { 125 | log.Println("[Static] Serving " + file) 126 | } 127 | 128 | // Add an Expires header to the static content 129 | if opt.Expires != nil { 130 | res.Header().Set("Expires", opt.Expires()) 131 | } 132 | 133 | http.ServeContent(res, req, file, fi.ModTime(), f) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /vendor/github.com/go-martini/martini/wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang@1.1.1 -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/auth/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/auth/README.md: -------------------------------------------------------------------------------- 1 | # auth [![wercker status](https://app.wercker.com/status/8e5237b01b52f169a1274fad9a89617b "wercker status")](https://app.wercker.com/project/bykey/8e5237b01b52f169a1274fad9a89617b) 2 | Martini middleware/handler for http basic authentication. 3 | 4 | [API Reference](http://godoc.org/github.com/martini-contrib/auth) 5 | 6 | ## Simple Usage 7 | 8 | Use `auth.Basic` to authenticate against a pre-defined username and password: 9 | 10 | ~~~ go 11 | import ( 12 | "github.com/go-martini/martini" 13 | "github.com/martini-contrib/auth" 14 | ) 15 | 16 | func main() { 17 | m := martini.Classic() 18 | // authenticate every request 19 | m.Use(auth.Basic("username", "secretpassword")) 20 | m.Run() 21 | } 22 | ~~~ 23 | 24 | ## Advanced Usage 25 | 26 | Using `auth.BasicFunc` lets you authenticate on a per-user level, by checking 27 | the username and password in the callback function: 28 | 29 | ~~~ go 30 | import ( 31 | "github.com/go-martini/martini" 32 | "github.com/martini-contrib/auth" 33 | ) 34 | 35 | func main() { 36 | m := martini.Classic() 37 | // authenticate every request 38 | m.Use(auth.BasicFunc(func(username, password string) bool { 39 | return username == "admin" && password == "guessme" 40 | })) 41 | m.Run() 42 | } 43 | ~~~ 44 | 45 | Note that checking usernames and passwords with string comparison might be 46 | susceptible to timing attacks. To avoid that, use `auth.SecureCompare` instead: 47 | 48 | ~~~ go 49 | m.Use(auth.BasicFunc(func(username, password string) bool { 50 | return auth.SecureCompare(username, "admin") && auth.SecureCompare(password, "guessme") 51 | })) 52 | } 53 | ~~~ 54 | 55 | Upon successful authentication, the username is available to all subsequent 56 | handlers via the `auth.User` type: 57 | 58 | ~~~ go 59 | m.Get("/", func(user auth.User) string { 60 | return "Welcome, " + string(user) 61 | }) 62 | } 63 | ~~~ 64 | 65 | ## Authors 66 | * [Jeremy Saenz](http://github.com/codegangsta) 67 | * [Brendon Murphy](http://github.com/bemurphy) 68 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/auth/basic.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "encoding/base64" 5 | "github.com/go-martini/martini" 6 | "net/http" 7 | "strings" 8 | ) 9 | 10 | // User is the authenticated username that was extracted from the request. 11 | type User string 12 | 13 | // BasicRealm is used when setting the WWW-Authenticate response header. 14 | var BasicRealm = "Authorization Required" 15 | 16 | // Basic returns a Handler that authenticates via Basic Auth. Writes a http.StatusUnauthorized 17 | // if authentication fails. 18 | func Basic(username string, password string) martini.Handler { 19 | var siteAuth = base64.StdEncoding.EncodeToString([]byte(username + ":" + password)) 20 | return func(res http.ResponseWriter, req *http.Request, c martini.Context) { 21 | auth := req.Header.Get("Authorization") 22 | if !SecureCompare(auth, "Basic "+siteAuth) { 23 | unauthorized(res) 24 | return 25 | } 26 | c.Map(User(username)) 27 | } 28 | } 29 | 30 | // BasicFunc returns a Handler that authenticates via Basic Auth using the provided function. 31 | // The function should return true for a valid username/password combination. 32 | func BasicFunc(authfn func(string, string) bool) martini.Handler { 33 | return func(res http.ResponseWriter, req *http.Request, c martini.Context) { 34 | auth := req.Header.Get("Authorization") 35 | if len(auth) < 6 || auth[:6] != "Basic " { 36 | unauthorized(res) 37 | return 38 | } 39 | b, err := base64.StdEncoding.DecodeString(auth[6:]) 40 | if err != nil { 41 | unauthorized(res) 42 | return 43 | } 44 | tokens := strings.SplitN(string(b), ":", 2) 45 | if len(tokens) != 2 || !authfn(tokens[0], tokens[1]) { 46 | unauthorized(res) 47 | return 48 | } 49 | c.Map(User(tokens[0])) 50 | } 51 | } 52 | 53 | func unauthorized(res http.ResponseWriter) { 54 | res.Header().Set("WWW-Authenticate", "Basic realm=\""+BasicRealm+"\"") 55 | http.Error(res, "Not Authorized", http.StatusUnauthorized) 56 | } 57 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/auth/util.go: -------------------------------------------------------------------------------- 1 | package auth 2 | 3 | import ( 4 | "crypto/sha256" 5 | "crypto/subtle" 6 | ) 7 | 8 | // SecureCompare performs a constant time compare of two strings to limit timing attacks. 9 | func SecureCompare(given string, actual string) bool { 10 | givenSha := sha256.Sum256([]byte(given)) 11 | actualSha := sha256.Sum256([]byte(actual)) 12 | 13 | return subtle.ConstantTimeCompare(givenSha[:], actualSha[:]) == 1 14 | } 15 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/auth/wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang@1.1.1 -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Jeremy Saenz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/README.md: -------------------------------------------------------------------------------- 1 | # render [![wercker status](https://app.wercker.com/status/fcf6b26a1b41f53540200b1949b48dec "wercker status")](https://app.wercker.com/project/bykey/fcf6b26a1b41f53540200b1949b48dec) 2 | Martini middleware/handler for easily rendering serialized JSON, XML, and HTML template responses. 3 | 4 | [API Reference](http://godoc.org/github.com/martini-contrib/render) 5 | 6 | ## Usage 7 | render uses Go's [html/template](http://golang.org/pkg/html/template/) package to render html templates. 8 | 9 | ~~~ go 10 | // main.go 11 | package main 12 | 13 | import ( 14 | "github.com/go-martini/martini" 15 | "github.com/martini-contrib/render" 16 | ) 17 | 18 | func main() { 19 | m := martini.Classic() 20 | // render html templates from templates directory 21 | m.Use(render.Renderer()) 22 | 23 | m.Get("/", func(r render.Render) { 24 | r.HTML(200, "hello", "jeremy") 25 | }) 26 | 27 | m.Run() 28 | } 29 | 30 | ~~~ 31 | 32 | ~~~ html 33 | 34 |

Hello {{.}}!

35 | ~~~ 36 | 37 | ### Options 38 | `render.Renderer` comes with a variety of configuration options: 39 | 40 | ~~~ go 41 | // ... 42 | m.Use(render.Renderer(render.Options{ 43 | Directory: "templates", // Specify what path to load the templates from. 44 | Layout: "layout", // Specify a layout template. Layouts can call {{ yield }} to render the current template. 45 | Extensions: []string{".tmpl", ".html"}, // Specify extensions to load for templates. 46 | Funcs: []template.FuncMap{AppHelpers}, // Specify helper function maps for templates to access. 47 | Delims: render.Delims{"{[{", "}]}"}, // Sets delimiters to the specified strings. 48 | Charset: "UTF-8", // Sets encoding for json and html content-types. Default is "UTF-8". 49 | IndentJSON: true, // Output human readable JSON 50 | IndentXML: true, // Output human readable XML 51 | HTMLContentType: "application/xhtml+xml", // Output XHTML content type instead of default "text/html" 52 | })) 53 | // ... 54 | ~~~ 55 | 56 | ### Loading Templates 57 | By default the `render.Renderer` middleware will attempt to load templates with a '.tmpl' extension from the "templates" directory. Templates are found by traversing the templates directory and are named by path and basename. For instance, the following directory structure: 58 | 59 | ~~~ 60 | templates/ 61 | | 62 | |__ admin/ 63 | | | 64 | | |__ index.tmpl 65 | | | 66 | | |__ edit.tmpl 67 | | 68 | |__ home.tmpl 69 | ~~~ 70 | 71 | Will provide the following templates: 72 | ~~~ 73 | admin/index 74 | admin/edit 75 | home 76 | ~~~ 77 | ### Layouts 78 | `render.Renderer` provides a `yield` function for layouts to access: 79 | ~~~ go 80 | // ... 81 | m.Use(render.Renderer(render.Options{ 82 | Layout: "layout", 83 | })) 84 | // ... 85 | ~~~ 86 | 87 | ~~~ html 88 | 89 | 90 | 91 | Martini Plz 92 | 93 | 94 | 95 | {{ yield }} 96 | 97 | 98 | ~~~ 99 | 100 | `current` can also be called to get the current template being rendered. 101 | ~~~ html 102 | 103 | 104 | 105 | Martini Plz 106 | 107 | 108 | This is the {{ current }} page. 109 | 110 | 111 | ~~~ 112 | 113 | ### Character Encodings 114 | The `render.Renderer` middleware will automatically set the proper Content-Type header based on which function you call. See below for an example of what the default settings would output (note that UTF-8 is the default): 115 | ~~~ go 116 | // main.go 117 | package main 118 | 119 | import ( 120 | "encoding/xml" 121 | 122 | "github.com/go-martini/martini" 123 | "github.com/martini-contrib/render" 124 | ) 125 | 126 | type Greeting struct { 127 | XMLName xml.Name `xml:"greeting"` 128 | One string `xml:"one,attr"` 129 | Two string `xml:"two,attr"` 130 | } 131 | 132 | func main() { 133 | m := martini.Classic() 134 | m.Use(render.Renderer()) 135 | 136 | // This will set the Content-Type header to "text/html; charset=UTF-8" 137 | m.Get("/", func(r render.Render) { 138 | r.HTML(200, "hello", "world") 139 | }) 140 | 141 | // This will set the Content-Type header to "application/json; charset=UTF-8" 142 | m.Get("/api", func(r render.Render) { 143 | r.JSON(200, map[string]interface{}{"hello": "world"}) 144 | }) 145 | 146 | // This will set the Content-Type header to "text/xml; charset=UTF-8" 147 | m.Get("/xml", func(r render.Render) { 148 | r.XML(200, Greeting{One: "hello", Two: "world"}) 149 | }) 150 | 151 | // This will set the Content-Type header to "text/plain; charset=UTF-8" 152 | m.Get("/text", func(r render.Render) { 153 | r.Text(200, "hello, world") 154 | }) 155 | 156 | m.Run() 157 | } 158 | 159 | ~~~ 160 | 161 | In order to change the charset, you can set the `Charset` within the `render.Options` to your encoding value: 162 | ~~~ go 163 | // main.go 164 | package main 165 | 166 | import ( 167 | "encoding/xml" 168 | 169 | "github.com/go-martini/martini" 170 | "github.com/martini-contrib/render" 171 | ) 172 | 173 | type Greeting struct { 174 | XMLName xml.Name `xml:"greeting"` 175 | One string `xml:"one,attr"` 176 | Two string `xml:"two,attr"` 177 | } 178 | 179 | func main() { 180 | m := martini.Classic() 181 | m.Use(render.Renderer(render.Options{ 182 | Charset: "ISO-8859-1", 183 | })) 184 | 185 | // This will set the Content-Type header to "text/html; charset=ISO-8859-1" 186 | m.Get("/", func(r render.Render) { 187 | r.HTML(200, "hello", "world") 188 | }) 189 | 190 | // This will set the Content-Type header to "application/json; charset=ISO-8859-1" 191 | m.Get("/api", func(r render.Render) { 192 | r.JSON(200, map[string]interface{}{"hello": "world"}) 193 | }) 194 | 195 | // This will set the Content-Type header to "text/xml; charset=ISO-8859-1" 196 | m.Get("/xml", func(r render.Render) { 197 | r.XML(200, Greeting{One: "hello", Two: "world"}) 198 | }) 199 | 200 | // This will set the Content-Type header to "text/plain; charset=ISO-8859-1" 201 | m.Get("/text", func(r render.Render) { 202 | r.Text(200, "hello, world") 203 | }) 204 | 205 | m.Run() 206 | } 207 | 208 | ~~~ 209 | 210 | ## Authors 211 | * [Jeremy Saenz](http://github.com/codegangsta) 212 | * [Cory Jacobsen](http://github.com/unrolled) 213 | -------------------------------------------------------------------------------- /vendor/github.com/martini-contrib/render/wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/golang@1.1.1 -------------------------------------------------------------------------------- /vendor/github.com/oxtoacart/bpool/README.md: -------------------------------------------------------------------------------- 1 | # bpool [![GoDoc](https://godoc.org/github.com/oxtoacart/bpool?status.png)](https://godoc.org/github.com/oxtoacart/bpool) 2 | 3 | Package bpool implements leaky pools of byte arrays and Buffers as bounded channels. 4 | It is based on the leaky buffer example from the Effective Go documentation: http://golang.org/doc/effective_go.html#leaky_buffer 5 | 6 | bpool provides the following pool types: 7 | 8 | * [bpool.BufferPool](https://godoc.org/github.com/oxtoacart/bpool#BufferPool) 9 | which provides a fixed-size pool of 10 | [bytes.Buffers](http://golang.org/pkg/bytes/#Buffer). 11 | * [bpool.BytePool](https://godoc.org/github.com/oxtoacart/bpool#BytePool) which 12 | provides a fixed-size pool of `[]byte` slices with a pre-set width (length). 13 | * [bpool.SizedBufferPool](https://godoc.org/github.com/oxtoacart/bpool#SizedBufferPool), 14 | which is an alternative to `bpool.BufferPool` that pre-sizes the capacity of 15 | buffers issued from the pool and discards buffers that have grown too large 16 | upon return. 17 | 18 | A common use case for this package is to use buffers to execute HTML templates 19 | against (via ExecuteTemplate) or encode JSON into (via json.NewEncoder). This 20 | allows you to catch any rendering or marshalling errors prior to writing to a 21 | `http.ResponseWriter`, which helps to avoid writing incomplete or malformed data 22 | to the response. 23 | 24 | ## Install 25 | 26 | `go get github.com/oxtoacart/bpool` 27 | 28 | ## Documentation 29 | 30 | See [godoc.org](http://godoc.org/github.com/oxtoacart/bpool) or use `godoc github.com/oxtoacart/bpool` 31 | 32 | ## Example 33 | 34 | Here's a quick example for using `bpool.BufferPool`. We create a pool of the 35 | desired size, call the `Get()` method to obtain a buffer for use, and call 36 | `Put(buf)` to return the buffer to the pool. 37 | 38 | ```go 39 | 40 | var bufpool *bpool.BufferPool 41 | 42 | func main() { 43 | 44 | bufpool = bpool.NewBufferPool(48) 45 | 46 | } 47 | 48 | func someFunction() error { 49 | 50 | // Get a buffer from the pool 51 | buf := bufpool.Get() 52 | ... 53 | ... 54 | ... 55 | // Return the buffer to the pool 56 | bufpool.Put(buf) 57 | 58 | return nil 59 | } 60 | ``` 61 | 62 | ## License 63 | 64 | Apache 2.0 Licensed. See the LICENSE file for details. 65 | 66 | -------------------------------------------------------------------------------- /vendor/github.com/oxtoacart/bpool/bpool.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package bpool implements leaky pools of byte arrays and Buffers as bounded 3 | channels. It is based on the leaky buffer example from the Effective Go 4 | documentation: http://golang.org/doc/effective_go.html#leaky_buffer 5 | */ 6 | package bpool 7 | -------------------------------------------------------------------------------- /vendor/github.com/oxtoacart/bpool/bufferpool.go: -------------------------------------------------------------------------------- 1 | package bpool 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | // BufferPool implements a pool of bytes.Buffers in the form of a bounded 8 | // channel. 9 | type BufferPool struct { 10 | c chan *bytes.Buffer 11 | } 12 | 13 | // NewBufferPool creates a new BufferPool bounded to the given size. 14 | func NewBufferPool(size int) (bp *BufferPool) { 15 | return &BufferPool{ 16 | c: make(chan *bytes.Buffer, size), 17 | } 18 | } 19 | 20 | // Get gets a Buffer from the BufferPool, or creates a new one if none are 21 | // available in the pool. 22 | func (bp *BufferPool) Get() (b *bytes.Buffer) { 23 | select { 24 | case b = <-bp.c: 25 | // reuse existing buffer 26 | default: 27 | // create new buffer 28 | b = bytes.NewBuffer([]byte{}) 29 | } 30 | return 31 | } 32 | 33 | // Put returns the given Buffer to the BufferPool. 34 | func (bp *BufferPool) Put(b *bytes.Buffer) { 35 | b.Reset() 36 | select { 37 | case bp.c <- b: 38 | default: // Discard the buffer if the pool is full. 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /vendor/github.com/oxtoacart/bpool/bytepool.go: -------------------------------------------------------------------------------- 1 | package bpool 2 | 3 | // BytePool implements a leaky pool of []byte in the form of a bounded 4 | // channel. 5 | type BytePool struct { 6 | c chan []byte 7 | w int 8 | } 9 | 10 | // NewBytePool creates a new BytePool bounded to the given maxSize, with new 11 | // byte arrays sized based on width. 12 | func NewBytePool(maxSize int, width int) (bp *BytePool) { 13 | return &BytePool{ 14 | c: make(chan []byte, maxSize), 15 | w: width, 16 | } 17 | } 18 | 19 | // Get gets a []byte from the BytePool, or creates a new one if none are 20 | // available in the pool. 21 | func (bp *BytePool) Get() (b []byte) { 22 | select { 23 | case b = <-bp.c: 24 | // reuse existing buffer 25 | default: 26 | // create new buffer 27 | b = make([]byte, bp.w) 28 | } 29 | return 30 | } 31 | 32 | // Put returns the given Buffer to the BytePool. 33 | func (bp *BytePool) Put(b []byte) { 34 | select { 35 | case bp.c <- b: 36 | // buffer went back into pool 37 | default: 38 | // buffer didn't go back into pool, just discard 39 | } 40 | } 41 | 42 | // Width returns the width of the byte arrays in this pool. 43 | func (bp *BytePool) Width() (n int) { 44 | return bp.w 45 | } 46 | -------------------------------------------------------------------------------- /vendor/github.com/oxtoacart/bpool/sizedbufferpool.go: -------------------------------------------------------------------------------- 1 | package bpool 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | // SizedBufferPool implements a pool of bytes.Buffers in the form of a bounded 8 | // channel. Buffers are pre-allocated to the requested size. 9 | type SizedBufferPool struct { 10 | c chan *bytes.Buffer 11 | a int 12 | } 13 | 14 | // SizedBufferPool creates a new BufferPool bounded to the given size. 15 | // size defines the number of buffers to be retained in the pool and alloc sets 16 | // the initial capacity of new buffers to minimize calls to make(). 17 | // 18 | // The value of alloc should seek to provide a buffer that is representative of 19 | // most data written to the the buffer (i.e. 95th percentile) without being 20 | // overly large (which will increase static memory consumption). You may wish to 21 | // track the capacity of your last N buffers (i.e. using an []int) prior to 22 | // returning them to the pool as input into calculating a suitable alloc value. 23 | func NewSizedBufferPool(size int, alloc int) (bp *SizedBufferPool) { 24 | return &SizedBufferPool{ 25 | c: make(chan *bytes.Buffer, size), 26 | a: alloc, 27 | } 28 | } 29 | 30 | // Get gets a Buffer from the SizedBufferPool, or creates a new one if none are 31 | // available in the pool. Buffers have a pre-allocated capacity. 32 | func (bp *SizedBufferPool) Get() (b *bytes.Buffer) { 33 | select { 34 | case b = <-bp.c: 35 | // reuse existing buffer 36 | default: 37 | // create new buffer 38 | b = bytes.NewBuffer(make([]byte, 0, bp.a)) 39 | } 40 | return 41 | } 42 | 43 | // Put returns the given Buffer to the SizedBufferPool. 44 | func (bp *SizedBufferPool) Put(b *bytes.Buffer) { 45 | b.Reset() 46 | 47 | // Release buffers over our maximum capacity and re-create a pre-sized 48 | // buffer to replace it. 49 | // Note that the cap(b.Bytes()) provides the capacity from the read off-set 50 | // only, but as we've called b.Reset() the full capacity of the underlying 51 | // byte slice is returned. 52 | if cap(b.Bytes()) > bp.a { 53 | b = bytes.NewBuffer(make([]byte, 0, bp.a)) 54 | } 55 | 56 | select { 57 | case bp.c <- b: 58 | default: // Discard the buffer if the pool is full. 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/.gitignore: -------------------------------------------------------------------------------- 1 | certs/* 2 | spec/spec 3 | examples/simple-consumer/simple-consumer 4 | examples/simple-producer/simple-producer 5 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.6 5 | - 1.7 6 | - tip 7 | 8 | services: 9 | - rabbitmq 10 | 11 | env: 12 | - AMQP_URL=amqp://guest:guest@127.0.0.1:5672/ 13 | 14 | before_install: 15 | - go get -v github.com/golang/lint/golint 16 | 17 | script: 18 | - ./pre-commit 19 | - go test -cpu=1,2 -v -tags integration ./... 20 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Prequisites 2 | 3 | 1. Go: [https://golang.org/dl/](https://golang.org/dl/) 4 | 1. Golint `go get -u -v github.com/golang/lint/golint` 5 | 6 | ## Contributing 7 | 8 | The workflow is pretty standard: 9 | 10 | 1. Fork github.com/streadway/amqp 11 | 1. Add the pre-commit hook: `ln -s ../../pre-commit .git/hooks/pre-commit` 12 | 1. Create your feature branch (`git checkout -b my-new-feature`) 13 | 1. Run integration tests (see below) 14 | 1. **Implement tests** 15 | 1. Implement fixs 16 | 1. Commit your changes (`git commit -am 'Add some feature'`) 17 | 1. Push to a branch (`git push -u origin my-new-feature`) 18 | 1. Submit a pull request 19 | 20 | ## Running Tests 21 | 22 | The test suite assumes that: 23 | 24 | * A RabbitMQ node is running on localhost with all defaults: [https://www.rabbitmq.com/download.html](https://www.rabbitmq.com/download.html) 25 | * `AMQP_URL` is exported to `amqp://guest:guest@127.0.0.1:5672/` 26 | 27 | ### Integration Tests 28 | 29 | After starting a local RabbitMQ, run integration tests with the following: 30 | 31 | env AMQP_URL=amqp://guest:guest@127.0.0.1:5672/ go test -v -cpu 2 -tags integration -race 32 | 33 | All integration tests should use the `integrationConnection(...)` test 34 | helpers defined in `integration_test.go` to setup the integration environment 35 | and logging. 36 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 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 met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/README.md: -------------------------------------------------------------------------------- 1 | # Go RabbitMQ Client Library 2 | 3 | This is an AMQP 0.9.1 client with RabbitMQ extensions in Go. 4 | 5 | ## Project Maturity 6 | 7 | This project has been used in production systems for over 3 years. It is reasonably mature 8 | and feature complete, and as of November 2016 has [a team of maintainers](https://github.com/streadway/amqp/issues/215) 9 | instead of being a one man show. 10 | 11 | Future API changes are unlikely but possible. They will be discussed on [Github 12 | issues](https://github.com/streadway/amqp/issues) along with any bugs or 13 | enhancements. 14 | 15 | ## Supported RabbitMQ Versions 16 | 17 | This project supports RabbitMQ versions starting with `2.0` but primarily tested 18 | against reasonably recent `3.x` releases. Some features and behaviours may be 19 | server version-specific. 20 | 21 | ## Goals 22 | 23 | Provide a functional interface that closely represents the AMQP 0.9.1 model 24 | targeted to RabbitMQ as a server. This includes the minimum necessary to 25 | interact the semantics of the protocol. 26 | 27 | ## Non-goals 28 | 29 | Things not intended to be supported. 30 | 31 | * Auto reconnect and re-synchronization of client and server topologies. 32 | * Reconnection would require understanding the error paths when the 33 | topology cannot be declared on reconnect. This would require a new set 34 | of types and code paths that are best suited at the call-site of this 35 | package. AMQP has a dynamic topology that needs all peers to agree. If 36 | this doesn't happen, the behavior is undefined. Instead of producing a 37 | possible interface with undefined behavior, this package is designed to 38 | be simple for the caller to implement the necessary connection-time 39 | topology declaration so that reconnection is trivial and encapsulated in 40 | the caller's application code. 41 | * AMQP Protocol negotiation for forward or backward compatibility. 42 | * 0.9.1 is stable and widely deployed. Versions 0.10 and 1.0 are divergent 43 | specifications that change the semantics and wire format of the protocol. 44 | We will accept patches for other protocol support but have no plans for 45 | implementation ourselves. 46 | * Anything other than PLAIN and EXTERNAL authentication mechanisms. 47 | * Keeping the mechanisms interface modular makes it possible to extend 48 | outside of this package. If other mechanisms prove to be popular, then 49 | we would accept patches to include them in this package. 50 | 51 | ## Usage 52 | 53 | See the 'examples' subdirectory for simple producers and consumers executables. 54 | If you have a use-case in mind which isn't well-represented by the examples, 55 | please file an issue. 56 | 57 | ## Documentation 58 | 59 | Use [Godoc documentation](http://godoc.org/github.com/streadway/amqp) for 60 | reference and usage. 61 | 62 | [RabbitMQ tutorials in 63 | Go](https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/go) are also 64 | available. 65 | 66 | ## Continuous Integration 67 | 68 | [![Build Status](https://secure.travis-ci.org/streadway/amqp.png)](http://travis-ci.org/streadway/amqp) 69 | 70 | ## Contributing 71 | 72 | Pull requests are very much welcomed. Create your pull request on a non-master 73 | branch, make sure a test or example is included that covers your change and 74 | your commits represent coherent changes that include a reason for the change. 75 | 76 | To run the integration tests, make sure you have RabbitMQ running on any host, 77 | export the environment variable `AMQP_URL=amqp://host/` and run `go test -tags 78 | integration`. TravisCI will also run the integration tests. 79 | 80 | Thanks to the [community of contributors](https://github.com/streadway/amqp/graphs/contributors). 81 | 82 | ## External packages 83 | 84 | * [Google App Engine Dialer support](https://github.com/soundtrackyourbrand/gaeamqp) 85 | * [RabbitMQ examples in Go](https://github.com/rabbitmq/rabbitmq-tutorials/tree/master/go) 86 | 87 | ## License 88 | 89 | BSD 2 clause - see LICENSE for more details. 90 | 91 | 92 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/allocator.go: -------------------------------------------------------------------------------- 1 | package amqp 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "math/big" 7 | ) 8 | 9 | const ( 10 | free = 0 11 | allocated = 1 12 | ) 13 | 14 | // allocator maintains a bitset of allocated numbers. 15 | type allocator struct { 16 | pool *big.Int 17 | last int 18 | low int 19 | high int 20 | } 21 | 22 | // NewAllocator reserves and frees integers out of a range between low and 23 | // high. 24 | // 25 | // O(N) worst case space used, where N is maximum allocated, divided by 26 | // sizeof(big.Word) 27 | func newAllocator(low, high int) *allocator { 28 | return &allocator{ 29 | pool: big.NewInt(0), 30 | last: low, 31 | low: low, 32 | high: high, 33 | } 34 | } 35 | 36 | // String returns a string describing the contents of the allocator like 37 | // "allocator[low..high] reserved..until" 38 | // 39 | // O(N) where N is high-low 40 | func (a allocator) String() string { 41 | b := &bytes.Buffer{} 42 | fmt.Fprintf(b, "allocator[%d..%d]", a.low, a.high) 43 | 44 | for low := a.low; low <= a.high; low++ { 45 | high := low 46 | for a.reserved(high) && high <= a.high { 47 | high++ 48 | } 49 | 50 | if high > low+1 { 51 | fmt.Fprintf(b, " %d..%d", low, high-1) 52 | } else if high > low { 53 | fmt.Fprintf(b, " %d", high-1) 54 | } 55 | 56 | low = high 57 | } 58 | return b.String() 59 | } 60 | 61 | // Next reserves and returns the next available number out of the range between 62 | // low and high. If no number is available, false is returned. 63 | // 64 | // O(N) worst case runtime where N is allocated, but usually O(1) due to a 65 | // rolling index into the oldest allocation. 66 | func (a *allocator) next() (int, bool) { 67 | wrapped := a.last 68 | 69 | // Find trailing bit 70 | for ; a.last <= a.high; a.last++ { 71 | if a.reserve(a.last) { 72 | return a.last, true 73 | } 74 | } 75 | 76 | // Find preceding free'd pool 77 | a.last = a.low 78 | 79 | for ; a.last < wrapped; a.last++ { 80 | if a.reserve(a.last) { 81 | return a.last, true 82 | } 83 | } 84 | 85 | return 0, false 86 | } 87 | 88 | // reserve claims the bit if it is not already claimed, returning true if 89 | // successfully claimed. 90 | func (a *allocator) reserve(n int) bool { 91 | if a.reserved(n) { 92 | return false 93 | } 94 | a.pool.SetBit(a.pool, n-a.low, allocated) 95 | return true 96 | } 97 | 98 | // reserved returns true if the integer has been allocated 99 | func (a *allocator) reserved(n int) bool { 100 | return a.pool.Bit(n-a.low) == allocated 101 | } 102 | 103 | // release frees the use of the number for another allocation 104 | func (a *allocator) release(n int) { 105 | a.pool.SetBit(a.pool, n-a.low, free) 106 | } 107 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/auth.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "fmt" 10 | ) 11 | 12 | // Authentication interface provides a means for different SASL authentication 13 | // mechanisms to be used during connection tuning. 14 | type Authentication interface { 15 | Mechanism() string 16 | Response() string 17 | } 18 | 19 | // PlainAuth is a similar to Basic Auth in HTTP. 20 | type PlainAuth struct { 21 | Username string 22 | Password string 23 | } 24 | 25 | // Mechanism returns "PLAIN" 26 | func (auth *PlainAuth) Mechanism() string { 27 | return "PLAIN" 28 | } 29 | 30 | // Response returns the null character delimited encoding for the SASL PLAIN Mechanism. 31 | func (auth *PlainAuth) Response() string { 32 | return fmt.Sprintf("\000%s\000%s", auth.Username, auth.Password) 33 | } 34 | 35 | // Finds the first mechanism preferred by the client that the server supports. 36 | func pickSASLMechanism(client []Authentication, serverMechanisms []string) (auth Authentication, ok bool) { 37 | for _, auth = range client { 38 | for _, mech := range serverMechanisms { 39 | if auth.Mechanism() == mech { 40 | return auth, true 41 | } 42 | } 43 | } 44 | 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/certs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Creates the CA, server and client certs to be used by tls_test.go 4 | # http://www.rabbitmq.com/ssl.html 5 | # 6 | # Copy stdout into the const section of tls_test.go or use for RabbitMQ 7 | # 8 | root=$PWD/certs 9 | 10 | if [ -f $root/ca/serial ]; then 11 | echo >&2 "Previous installation found" 12 | echo >&2 "Remove $root/ca and rerun to overwrite" 13 | exit 1 14 | fi 15 | 16 | mkdir -p $root/ca/private 17 | mkdir -p $root/ca/certs 18 | mkdir -p $root/server 19 | mkdir -p $root/client 20 | 21 | cd $root/ca 22 | 23 | chmod 700 private 24 | touch index.txt 25 | echo 'unique_subject = no' > index.txt.attr 26 | echo '01' > serial 27 | echo >openssl.cnf ' 28 | [ ca ] 29 | default_ca = testca 30 | 31 | [ testca ] 32 | dir = . 33 | certificate = $dir/cacert.pem 34 | database = $dir/index.txt 35 | new_certs_dir = $dir/certs 36 | private_key = $dir/private/cakey.pem 37 | serial = $dir/serial 38 | 39 | default_crl_days = 7 40 | default_days = 3650 41 | default_md = sha1 42 | 43 | policy = testca_policy 44 | x509_extensions = certificate_extensions 45 | 46 | [ testca_policy ] 47 | commonName = supplied 48 | stateOrProvinceName = optional 49 | countryName = optional 50 | emailAddress = optional 51 | organizationName = optional 52 | organizationalUnitName = optional 53 | 54 | [ certificate_extensions ] 55 | basicConstraints = CA:false 56 | 57 | [ req ] 58 | default_bits = 2048 59 | default_keyfile = ./private/cakey.pem 60 | default_md = sha1 61 | prompt = yes 62 | distinguished_name = root_ca_distinguished_name 63 | x509_extensions = root_ca_extensions 64 | 65 | [ root_ca_distinguished_name ] 66 | commonName = hostname 67 | 68 | [ root_ca_extensions ] 69 | basicConstraints = CA:true 70 | keyUsage = keyCertSign, cRLSign 71 | 72 | [ client_ca_extensions ] 73 | basicConstraints = CA:false 74 | keyUsage = digitalSignature 75 | extendedKeyUsage = 1.3.6.1.5.5.7.3.2 76 | 77 | [ server_ca_extensions ] 78 | basicConstraints = CA:false 79 | keyUsage = keyEncipherment 80 | extendedKeyUsage = 1.3.6.1.5.5.7.3.1 81 | subjectAltName = @alt_names 82 | 83 | [ alt_names ] 84 | IP.1 = 127.0.0.1 85 | ' 86 | 87 | openssl req \ 88 | -x509 \ 89 | -nodes \ 90 | -config openssl.cnf \ 91 | -newkey rsa:2048 \ 92 | -days 3650 \ 93 | -subj "/CN=MyTestCA/" \ 94 | -out cacert.pem \ 95 | -outform PEM 96 | 97 | openssl x509 \ 98 | -in cacert.pem \ 99 | -out cacert.cer \ 100 | -outform DER 101 | 102 | openssl genrsa -out $root/server/key.pem 2048 103 | openssl genrsa -out $root/client/key.pem 2048 104 | 105 | openssl req \ 106 | -new \ 107 | -nodes \ 108 | -config openssl.cnf \ 109 | -subj "/CN=127.0.0.1/O=server/" \ 110 | -key $root/server/key.pem \ 111 | -out $root/server/req.pem \ 112 | -outform PEM 113 | 114 | openssl req \ 115 | -new \ 116 | -nodes \ 117 | -config openssl.cnf \ 118 | -subj "/CN=127.0.0.1/O=client/" \ 119 | -key $root/client/key.pem \ 120 | -out $root/client/req.pem \ 121 | -outform PEM 122 | 123 | openssl ca \ 124 | -config openssl.cnf \ 125 | -in $root/server/req.pem \ 126 | -out $root/server/cert.pem \ 127 | -notext \ 128 | -batch \ 129 | -extensions server_ca_extensions 130 | 131 | openssl ca \ 132 | -config openssl.cnf \ 133 | -in $root/client/req.pem \ 134 | -out $root/client/cert.pem \ 135 | -notext \ 136 | -batch \ 137 | -extensions client_ca_extensions 138 | 139 | cat <<-END 140 | const caCert = \` 141 | `cat $root/ca/cacert.pem` 142 | \` 143 | 144 | const serverCert = \` 145 | `cat $root/server/cert.pem` 146 | \` 147 | 148 | const serverKey = \` 149 | `cat $root/server/key.pem` 150 | \` 151 | 152 | const clientCert = \` 153 | `cat $root/client/cert.pem` 154 | \` 155 | 156 | const clientKey = \` 157 | `cat $root/client/key.pem` 158 | \` 159 | END 160 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/confirms.go: -------------------------------------------------------------------------------- 1 | package amqp 2 | 3 | import "sync" 4 | 5 | // confirms resequences and notifies one or multiple publisher confirmation listeners 6 | type confirms struct { 7 | m sync.Mutex 8 | listeners []chan Confirmation 9 | sequencer map[uint64]Confirmation 10 | published uint64 11 | expecting uint64 12 | } 13 | 14 | // newConfirms allocates a confirms 15 | func newConfirms() *confirms { 16 | return &confirms{ 17 | sequencer: map[uint64]Confirmation{}, 18 | published: 0, 19 | expecting: 1, 20 | } 21 | } 22 | 23 | func (c *confirms) Listen(l chan Confirmation) { 24 | c.m.Lock() 25 | defer c.m.Unlock() 26 | 27 | c.listeners = append(c.listeners, l) 28 | } 29 | 30 | // publish increments the publishing counter 31 | func (c *confirms) Publish() uint64 { 32 | c.m.Lock() 33 | defer c.m.Unlock() 34 | 35 | c.published++ 36 | return c.published 37 | } 38 | 39 | // confirm confirms one publishing, increments the expecting delivery tag, and 40 | // removes bookkeeping for that delivery tag. 41 | func (c *confirms) confirm(confirmation Confirmation) { 42 | delete(c.sequencer, c.expecting) 43 | c.expecting++ 44 | for _, l := range c.listeners { 45 | l <- confirmation 46 | } 47 | } 48 | 49 | // resequence confirms any out of order delivered confirmations 50 | func (c *confirms) resequence() { 51 | for c.expecting <= c.published { 52 | sequenced, found := c.sequencer[c.expecting] 53 | if !found { 54 | return 55 | } 56 | c.confirm(sequenced) 57 | } 58 | } 59 | 60 | // one confirms one publishing and all following in the publishing sequence 61 | func (c *confirms) One(confirmed Confirmation) { 62 | c.m.Lock() 63 | defer c.m.Unlock() 64 | 65 | if c.expecting == confirmed.DeliveryTag { 66 | c.confirm(confirmed) 67 | } else { 68 | c.sequencer[confirmed.DeliveryTag] = confirmed 69 | } 70 | c.resequence() 71 | } 72 | 73 | // multiple confirms all publishings up until the delivery tag 74 | func (c *confirms) Multiple(confirmed Confirmation) { 75 | c.m.Lock() 76 | defer c.m.Unlock() 77 | 78 | for c.expecting <= confirmed.DeliveryTag { 79 | c.confirm(Confirmation{c.expecting, confirmed.Ack}) 80 | } 81 | } 82 | 83 | // Close closes all listeners, discarding any out of sequence confirmations 84 | func (c *confirms) Close() error { 85 | c.m.Lock() 86 | defer c.m.Unlock() 87 | 88 | for _, l := range c.listeners { 89 | close(l) 90 | } 91 | c.listeners = nil 92 | return nil 93 | } 94 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/consumers.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | "sync" 12 | "sync/atomic" 13 | ) 14 | 15 | var consumerSeq uint64 16 | 17 | func uniqueConsumerTag() string { 18 | return fmt.Sprintf("ctag-%s-%d", os.Args[0], atomic.AddUint64(&consumerSeq, 1)) 19 | } 20 | 21 | type consumerBuffers map[string]chan *Delivery 22 | 23 | // Concurrent type that manages the consumerTag -> 24 | // ingress consumerBuffer mapping 25 | type consumers struct { 26 | sync.Mutex 27 | chans consumerBuffers 28 | } 29 | 30 | func makeConsumers() *consumers { 31 | return &consumers{chans: make(consumerBuffers)} 32 | } 33 | 34 | func bufferDeliveries(in chan *Delivery, out chan Delivery) { 35 | var queue []*Delivery 36 | var queueIn = in 37 | 38 | for delivery := range in { 39 | select { 40 | case out <- *delivery: 41 | // delivered immediately while the consumer chan can receive 42 | default: 43 | queue = append(queue, delivery) 44 | } 45 | 46 | for len(queue) > 0 { 47 | select { 48 | case out <- *queue[0]: 49 | queue = queue[1:] 50 | case delivery, open := <-queueIn: 51 | if open { 52 | queue = append(queue, delivery) 53 | } else { 54 | // stop receiving to drain the queue 55 | queueIn = nil 56 | } 57 | } 58 | } 59 | } 60 | 61 | close(out) 62 | } 63 | 64 | // On key conflict, close the previous channel. 65 | func (subs *consumers) add(tag string, consumer chan Delivery) { 66 | subs.Lock() 67 | defer subs.Unlock() 68 | 69 | if prev, found := subs.chans[tag]; found { 70 | close(prev) 71 | } 72 | 73 | in := make(chan *Delivery) 74 | go bufferDeliveries(in, consumer) 75 | 76 | subs.chans[tag] = in 77 | } 78 | 79 | func (subs *consumers) close(tag string) (found bool) { 80 | subs.Lock() 81 | defer subs.Unlock() 82 | 83 | ch, found := subs.chans[tag] 84 | 85 | if found { 86 | delete(subs.chans, tag) 87 | close(ch) 88 | } 89 | 90 | return found 91 | } 92 | 93 | func (subs *consumers) closeAll() { 94 | subs.Lock() 95 | defer subs.Unlock() 96 | 97 | for _, ch := range subs.chans { 98 | close(ch) 99 | } 100 | 101 | subs.chans = make(consumerBuffers) 102 | } 103 | 104 | // Sends a delivery to a the consumer identified by `tag`. 105 | // If unbuffered channels are used for Consume this method 106 | // could block all deliveries until the consumer 107 | // receives on the other end of the channel. 108 | func (subs *consumers) send(tag string, msg *Delivery) bool { 109 | subs.Lock() 110 | defer subs.Unlock() 111 | 112 | buffer, found := subs.chans[tag] 113 | if found { 114 | buffer <- msg 115 | } 116 | 117 | return found 118 | } 119 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/delivery.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "errors" 10 | "time" 11 | ) 12 | 13 | var errDeliveryNotInitialized = errors.New("delivery not initialized") 14 | 15 | // Acknowledger notifies the server of successful or failed consumption of 16 | // delivieries via identifier found in the Delivery.DeliveryTag field. 17 | // 18 | // Applications can provide mock implementations in tests of Delivery handlers. 19 | type Acknowledger interface { 20 | Ack(tag uint64, multiple bool) error 21 | Nack(tag uint64, multiple bool, requeue bool) error 22 | Reject(tag uint64, requeue bool) error 23 | } 24 | 25 | // Delivery captures the fields for a previously delivered message resident in 26 | // a queue to be delivered by the server to a consumer from Channel.Consume or 27 | // Channel.Get. 28 | type Delivery struct { 29 | Acknowledger Acknowledger // the channel from which this delivery arrived 30 | 31 | Headers Table // Application or header exchange table 32 | 33 | // Properties 34 | ContentType string // MIME content type 35 | ContentEncoding string // MIME content encoding 36 | DeliveryMode uint8 // queue implementation use - non-persistent (1) or persistent (2) 37 | Priority uint8 // queue implementation use - 0 to 9 38 | CorrelationId string // application use - correlation identifier 39 | ReplyTo string // application use - address to to reply to (ex: RPC) 40 | Expiration string // implementation use - message expiration spec 41 | MessageId string // application use - message identifier 42 | Timestamp time.Time // application use - message timestamp 43 | Type string // application use - message type name 44 | UserId string // application use - creating user - should be authenticated user 45 | AppId string // application use - creating application id 46 | 47 | // Valid only with Channel.Consume 48 | ConsumerTag string 49 | 50 | // Valid only with Channel.Get 51 | MessageCount uint32 52 | 53 | DeliveryTag uint64 54 | Redelivered bool 55 | Exchange string // basic.publish exhange 56 | RoutingKey string // basic.publish routing key 57 | 58 | Body []byte 59 | } 60 | 61 | func newDelivery(channel *Channel, msg messageWithContent) *Delivery { 62 | props, body := msg.getContent() 63 | 64 | delivery := Delivery{ 65 | Acknowledger: channel, 66 | 67 | Headers: props.Headers, 68 | ContentType: props.ContentType, 69 | ContentEncoding: props.ContentEncoding, 70 | DeliveryMode: props.DeliveryMode, 71 | Priority: props.Priority, 72 | CorrelationId: props.CorrelationId, 73 | ReplyTo: props.ReplyTo, 74 | Expiration: props.Expiration, 75 | MessageId: props.MessageId, 76 | Timestamp: props.Timestamp, 77 | Type: props.Type, 78 | UserId: props.UserId, 79 | AppId: props.AppId, 80 | 81 | Body: body, 82 | } 83 | 84 | // Properties for the delivery types 85 | switch m := msg.(type) { 86 | case *basicDeliver: 87 | delivery.ConsumerTag = m.ConsumerTag 88 | delivery.DeliveryTag = m.DeliveryTag 89 | delivery.Redelivered = m.Redelivered 90 | delivery.Exchange = m.Exchange 91 | delivery.RoutingKey = m.RoutingKey 92 | 93 | case *basicGetOk: 94 | delivery.MessageCount = m.MessageCount 95 | delivery.DeliveryTag = m.DeliveryTag 96 | delivery.Redelivered = m.Redelivered 97 | delivery.Exchange = m.Exchange 98 | delivery.RoutingKey = m.RoutingKey 99 | } 100 | 101 | return &delivery 102 | } 103 | 104 | /* 105 | Ack delegates an acknowledgement through the Acknowledger interface that the 106 | client or server has finished work on a delivery. 107 | 108 | All deliveries in AMQP must be acknowledged. If you called Channel.Consume 109 | with autoAck true then the server will be automatically ack each message and 110 | this method should not be called. Otherwise, you must call Delivery.Ack after 111 | you have successfully processed this delivery. 112 | 113 | When multiple is true, this delivery and all prior unacknowledged deliveries 114 | on the same channel will be acknowledged. This is useful for batch processing 115 | of deliveries. 116 | 117 | An error will indicate that the acknowledge could not be delivered to the 118 | channel it was sent from. 119 | 120 | Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every 121 | delivery that is not automatically acknowledged. 122 | */ 123 | func (d Delivery) Ack(multiple bool) error { 124 | if d.Acknowledger == nil { 125 | return errDeliveryNotInitialized 126 | } 127 | return d.Acknowledger.Ack(d.DeliveryTag, multiple) 128 | } 129 | 130 | /* 131 | Reject delegates a negatively acknowledgement through the Acknowledger interface. 132 | 133 | When requeue is true, queue this message to be delivered to a consumer on a 134 | different channel. When requeue is false or the server is unable to queue this 135 | message, it will be dropped. 136 | 137 | If you are batch processing deliveries, and your server supports it, prefer 138 | Delivery.Nack. 139 | 140 | Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every 141 | delivery that is not automatically acknowledged. 142 | */ 143 | func (d Delivery) Reject(requeue bool) error { 144 | if d.Acknowledger == nil { 145 | return errDeliveryNotInitialized 146 | } 147 | return d.Acknowledger.Reject(d.DeliveryTag, requeue) 148 | } 149 | 150 | /* 151 | Nack negatively acknowledge the delivery of message(s) identified by the 152 | delivery tag from either the client or server. 153 | 154 | When multiple is true, nack messages up to and including delivered messages up 155 | until the delivery tag delivered on the same channel. 156 | 157 | When requeue is true, request the server to deliver this message to a different 158 | consumer. If it is not possible or requeue is false, the message will be 159 | dropped or delivered to a server configured dead-letter queue. 160 | 161 | This method must not be used to select or requeue messages the client wishes 162 | not to handle, rather it is to inform the server that the client is incapable 163 | of handling this message at this time. 164 | 165 | Either Delivery.Ack, Delivery.Reject or Delivery.Nack must be called for every 166 | delivery that is not automatically acknowledged. 167 | */ 168 | func (d Delivery) Nack(multiple, requeue bool) error { 169 | if d.Acknowledger == nil { 170 | return errDeliveryNotInitialized 171 | } 172 | return d.Acknowledger.Nack(d.DeliveryTag, multiple, requeue) 173 | } 174 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | /* 7 | Package amqp is an AMQP 0.9.1 client with RabbitMQ extensions 8 | 9 | Understand the AMQP 0.9.1 messaging model by reviewing these links first. Much 10 | of the terminology in this library directly relates to AMQP concepts. 11 | 12 | Resources 13 | 14 | http://www.rabbitmq.com/tutorials/amqp-concepts.html 15 | http://www.rabbitmq.com/getstarted.html 16 | http://www.rabbitmq.com/amqp-0-9-1-reference.html 17 | 18 | Design 19 | 20 | Most other broker clients publish to queues, but in AMQP, clients publish 21 | Exchanges instead. AMQP is programmable, meaning that both the producers and 22 | consumers agree on the configuration of the broker, instead requiring an 23 | operator or system configuration that declares the logical topology in the 24 | broker. The routing between producers and consumer queues is via Bindings. 25 | These bindings form the logical topology of the broker. 26 | 27 | In this library, a message sent from publisher is called a "Publishing" and a 28 | message received to a consumer is called a "Delivery". The fields of 29 | Publishings and Deliveries are close but not exact mappings to the underlying 30 | wire format to maintain stronger types. Many other libraries will combine 31 | message properties with message headers. In this library, the message well 32 | known properties are strongly typed fields on the Publishings and Deliveries, 33 | whereas the user defined headers are in the Headers field. 34 | 35 | The method naming closely matches the protocol's method name with positional 36 | parameters mapping to named protocol message fields. The motivation here is to 37 | present a comprehensive view over all possible interactions with the server. 38 | 39 | Generally, methods that map to protocol methods of the "basic" class will be 40 | elided in this interface, and "select" methods of various channel mode selectors 41 | will be elided for example Channel.Confirm and Channel.Tx. 42 | 43 | The library is intentionally designed to be synchronous, where responses for 44 | each protocol message are required to be received in an RPC manner. Some 45 | methods have a noWait parameter like Channel.QueueDeclare, and some methods are 46 | asynchronous like Channel.Publish. The error values should still be checked for 47 | these methods as they will indicate IO failures like when the underlying 48 | connection closes. 49 | 50 | Asynchronous Events 51 | 52 | Clients of this library may be interested in receiving some of the protocol 53 | messages other than Deliveries like basic.ack methods while a channel is in 54 | confirm mode. 55 | 56 | The Notify* methods with Connection and Channel receivers model the pattern of 57 | asynchronous events like closes due to exceptions, or messages that are sent out 58 | of band from an RPC call like basic.ack or basic.flow. 59 | 60 | Any asynchronous events, including Deliveries and Publishings must always have 61 | a receiver until the corresponding chans are closed. Without asynchronous 62 | receivers, the sychronous methods will block. 63 | 64 | Use Case 65 | 66 | It's important as a client to an AMQP topology to ensure the state of the 67 | broker matches your expectations. For both publish and consume use cases, 68 | make sure you declare the queues, exchanges and bindings you expect to exist 69 | prior to calling Channel.Publish or Channel.Consume. 70 | 71 | // Connections start with amqp.Dial() typically from a command line argument 72 | // or environment variable. 73 | connection, err := amqp.Dial(os.Getenv("AMQP_URL")) 74 | 75 | // To cleanly shutdown by flushing kernel buffers, make sure to close and 76 | // wait for the response. 77 | defer connection.Close() 78 | 79 | // Most operations happen on a channel. If any error is returned on a 80 | // channel, the channel will no longer be valid, throw it away and try with 81 | // a different channel. If you use many channels, it's useful for the 82 | // server to 83 | channel, err := connection.Channel() 84 | 85 | // Declare your topology here, if it doesn't exist, it will be created, if 86 | // it existed already and is not what you expect, then that's considered an 87 | // error. 88 | 89 | // Use your connection on this topology with either Publish or Consume, or 90 | // inspect your queues with QueueInspect. It's unwise to mix Publish and 91 | // Consume to let TCP do its job well. 92 | 93 | SSL/TLS - Secure connections 94 | 95 | When Dial encounters an amqps:// scheme, it will use the zero value of a 96 | tls.Config. This will only perform server certificate and host verification. 97 | 98 | Use DialTLS when you wish to provide a client certificate (recommended), 99 | include a private certificate authority's certificate in the cert chain for 100 | server validity, or run insecure by not verifying the server certificate dial 101 | your own connection. DialTLS will use the provided tls.Config when it 102 | encounters an amqps:// scheme and will dial a plain connection when it 103 | encounters an amqp:// scheme. 104 | 105 | SSL/TLS in RabbitMQ is documented here: http://www.rabbitmq.com/ssl.html 106 | 107 | */ 108 | package amqp 109 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package amqp 4 | 5 | import "bytes" 6 | 7 | func Fuzz(data []byte) int { 8 | r := reader{bytes.NewReader(data)} 9 | frame, err := r.ReadFrame() 10 | if err != nil { 11 | if frame != nil { 12 | panic("frame is not nil") 13 | } 14 | return 0 15 | } 16 | return 1 17 | } 18 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | go run spec/gen.go < spec/amqp0-9-1.stripped.extended.xml | gofmt > spec091.go 3 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | GOFMT_FILES=$(gofmt -l .) 4 | if [ -n "${GOFMT_FILES}" ]; then 5 | printf >&2 'gofmt failed for the following files:\n%s\n\nplease run "gofmt -w ." on your changes before committing.\n' "${GOFMT_FILES}" 6 | exit 1 7 | fi 8 | 9 | GOLINT_ERRORS=$(golint ./... | grep -v "Id should be") 10 | if [ -n "${GOLINT_ERRORS}" ]; then 11 | printf >&2 'golint failed for the following reasons:\n%s\n\nplease run 'golint ./...' on your changes before committing.\n' "${GOLINT_ERRORS}" 12 | exit 1 13 | fi 14 | 15 | GOVET_ERRORS=$(go tool vet -copylocks=false *.go 2>&1) 16 | if [ -n "${GOVET_ERRORS}" ]; then 17 | printf >&2 'go vet failed for the following reasons:\n%s\n\nplease run "go tool vet *.go" on your changes before committing.\n' "${GOVET_ERRORS}" 18 | exit 1 19 | fi 20 | 21 | if [ -z "${NOTEST}" ]; then 22 | printf >&2 'Running short tests...\n' 23 | env AMQP_URL= go test -short -v | egrep 'PASS|ok' 24 | 25 | if [ $? -ne 0 ]; then 26 | printf >&2 'go test failed, please fix before committing.\n' 27 | exit 1 28 | fi 29 | fi 30 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/return.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "time" 10 | ) 11 | 12 | // Return captures a flattened struct of fields returned by the server when a 13 | // Publishing is unable to be delivered either due to the `mandatory` flag set 14 | // and no route found, or `immediate` flag set and no free consumer. 15 | type Return struct { 16 | ReplyCode uint16 // reason 17 | ReplyText string // description 18 | Exchange string // basic.publish exchange 19 | RoutingKey string // basic.publish routing key 20 | 21 | // Properties 22 | ContentType string // MIME content type 23 | ContentEncoding string // MIME content encoding 24 | Headers Table // Application or header exchange table 25 | DeliveryMode uint8 // queue implementation use - non-persistent (1) or persistent (2) 26 | Priority uint8 // queue implementation use - 0 to 9 27 | CorrelationId string // application use - correlation identifier 28 | ReplyTo string // application use - address to to reply to (ex: RPC) 29 | Expiration string // implementation use - message expiration spec 30 | MessageId string // application use - message identifier 31 | Timestamp time.Time // application use - message timestamp 32 | Type string // application use - message type name 33 | UserId string // application use - creating user id 34 | AppId string // application use - creating application 35 | 36 | Body []byte 37 | } 38 | 39 | func newReturn(msg basicReturn) *Return { 40 | props, body := msg.getContent() 41 | 42 | return &Return{ 43 | ReplyCode: msg.ReplyCode, 44 | ReplyText: msg.ReplyText, 45 | Exchange: msg.Exchange, 46 | RoutingKey: msg.RoutingKey, 47 | 48 | Headers: props.Headers, 49 | ContentType: props.ContentType, 50 | ContentEncoding: props.ContentEncoding, 51 | DeliveryMode: props.DeliveryMode, 52 | Priority: props.Priority, 53 | CorrelationId: props.CorrelationId, 54 | ReplyTo: props.ReplyTo, 55 | Expiration: props.Expiration, 56 | MessageId: props.MessageId, 57 | Timestamp: props.Timestamp, 58 | Type: props.Type, 59 | UserId: props.UserId, 60 | AppId: props.AppId, 61 | 62 | Body: body, 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /vendor/github.com/streadway/amqp/uri.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012, Sean Treadway, SoundCloud Ltd. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | // Source code and contact info at http://github.com/streadway/amqp 5 | 6 | package amqp 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | "net/url" 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | var errURIScheme = errors.New("AMQP scheme must be either 'amqp://' or 'amqps://'") 17 | 18 | var schemePorts = map[string]int{ 19 | "amqp": 5672, 20 | "amqps": 5671, 21 | } 22 | 23 | var defaultURI = URI{ 24 | Scheme: "amqp", 25 | Host: "localhost", 26 | Port: 5672, 27 | Username: "guest", 28 | Password: "guest", 29 | Vhost: "/", 30 | } 31 | 32 | // URI represents a parsed AMQP URI string. 33 | type URI struct { 34 | Scheme string 35 | Host string 36 | Port int 37 | Username string 38 | Password string 39 | Vhost string 40 | } 41 | 42 | // ParseURI attempts to parse the given AMQP URI according to the spec. 43 | // See http://www.rabbitmq.com/uri-spec.html. 44 | // 45 | // Default values for the fields are: 46 | // 47 | // Scheme: amqp 48 | // Host: localhost 49 | // Port: 5672 50 | // Username: guest 51 | // Password: guest 52 | // Vhost: / 53 | // 54 | func ParseURI(uri string) (URI, error) { 55 | builder := defaultURI 56 | 57 | u, err := url.Parse(uri) 58 | if err != nil { 59 | return builder, err 60 | } 61 | 62 | defaultPort, okScheme := schemePorts[u.Scheme] 63 | 64 | if okScheme { 65 | builder.Scheme = u.Scheme 66 | } else { 67 | return builder, errURIScheme 68 | } 69 | 70 | host, port := splitHostPort(u.Host) 71 | 72 | if host != "" { 73 | builder.Host = host 74 | } 75 | 76 | if port != "" { 77 | port32, err := strconv.ParseInt(port, 10, 32) 78 | if err != nil { 79 | return builder, err 80 | } 81 | builder.Port = int(port32) 82 | } else { 83 | builder.Port = defaultPort 84 | } 85 | 86 | if u.User != nil { 87 | builder.Username = u.User.Username() 88 | if password, ok := u.User.Password(); ok { 89 | builder.Password = password 90 | } 91 | } 92 | 93 | if u.Path != "" { 94 | if strings.HasPrefix(u.Path, "/") { 95 | if u.Host == "" && strings.HasPrefix(u.Path, "///") { 96 | // net/url doesn't handle local context authorities and leaves that up 97 | // to the scheme handler. In our case, we translate amqp:/// into the 98 | // default host and whatever the vhost should be 99 | if len(u.Path) > 3 { 100 | builder.Vhost = u.Path[3:] 101 | } 102 | } else if len(u.Path) > 1 { 103 | builder.Vhost = u.Path[1:] 104 | } 105 | } else { 106 | builder.Vhost = u.Path 107 | } 108 | } 109 | 110 | return builder, nil 111 | } 112 | 113 | // Splits host:port, host, [ho:st]:port, or [ho:st]. Unlike net.SplitHostPort 114 | // which splits :port, host:port or [host]:port 115 | // 116 | // Handles hosts that have colons that are in brackets like [::1]:http 117 | func splitHostPort(addr string) (host, port string) { 118 | i := strings.LastIndex(addr, ":") 119 | 120 | if i >= 0 { 121 | host, port = addr[:i], addr[i+1:] 122 | 123 | if len(port) > 0 && port[len(port)-1] == ']' && addr[0] == '[' { 124 | // we've split on an inner colon, the port was missing outside of the 125 | // brackets so use the full addr. We could assert that host should not 126 | // contain any colons here 127 | host, port = addr, "" 128 | } 129 | } else { 130 | host = addr 131 | } 132 | 133 | return 134 | } 135 | 136 | // PlainAuth returns a PlainAuth structure based on the parsed URI's 137 | // Username and Password fields. 138 | func (uri URI) PlainAuth() *PlainAuth { 139 | return &PlainAuth{ 140 | Username: uri.Username, 141 | Password: uri.Password, 142 | } 143 | } 144 | 145 | func (uri URI) String() string { 146 | var authority string 147 | 148 | if uri.Username != defaultURI.Username || uri.Password != defaultURI.Password { 149 | authority += uri.Username 150 | 151 | if uri.Password != defaultURI.Password { 152 | authority += ":" + uri.Password 153 | } 154 | 155 | authority += "@" 156 | } 157 | 158 | authority += uri.Host 159 | 160 | if defaultPort, found := schemePorts[uri.Scheme]; !found || defaultPort != uri.Port { 161 | authority += ":" + strconv.FormatInt(int64(uri.Port), 10) 162 | } 163 | 164 | var vhost string 165 | if uri.Vhost != defaultURI.Vhost { 166 | vhost = uri.Vhost 167 | } 168 | 169 | return fmt.Sprintf("%s://%s/%s", uri.Scheme, authority, url.QueryEscape(vhost)) 170 | } 171 | -------------------------------------------------------------------------------- /vendor/github.com/toolkits/file/downloader.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "io" 5 | "net/http" 6 | "os" 7 | ) 8 | 9 | func Download(toFile, url string) error { 10 | out, err := os.Create(toFile) 11 | if err != nil { 12 | return err 13 | } 14 | 15 | defer out.Close() 16 | 17 | resp, err := http.Get(url) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | defer resp.Body.Close() 23 | 24 | _, err = io.Copy(out, resp.Body) 25 | return err 26 | } 27 | -------------------------------------------------------------------------------- /vendor/github.com/toolkits/file/file.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "path" 9 | "path/filepath" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | // SelfPath gets compiled executable file absolute path 15 | func SelfPath() string { 16 | path, _ := filepath.Abs(os.Args[0]) 17 | return path 18 | } 19 | 20 | // get absolute filepath, based on built executable file 21 | func RealPath(fp string) (string, error) { 22 | if path.IsAbs(fp) { 23 | return fp, nil 24 | } 25 | wd, err := os.Getwd() 26 | return path.Join(wd, fp), err 27 | } 28 | 29 | // SelfDir gets compiled executable file directory 30 | func SelfDir() string { 31 | return filepath.Dir(SelfPath()) 32 | } 33 | 34 | // get filepath base name 35 | func Basename(fp string) string { 36 | return path.Base(fp) 37 | } 38 | 39 | // get filepath dir name 40 | func Dir(fp string) string { 41 | return path.Dir(fp) 42 | } 43 | 44 | func InsureDir(fp string) error { 45 | if IsExist(fp) { 46 | return nil 47 | } 48 | return os.MkdirAll(fp, os.ModePerm) 49 | } 50 | 51 | // mkdir dir if not exist 52 | func EnsureDir(fp string) error { 53 | return os.MkdirAll(fp, os.ModePerm) 54 | } 55 | 56 | // ensure the datadir and make sure it's rw-able 57 | func EnsureDirRW(dataDir string) error { 58 | err := EnsureDir(dataDir) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | checkFile := fmt.Sprintf("%s/rw.%d", dataDir, time.Now().UnixNano()) 64 | fd, err := Create(checkFile) 65 | if err != nil { 66 | if os.IsPermission(err) { 67 | return fmt.Errorf("open %s: rw permission denied", dataDir) 68 | } 69 | return err 70 | } 71 | Close(fd) 72 | Remove(checkFile) 73 | 74 | return nil 75 | } 76 | 77 | // create one file 78 | func Create(name string) (*os.File, error) { 79 | return os.Create(name) 80 | } 81 | 82 | // remove one file 83 | func Remove(name string) error { 84 | return os.Remove(name) 85 | } 86 | 87 | // close fd 88 | func Close(fd *os.File) error { 89 | return fd.Close() 90 | } 91 | 92 | func Ext(fp string) string { 93 | return path.Ext(fp) 94 | } 95 | 96 | // rename file name 97 | func Rename(src string, target string) error { 98 | return os.Rename(src, target) 99 | } 100 | 101 | // delete file 102 | func Unlink(fp string) error { 103 | return os.Remove(fp) 104 | } 105 | 106 | // IsFile checks whether the path is a file, 107 | // it returns false when it's a directory or does not exist. 108 | func IsFile(fp string) bool { 109 | f, e := os.Stat(fp) 110 | if e != nil { 111 | return false 112 | } 113 | return !f.IsDir() 114 | } 115 | 116 | // IsExist checks whether a file or directory exists. 117 | // It returns false when the file or directory does not exist. 118 | func IsExist(fp string) bool { 119 | _, err := os.Stat(fp) 120 | return err == nil || os.IsExist(err) 121 | } 122 | 123 | // Search a file in paths. 124 | // this is often used in search config file in /etc ~/ 125 | func SearchFile(filename string, paths ...string) (fullPath string, err error) { 126 | for _, path := range paths { 127 | if fullPath = filepath.Join(path, filename); IsExist(fullPath) { 128 | return 129 | } 130 | } 131 | err = fmt.Errorf("%s not found in paths", fullPath) 132 | return 133 | } 134 | 135 | // get file modified time 136 | func FileMTime(fp string) (int64, error) { 137 | f, e := os.Stat(fp) 138 | if e != nil { 139 | return 0, e 140 | } 141 | return f.ModTime().Unix(), nil 142 | } 143 | 144 | // get file size as how many bytes 145 | func FileSize(fp string) (int64, error) { 146 | f, e := os.Stat(fp) 147 | if e != nil { 148 | return 0, e 149 | } 150 | return f.Size(), nil 151 | } 152 | 153 | // list dirs under dirPath 154 | func DirsUnder(dirPath string) ([]string, error) { 155 | if !IsExist(dirPath) { 156 | return []string{}, nil 157 | } 158 | 159 | fs, err := ioutil.ReadDir(dirPath) 160 | if err != nil { 161 | return []string{}, err 162 | } 163 | 164 | sz := len(fs) 165 | if sz == 0 { 166 | return []string{}, nil 167 | } 168 | 169 | ret := make([]string, 0, sz) 170 | for i := 0; i < sz; i++ { 171 | if fs[i].IsDir() { 172 | name := fs[i].Name() 173 | if name != "." && name != ".." { 174 | ret = append(ret, name) 175 | } 176 | } 177 | } 178 | 179 | return ret, nil 180 | } 181 | 182 | // list files under dirPath 183 | func FilesUnder(dirPath string) ([]string, error) { 184 | if !IsExist(dirPath) { 185 | return []string{}, nil 186 | } 187 | 188 | fs, err := ioutil.ReadDir(dirPath) 189 | if err != nil { 190 | return []string{}, err 191 | } 192 | 193 | sz := len(fs) 194 | if sz == 0 { 195 | return []string{}, nil 196 | } 197 | 198 | ret := make([]string, 0, sz) 199 | for i := 0; i < sz; i++ { 200 | if !fs[i].IsDir() { 201 | ret = append(ret, fs[i].Name()) 202 | } 203 | } 204 | 205 | return ret, nil 206 | } 207 | 208 | func MustOpenLogFile(fp string) *os.File { 209 | if strings.Contains(fp, "/") { 210 | dir := Dir(fp) 211 | err := EnsureDir(dir) 212 | if err != nil { 213 | log.Fatalf("mkdir -p %s occur error %v", dir, err) 214 | } 215 | } 216 | 217 | f, err := os.OpenFile(fp, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) 218 | if err != nil { 219 | log.Fatalf("open %s occur error %v", fp, err) 220 | } 221 | 222 | return f 223 | } 224 | -------------------------------------------------------------------------------- /vendor/github.com/toolkits/file/reader.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "bufio" 5 | "io/ioutil" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | func ToBytes(filePath string) ([]byte, error) { 11 | return ioutil.ReadFile(filePath) 12 | } 13 | 14 | func ToString(filePath string) (string, error) { 15 | b, err := ioutil.ReadFile(filePath) 16 | if err != nil { 17 | return "", err 18 | } 19 | return string(b), nil 20 | } 21 | 22 | func ToTrimString(filePath string) (string, error) { 23 | str, err := ToString(filePath) 24 | if err != nil { 25 | return "", err 26 | } 27 | 28 | return strings.TrimSpace(str), nil 29 | } 30 | 31 | func ToUint64(filePath string) (uint64, error) { 32 | content, err := ToTrimString(filePath) 33 | if err != nil { 34 | return 0, err 35 | } 36 | 37 | var ret uint64 38 | if ret, err = strconv.ParseUint(content, 10, 64); err != nil { 39 | return 0, err 40 | } 41 | return ret, nil 42 | } 43 | 44 | func ToInt64(filePath string) (int64, error) { 45 | content, err := ToTrimString(filePath) 46 | if err != nil { 47 | return 0, err 48 | } 49 | 50 | var ret int64 51 | if ret, err = strconv.ParseInt(content, 10, 64); err != nil { 52 | return 0, err 53 | } 54 | return ret, nil 55 | } 56 | 57 | func ReadLine(r *bufio.Reader) ([]byte, error) { 58 | line, isPrefix, err := r.ReadLine() 59 | for isPrefix && err == nil { 60 | var bs []byte 61 | bs, isPrefix, err = r.ReadLine() 62 | line = append(line, bs...) 63 | } 64 | 65 | return line, err 66 | } 67 | -------------------------------------------------------------------------------- /vendor/github.com/toolkits/file/writer.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "os" 5 | "path" 6 | ) 7 | 8 | func WriteBytes(filePath string, b []byte) (int, error) { 9 | os.MkdirAll(path.Dir(filePath), os.ModePerm) 10 | fw, err := os.Create(filePath) 11 | if err != nil { 12 | return 0, err 13 | } 14 | defer fw.Close() 15 | return fw.Write(b) 16 | } 17 | 18 | func WriteString(filePath string, s string) (int, error) { 19 | return WriteBytes(filePath, []byte(s)) 20 | } 21 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.4 5 | - 1.5 6 | - 1.6 7 | - 1.7 8 | - 1.8 9 | - 1.9 10 | - tip 11 | 12 | go_import_path: gopkg.in/yaml.v2 13 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/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/gopkg.in/yaml.v2/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2011-2016 Canonical Ltd. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/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 supports most of YAML 1.1 and 1.2, including support for 16 | anchors, tags, map merging, etc. Multi-document unmarshalling is not yet 17 | implemented, and base-60 floats from YAML 1.1 are purposefully not 18 | supported since they're a poor design and are gone in YAML 1.2. 19 | 20 | Installation and usage 21 | ---------------------- 22 | 23 | The import path for the package is *gopkg.in/yaml.v2*. 24 | 25 | To install it, run: 26 | 27 | go get gopkg.in/yaml.v2 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.v2](https://gopkg.in/yaml.v2) 35 | 36 | API stability 37 | ------------- 38 | 39 | The package API for yaml v2 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 Apache License 2.0. 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.v2" 59 | ) 60 | 61 | var data = ` 62 | a: Easy! 63 | b: 64 | c: 2 65 | d: [3, 4] 66 | ` 67 | 68 | // Note: struct fields must be public in order for unmarshal to 69 | // correctly populate the data. 70 | type T struct { 71 | A string 72 | B struct { 73 | RenamedC int `yaml:"c"` 74 | D []int `yaml:",flow"` 75 | } 76 | } 77 | 78 | func main() { 79 | t := T{} 80 | 81 | err := yaml.Unmarshal([]byte(data), &t) 82 | if err != nil { 83 | log.Fatalf("error: %v", err) 84 | } 85 | fmt.Printf("--- t:\n%v\n\n", t) 86 | 87 | d, err := yaml.Marshal(&t) 88 | if err != nil { 89 | log.Fatalf("error: %v", err) 90 | } 91 | fmt.Printf("--- t dump:\n%s\n\n", string(d)) 92 | 93 | m := make(map[interface{}]interface{}) 94 | 95 | err = yaml.Unmarshal([]byte(data), &m) 96 | if err != nil { 97 | log.Fatalf("error: %v", err) 98 | } 99 | fmt.Printf("--- m:\n%v\n\n", m) 100 | 101 | d, err = yaml.Marshal(&m) 102 | if err != nil { 103 | log.Fatalf("error: %v", err) 104 | } 105 | fmt.Printf("--- m dump:\n%s\n\n", string(d)) 106 | } 107 | ``` 108 | 109 | This example will generate the following output: 110 | 111 | ``` 112 | --- t: 113 | {Easy! {2 [3 4]}} 114 | 115 | --- t dump: 116 | a: Easy! 117 | b: 118 | c: 2 119 | d: [3, 4] 120 | 121 | 122 | --- m: 123 | map[a:Easy! b:map[c:2 d:[3 4]]] 124 | 125 | --- m dump: 126 | a: Easy! 127 | b: 128 | c: 2 129 | d: 130 | - 3 131 | - 4 132 | ``` 133 | 134 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/go.mod: -------------------------------------------------------------------------------- 1 | module "gopkg.in/yaml.v2" 2 | 3 | require ( 4 | "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 5 | ) 6 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/resolve.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "encoding/base64" 5 | "math" 6 | "regexp" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | type resolveMapItem struct { 13 | value interface{} 14 | tag string 15 | } 16 | 17 | var resolveTable = make([]byte, 256) 18 | var resolveMap = make(map[string]resolveMapItem) 19 | 20 | func init() { 21 | t := resolveTable 22 | t[int('+')] = 'S' // Sign 23 | t[int('-')] = 'S' 24 | for _, c := range "0123456789" { 25 | t[int(c)] = 'D' // Digit 26 | } 27 | for _, c := range "yYnNtTfFoO~" { 28 | t[int(c)] = 'M' // In map 29 | } 30 | t[int('.')] = '.' // Float (potentially in map) 31 | 32 | var resolveMapList = []struct { 33 | v interface{} 34 | tag string 35 | l []string 36 | }{ 37 | {true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, 38 | {true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, 39 | {true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, 40 | {false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, 41 | {false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, 42 | {false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, 43 | {nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, 44 | {math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, 45 | {math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, 46 | {math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, 47 | {math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, 48 | {"<<", yaml_MERGE_TAG, []string{"<<"}}, 49 | } 50 | 51 | m := resolveMap 52 | for _, item := range resolveMapList { 53 | for _, s := range item.l { 54 | m[s] = resolveMapItem{item.v, item.tag} 55 | } 56 | } 57 | } 58 | 59 | const longTagPrefix = "tag:yaml.org,2002:" 60 | 61 | func shortTag(tag string) string { 62 | // TODO This can easily be made faster and produce less garbage. 63 | if strings.HasPrefix(tag, longTagPrefix) { 64 | return "!!" + tag[len(longTagPrefix):] 65 | } 66 | return tag 67 | } 68 | 69 | func longTag(tag string) string { 70 | if strings.HasPrefix(tag, "!!") { 71 | return longTagPrefix + tag[2:] 72 | } 73 | return tag 74 | } 75 | 76 | func resolvableTag(tag string) bool { 77 | switch tag { 78 | case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG: 79 | return true 80 | } 81 | return false 82 | } 83 | 84 | var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`) 85 | 86 | func resolve(tag string, in string) (rtag string, out interface{}) { 87 | if !resolvableTag(tag) { 88 | return tag, in 89 | } 90 | 91 | defer func() { 92 | switch tag { 93 | case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: 94 | return 95 | case yaml_FLOAT_TAG: 96 | if rtag == yaml_INT_TAG { 97 | switch v := out.(type) { 98 | case int64: 99 | rtag = yaml_FLOAT_TAG 100 | out = float64(v) 101 | return 102 | case int: 103 | rtag = yaml_FLOAT_TAG 104 | out = float64(v) 105 | return 106 | } 107 | } 108 | } 109 | failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) 110 | }() 111 | 112 | // Any data is accepted as a !!str or !!binary. 113 | // Otherwise, the prefix is enough of a hint about what it might be. 114 | hint := byte('N') 115 | if in != "" { 116 | hint = resolveTable[in[0]] 117 | } 118 | if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { 119 | // Handle things we can lookup in a map. 120 | if item, ok := resolveMap[in]; ok { 121 | return item.tag, item.value 122 | } 123 | 124 | // Base 60 floats are a bad idea, were dropped in YAML 1.2, and 125 | // are purposefully unsupported here. They're still quoted on 126 | // the way out for compatibility with other parser, though. 127 | 128 | switch hint { 129 | case 'M': 130 | // We've already checked the map above. 131 | 132 | case '.': 133 | // Not in the map, so maybe a normal float. 134 | floatv, err := strconv.ParseFloat(in, 64) 135 | if err == nil { 136 | return yaml_FLOAT_TAG, floatv 137 | } 138 | 139 | case 'D', 'S': 140 | // Int, float, or timestamp. 141 | // Only try values as a timestamp if the value is unquoted or there's an explicit 142 | // !!timestamp tag. 143 | if tag == "" || tag == yaml_TIMESTAMP_TAG { 144 | t, ok := parseTimestamp(in) 145 | if ok { 146 | return yaml_TIMESTAMP_TAG, t 147 | } 148 | } 149 | 150 | plain := strings.Replace(in, "_", "", -1) 151 | intv, err := strconv.ParseInt(plain, 0, 64) 152 | if err == nil { 153 | if intv == int64(int(intv)) { 154 | return yaml_INT_TAG, int(intv) 155 | } else { 156 | return yaml_INT_TAG, intv 157 | } 158 | } 159 | uintv, err := strconv.ParseUint(plain, 0, 64) 160 | if err == nil { 161 | return yaml_INT_TAG, uintv 162 | } 163 | if yamlStyleFloat.MatchString(plain) { 164 | floatv, err := strconv.ParseFloat(plain, 64) 165 | if err == nil { 166 | return yaml_FLOAT_TAG, floatv 167 | } 168 | } 169 | if strings.HasPrefix(plain, "0b") { 170 | intv, err := strconv.ParseInt(plain[2:], 2, 64) 171 | if err == nil { 172 | if intv == int64(int(intv)) { 173 | return yaml_INT_TAG, int(intv) 174 | } else { 175 | return yaml_INT_TAG, intv 176 | } 177 | } 178 | uintv, err := strconv.ParseUint(plain[2:], 2, 64) 179 | if err == nil { 180 | return yaml_INT_TAG, uintv 181 | } 182 | } else if strings.HasPrefix(plain, "-0b") { 183 | intv, err := strconv.ParseInt("-" + plain[3:], 2, 64) 184 | if err == nil { 185 | if true || intv == int64(int(intv)) { 186 | return yaml_INT_TAG, int(intv) 187 | } else { 188 | return yaml_INT_TAG, intv 189 | } 190 | } 191 | } 192 | default: 193 | panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") 194 | } 195 | } 196 | return yaml_STR_TAG, in 197 | } 198 | 199 | // encodeBase64 encodes s as base64 that is broken up into multiple lines 200 | // as appropriate for the resulting length. 201 | func encodeBase64(s string) string { 202 | const lineLen = 70 203 | encLen := base64.StdEncoding.EncodedLen(len(s)) 204 | lines := encLen/lineLen + 1 205 | buf := make([]byte, encLen*2+lines) 206 | in := buf[0:encLen] 207 | out := buf[encLen:] 208 | base64.StdEncoding.Encode(in, []byte(s)) 209 | k := 0 210 | for i := 0; i < len(in); i += lineLen { 211 | j := i + lineLen 212 | if j > len(in) { 213 | j = len(in) 214 | } 215 | k += copy(out[k:], in[i:j]) 216 | if lines > 1 { 217 | out[k] = '\n' 218 | k++ 219 | } 220 | } 221 | return string(out[:k]) 222 | } 223 | 224 | // This is a subset of the formats allowed by the regular expression 225 | // defined at http://yaml.org/type/timestamp.html. 226 | var allowedTimestampFormats = []string{ 227 | "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. 228 | "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". 229 | "2006-1-2 15:4:5.999999999", // space separated with no time zone 230 | "2006-1-2", // date only 231 | // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" 232 | // from the set of examples. 233 | } 234 | 235 | // parseTimestamp parses s as a timestamp string and 236 | // returns the timestamp and reports whether it succeeded. 237 | // Timestamp formats are defined at http://yaml.org/type/timestamp.html 238 | func parseTimestamp(s string) (time.Time, bool) { 239 | // TODO write code to check all the formats supported by 240 | // http://yaml.org/type/timestamp.html instead of using time.Parse. 241 | 242 | // Quick check: all date formats start with YYYY-. 243 | i := 0 244 | for ; i < len(s); i++ { 245 | if c := s[i]; c < '0' || c > '9' { 246 | break 247 | } 248 | } 249 | if i != 4 || i == len(s) || s[i] != '-' { 250 | return time.Time{}, false 251 | } 252 | for _, format := range allowedTimestampFormats { 253 | if t, err := time.Parse(format, s); err == nil { 254 | return t, true 255 | } 256 | } 257 | return time.Time{}, false 258 | } 259 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/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 | if ar[i] == '0' || br[i] == '0' { 55 | for j := i-1; j >= 0 && unicode.IsDigit(ar[j]); j-- { 56 | if ar[j] != '0' { 57 | an = 1 58 | bn = 1 59 | break 60 | } 61 | } 62 | } 63 | for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { 64 | an = an*10 + int64(ar[ai]-'0') 65 | } 66 | for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { 67 | bn = bn*10 + int64(br[bi]-'0') 68 | } 69 | if an != bn { 70 | return an < bn 71 | } 72 | if ai != bi { 73 | return ai < bi 74 | } 75 | return ar[i] < br[i] 76 | } 77 | return len(ar) < len(br) 78 | } 79 | 80 | // keyFloat returns a float value for v if it is a number/bool 81 | // and whether it is a number/bool or not. 82 | func keyFloat(v reflect.Value) (f float64, ok bool) { 83 | switch v.Kind() { 84 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 85 | return float64(v.Int()), true 86 | case reflect.Float32, reflect.Float64: 87 | return v.Float(), true 88 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 89 | return float64(v.Uint()), true 90 | case reflect.Bool: 91 | if v.Bool() { 92 | return 1, true 93 | } 94 | return 0, true 95 | } 96 | return 0, false 97 | } 98 | 99 | // numLess returns whether a < b. 100 | // a and b must necessarily have the same kind. 101 | func numLess(a, b reflect.Value) bool { 102 | switch a.Kind() { 103 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 104 | return a.Int() < b.Int() 105 | case reflect.Float32, reflect.Float64: 106 | return a.Float() < b.Float() 107 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 108 | return a.Uint() < b.Uint() 109 | case reflect.Bool: 110 | return !a.Bool() && b.Bool() 111 | } 112 | panic("not a number") 113 | } 114 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/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 err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { 22 | return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) 23 | } 24 | emitter.buffer_pos = 0 25 | return true 26 | } 27 | -------------------------------------------------------------------------------- /vendor/gopkg.in/yaml.v2/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 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | var ( 4 | // Build agent Build version 5 | Build string 6 | ) 7 | -------------------------------------------------------------------------------- /witch/handler.go: -------------------------------------------------------------------------------- 1 | package witch 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "log" 9 | "net/http" 10 | 11 | "github.com/barryz/rmqmonitor/falcon" 12 | "github.com/barryz/rmqmonitor/g" 13 | "github.com/barryz/rmqmonitor/witch/system" 14 | 15 | "github.com/martini-contrib/render" 16 | ) 17 | 18 | var ( 19 | // ErrServerError is internal server error. 20 | ErrServerError = errors.New("Internal Server Error") 21 | // ErrBadRequest is bad request error. 22 | ErrBadRequest = errors.New("Bad Request") 23 | ) 24 | 25 | func sysAction(control *system.SysController, req *http.Request, r render.Render) { 26 | bs, err := ioutil.ReadAll(req.Body) 27 | if err != nil { 28 | log.Printf("[ERROR] Read request body error: %s", err) 29 | r.JSON(http.StatusInternalServerError, ErrServerError) 30 | return 31 | } 32 | log.Printf("[INFO] Request action: %s", bs) 33 | action := &system.Action{} 34 | if err := json.Unmarshal(bs, action); err != nil { 35 | log.Printf("[WARN] Invalid action format: %s", err) 36 | r.JSON(http.StatusBadRequest, ErrBadRequest) 37 | return 38 | } 39 | r.JSON(http.StatusOK, control.Handle(action)) 40 | } 41 | 42 | func statsAction(control *system.StatsController, req *http.Request, r render.Render) { 43 | bs, err := ioutil.ReadAll(req.Body) 44 | if err != nil { 45 | log.Printf("[ERROR] Read request body error: %s", err) 46 | r.JSON(http.StatusInternalServerError, ErrServerError) 47 | return 48 | } 49 | log.Printf("[INFO] Request stats action: %s", bs) 50 | action := &system.Action{} 51 | if err := json.Unmarshal(bs, action); err != nil { 52 | log.Printf("[WARN] Invalid action format: %s", err) 53 | r.JSON(http.StatusBadRequest, ErrBadRequest) 54 | return 55 | } 56 | r.JSON(http.StatusOK, control.Handle(action)) 57 | 58 | } 59 | 60 | func procForceStop(req *http.Request, r render.Render) { 61 | if req.Method != "GET" { 62 | r.JSON(http.StatusMethodNotAllowed, ErrBadRequest) 63 | return 64 | } 65 | proc := g.Config().Witch.Process 66 | args := fmt.Sprintf("pgrep %s|xargs skill -9", proc) 67 | _, err := system.ExecCommand("bash", []string{"-c", args}) 68 | if err != nil { 69 | r.JSON(http.StatusServiceUnavailable, ErrServerError) 70 | return 71 | } 72 | 73 | r.JSON(http.StatusOK, map[string]interface{}{"status": true, "data": "ok"}) 74 | } 75 | 76 | func statsInfo(req *http.Request, r render.Render) { 77 | if req.Method != "GET" { 78 | r.JSON(http.StatusMethodNotAllowed, ErrBadRequest) 79 | return 80 | } 81 | stats := falcon.GetCurrentStatsDB() 82 | r.JSON(http.StatusOK, map[string]interface{}{"status": true, "data": stats}) 83 | 84 | } 85 | -------------------------------------------------------------------------------- /witch/server.go: -------------------------------------------------------------------------------- 1 | package witch 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "strings" 7 | 8 | "github.com/barryz/rmqmonitor/g" 9 | "github.com/barryz/rmqmonitor/witch/system" 10 | 11 | "github.com/braintree/manners" 12 | "github.com/go-martini/martini" 13 | mauth "github.com/martini-contrib/auth" 14 | "github.com/martini-contrib/render" 15 | ) 16 | 17 | // Server is the system RESTFul web server. 18 | type Server struct { 19 | addr string 20 | m *martini.ClassicMartini 21 | } 22 | 23 | // NewServer inits a system RESTful web server. 24 | func NewServer(addr string, sysControl *system.SysController, statsControl *system.StatsController, cfg *g.GlobalConfig) *Server { 25 | ser := &Server{ 26 | addr: addr, 27 | m: martini.Classic(), 28 | } 29 | authFunc := mauth.BasicFunc(func(username, password string) bool { 30 | pwd, ok := cfg.Witch.Auth[username] 31 | return ok && pwd == password 32 | }).(func(http.ResponseWriter, *http.Request, martini.Context)) 33 | ser.m.Map(sysControl) 34 | ser.m.Map(statsControl) 35 | ser.m.Use(authInclusive("/api", authFunc)) 36 | ser.m.Use(render.Renderer(render.Options{})) 37 | // start|stop|restart RabbitMQ process(other process) via supervisor or systemd 38 | ser.m.Put("/api/app/actions", sysAction) 39 | // forced to stop RabbitMQ process(other process) via sent SIGTERM syscall signal 40 | ser.m.Get("/api/app/fstop", procForceStop) 41 | // get current RabbitMQ statistic db node location 42 | ser.m.Get("/api/stats", statsInfo) 43 | // reset|crash|terminate current RabbitMQ statistic db node 44 | ser.m.Put("/api/stats/actions", statsAction) 45 | return ser 46 | } 47 | 48 | // Start starts the server. 49 | func (ser *Server) Start() error { 50 | log.Printf("[INFO] System webapp start at %s", ser.addr) 51 | return manners.ListenAndServe(ser.addr, ser.m) 52 | } 53 | 54 | // Stop stops the server. 55 | func (ser *Server) Stop() { 56 | manners.Close() 57 | } 58 | 59 | func authInclusive(urlPrefix string, authFunc func(http.ResponseWriter, *http.Request, martini.Context)) martini.Handler { 60 | return func(resp http.ResponseWriter, req *http.Request, ctx martini.Context) { 61 | if strings.HasPrefix(req.URL.String(), urlPrefix) { 62 | if auth := req.URL.Query().Get("auth"); auth != "" && req.Header.Get("Authorization") == "" { 63 | req.Header.Set("Authorization", "Basic "+auth) 64 | } 65 | authFunc(resp, req, ctx) 66 | } else { 67 | ctx.Next() 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /witch/system/launcher.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "os" 7 | "os/exec" 8 | "path" 9 | "strconv" 10 | "syscall" 11 | "time" 12 | ) 13 | 14 | // Launcher supervises the process status, start, stop and restart. 15 | type Launcher struct { 16 | pidFile string 17 | cmd string 18 | } 19 | 20 | // NewLauncher creates new system. 21 | func NewLauncher(pidFile, cmd string) *Launcher { 22 | return &Launcher{ 23 | pidFile: pidFile, 24 | cmd: cmd, 25 | } 26 | } 27 | 28 | func (s *Launcher) writePid(pid int) { 29 | if err := WriteFile(s.pidFile, []byte(strconv.FormatInt(int64(pid), 10)), 0644); err != nil { 30 | log.Fatalf("Failed to write pid file: %s", err) 31 | } 32 | } 33 | 34 | func (s *Launcher) readPid() (int, bool) { 35 | f, err := ioutil.ReadFile(s.pidFile) 36 | if err != nil { 37 | log.Printf("Error reading pid file[%s]: %s", s.pidFile, err) 38 | return -1, false 39 | } 40 | 41 | pid, err := strconv.Atoi(string(f)) 42 | if err != nil { 43 | log.Printf("Invalid pid value[%s]: %s", s.pidFile, err) 44 | return -1, false 45 | } 46 | 47 | return pid, true 48 | } 49 | 50 | func (s *Launcher) pidAlive(pid int) bool { 51 | return syscall.Kill(pid, 0) == nil 52 | } 53 | 54 | // IsAlive check if the process alive. 55 | func (s *Launcher) IsAlive() (int, bool) { 56 | pid, ok := s.readPid() 57 | if !ok || pid < 1 { 58 | return pid, false 59 | } 60 | return pid, s.pidAlive(pid) 61 | } 62 | 63 | // Start starts the process. 64 | func (s *Launcher) Start() (bool, error) { 65 | if pid, ok := s.IsAlive(); ok { 66 | log.Printf("The process is alive, pid: %d", pid) 67 | return true, nil 68 | } 69 | 70 | log.Printf("Starting [%s]", s.cmd) 71 | child := exec.Command("/bin/bash", []string{"-c", s.cmd}...) 72 | child.Stdin = os.Stdin 73 | child.Stdout = os.Stdout 74 | child.Stderr = os.Stderr 75 | if err := child.Start(); err != nil { 76 | log.Printf("Failed to start: %s", err) 77 | return false, err 78 | } 79 | s.writePid(child.Process.Pid) 80 | go child.Wait() 81 | return true, nil 82 | } 83 | 84 | // Restart restart the process 85 | func (s *Launcher) Restart() (bool, error) { 86 | s.Stop() 87 | return s.Start() 88 | } 89 | 90 | // Stop stops the process. 91 | func (s *Launcher) Stop() bool { 92 | pid, ok := s.IsAlive() 93 | if !ok { 94 | log.Printf("The process not alive") 95 | return true 96 | } 97 | syscall.Kill(pid, syscall.SIGTERM) 98 | stopped := make(chan bool) 99 | go func() { 100 | for s.pidAlive(pid) { 101 | time.Sleep(time.Second) 102 | } 103 | close(stopped) 104 | }() 105 | select { 106 | case <-stopped: 107 | log.Printf("[INFO] Stop the process success.") 108 | case <-time.After(time.Duration(stopWaitSecs) * time.Second): 109 | log.Printf("[INFO] Stop the process timeout, force to kill.") 110 | syscall.Kill(pid, syscall.SIGKILL) 111 | } 112 | return true 113 | } 114 | 115 | // WriteFile tries to create parent directory before WriteFile. 116 | func WriteFile(filename string, data []byte, perm os.FileMode) error { 117 | if err := os.MkdirAll(path.Dir(filename), 0755); err != nil { 118 | return err 119 | } 120 | return ioutil.WriteFile(filename, data, perm) 121 | } 122 | -------------------------------------------------------------------------------- /witch/system/statsdb.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | // StatsDBCtl is the RabbitMQ statsdb control system 4 | type StatsDBCtl struct { 5 | name string 6 | } 7 | 8 | // NewStatsDBCtl creates a new StatsDBCtl instance 9 | func NewStatsDBCtl() *StatsDBCtl { 10 | return &StatsDBCtl{ 11 | name: "/sbin/rabbitmqctl", 12 | } 13 | } 14 | 15 | // Reset reset the RabbitMQ statsdb 16 | func (s *StatsDBCtl) Reset() (bool, string, error) { 17 | output, err := ExecCommand(s.name, []string{"eval", "application:stop(rabbitmq_management), application:start(rabbitmq_management)."}) 18 | return err == nil, output, err 19 | } 20 | 21 | // Terminate terminate the RabbitMQ statsdb 22 | func (s *StatsDBCtl) Terminate() (bool, string, error) { 23 | output, err := ExecCommand(s.name, []string{"eval", "exit(erlang:whereis(rabbit_mgmt_db), please_terminate)."}) 24 | return err == nil, output, err 25 | } 26 | 27 | // Crash crash the RabbitMQ statsdb 28 | func (s *StatsDBCtl) Crash() (bool, string, error) { 29 | output, err := ExecCommand(s.name, []string{"eval", "exit(erlang:whereis(rabbit_mgmt_db), please_crash)."}) 30 | return err == nil, output, err 31 | } 32 | -------------------------------------------------------------------------------- /witch/system/supervisor.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Supervisor is the supervisor process control system. 8 | type Supervisor struct { 9 | name string 10 | service string 11 | } 12 | 13 | // NewSupervisor creates new Supervisor instance. 14 | func NewSupervisor(service string) *Supervisor { 15 | return &Supervisor{ 16 | name: "/usr/bin/supervisorctl", 17 | service: service, 18 | } 19 | } 20 | 21 | // IsAlive gets results from `supervisorctl status [service]` 22 | func (s *Supervisor) IsAlive() (int, bool) { 23 | r, err := ExecCommand(s.name, []string{"status", s.service}) 24 | if err != nil { 25 | return -1, false 26 | } 27 | return -1, strings.Contains(r, "RUNNING") 28 | } 29 | 30 | // Start executes `supervisorctl start [service]` 31 | func (s *Supervisor) Start() (bool, error) { 32 | _, err := ExecCommand(s.name, []string{"start", s.service}) 33 | return err == nil, err 34 | } 35 | 36 | // Restart executes `supervisorctl restart [service]` 37 | func (s *Supervisor) Restart() (bool, error) { 38 | _, err := ExecCommand(s.name, []string{"restart", s.service}) 39 | return err == nil, err 40 | } 41 | 42 | // Stop executes `supervisorctl stop [service]` 43 | func (s *Supervisor) Stop() bool { 44 | _, err := ExecCommand(s.name, []string{"stop", s.service}) 45 | return err == nil 46 | } 47 | -------------------------------------------------------------------------------- /witch/system/system.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "os/exec" 8 | ) 9 | 10 | var ( 11 | stopWaitSecs = 5 12 | ) 13 | 14 | // System is the interface of process control system. 15 | type System interface { 16 | // IsAlive checks process is alive. 17 | IsAlive() (int, bool) 18 | // Start starts process. 19 | Start() (bool, error) 20 | // Start restart process. 21 | Restart() (bool, error) 22 | // Stop stops process. 23 | Stop() bool 24 | } 25 | 26 | // Stats represents a stats interface 27 | type Stats interface { 28 | // Reset the RabbitMQ StatsDB, A node may be randomly selected 29 | Reset() (bool, string, error) 30 | // Terminate the RabbitMQ StatsDB, Not to choose the node 31 | Terminate() (bool, string, error) 32 | // Crash the RabbitMQ StatsDB, Not to choose the node 33 | Crash() (bool, string, error) 34 | } 35 | 36 | // Action is the system action. 37 | type Action struct { 38 | Name string `json:"name"` 39 | } 40 | 41 | // ActionStatus is the status of action. 42 | type ActionStatus struct { 43 | Status bool `json:"status"` 44 | Text string `json:"text"` 45 | } 46 | 47 | // SysController controls the System. 48 | type SysController struct { 49 | System 50 | } 51 | 52 | // StatsController controls the RabbitMQ StatsDB 53 | type StatsController struct { 54 | Stats 55 | } 56 | 57 | // Handle handle action 58 | func (c *StatsController) Handle(action *Action) *ActionStatus { 59 | var ( 60 | st = &ActionStatus{} 61 | err error 62 | output string 63 | ) 64 | 65 | switch action.Name { 66 | case "status": 67 | fallthrough 68 | case "reset": 69 | if st.Status, output, err = c.Reset(); err != nil { 70 | st.Text = err.Error() 71 | } else { 72 | st.Text = output 73 | } 74 | case "terminate": 75 | if st.Status, output, err = c.Terminate(); err != nil { 76 | st.Text = err.Error() 77 | } else { 78 | st.Text = output 79 | } 80 | case "crash": 81 | if st.Status, output, err = c.Crash(); err != nil { 82 | st.Text = err.Error() 83 | } else { 84 | st.Text = output 85 | } 86 | 87 | default: 88 | st.Status, st.Text = false, fmt.Sprintf("Invalid action: %s", action.Name) 89 | 90 | } 91 | log.Println("[INFO]: StatsDB Action finished") 92 | return st 93 | } 94 | 95 | // Handle plays action. 96 | func (c *SysController) Handle(action *Action) *ActionStatus { 97 | var ( 98 | st = &ActionStatus{} 99 | err error 100 | ) 101 | switch action.Name { 102 | case "status": 103 | fallthrough 104 | case "is_alive": 105 | _, st.Status = c.IsAlive() 106 | case "start": 107 | if st.Status, err = c.Start(); err != nil { 108 | st.Text = err.Error() 109 | } 110 | case "stop": 111 | st.Status = c.Stop() 112 | case "restart": 113 | if st.Status, err = c.Restart(); err != nil { 114 | st.Text = err.Error() 115 | } 116 | default: 117 | st.Status, st.Text = false, fmt.Sprintf("Invalid action: %s", action.Name) 118 | } 119 | log.Printf("[INFO] System Action finished") 120 | return st 121 | } 122 | 123 | // ExecCommand command execution 124 | func ExecCommand(name string, args []string) (string, error) { 125 | var buf bytes.Buffer 126 | log.Printf("[INFO] Exec %s %v", name, args) 127 | child := exec.Command(name, args...) 128 | child.Stdout = &buf 129 | child.Stderr = &buf 130 | if err := child.Start(); err != nil { 131 | log.Printf("[ERROR] Failed to start: %s", err) 132 | return buf.String(), err 133 | } 134 | child.Wait() 135 | return buf.String(), nil 136 | } 137 | -------------------------------------------------------------------------------- /witch/system/systemd.go: -------------------------------------------------------------------------------- 1 | package system 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // Systemd is the systemd process control system. 8 | type Systemd struct { 9 | name string 10 | service string 11 | } 12 | 13 | // NewSystemd creates new Systemd instance. 14 | func NewSystemd(service string) *Systemd { 15 | return &Systemd{ 16 | name: "/bin/systemctl", 17 | service: service, 18 | } 19 | } 20 | 21 | // IsAlive gets results from `systemctl is-active [service]` 22 | func (s *Systemd) IsAlive() (int, bool) { 23 | r, err := ExecCommand(s.name, []string{"is-active", s.service}) 24 | if err != nil { 25 | return -1, false 26 | } 27 | return -1, strings.HasPrefix(r, "active") 28 | } 29 | 30 | // Start executes `systemctl start [service]` 31 | func (s *Systemd) Start() (bool, error) { 32 | _, err := ExecCommand(s.name, []string{"start", s.service}) 33 | return err == nil, err 34 | } 35 | 36 | // Restart executes `systemctl restart [service]` 37 | func (s *Systemd) Restart() (bool, error) { 38 | _, err := ExecCommand(s.name, []string{"restart", s.service}) 39 | return err == nil, err 40 | } 41 | 42 | // Stop executes `systemctl stop [service]` 43 | func (s *Systemd) Stop() bool { 44 | _, err := ExecCommand(s.name, []string{"stop", s.service}) 45 | return err == nil 46 | } 47 | -------------------------------------------------------------------------------- /witch/witch.go: -------------------------------------------------------------------------------- 1 | package witch 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "os/signal" 7 | "syscall" 8 | 9 | "github.com/barryz/rmqmonitor/g" 10 | "github.com/barryz/rmqmonitor/witch/system" 11 | ) 12 | 13 | const ( 14 | commandBuildIn = "buildin" 15 | commandSupervisor = "supervisor" 16 | commandSystemd = "systemd" 17 | ) 18 | 19 | func handleSignals(exitFunc func()) { 20 | sigs := make(chan os.Signal, 1) 21 | signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT) 22 | sig := <-sigs 23 | log.Printf("Signal %v captured", sig) 24 | exitFunc() 25 | } 26 | 27 | func createSystem(cfg *g.GlobalConfig) system.System { 28 | switch cfg.Witch.Control { 29 | case commandBuildIn: 30 | return system.NewLauncher(cfg.Witch.PidFile, cfg.Witch.Command) 31 | case commandSupervisor: 32 | return system.NewSupervisor(cfg.Witch.Service) 33 | case commandSystemd: 34 | return system.NewSystemd(cfg.Witch.Service) 35 | } 36 | log.Fatalf("Invalid control '%s'", cfg.Witch.Control) 37 | return nil 38 | } 39 | 40 | func createStats() system.Stats { 41 | return system.NewStatsDBCtl() 42 | } 43 | 44 | // Launch launch witch service 45 | func Launch() { 46 | cfg := g.Config() 47 | stats := createStats() 48 | sys := createSystem(cfg) 49 | sys.Start() 50 | 51 | srv := NewServer(cfg.Witch.ListenAddr, &system.SysController{System: sys}, &system.StatsController{Stats: stats}, cfg) 52 | go func() { 53 | if err := srv.Start(); err != nil { 54 | log.Fatalf("[FATAL] Start system server failed: %v", err) 55 | } 56 | }() 57 | 58 | handleSignals(func() { 59 | sys.Stop() 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /witch/witch_test.go: -------------------------------------------------------------------------------- 1 | package witch 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/barryz/rmqmonitor/g" 7 | ) 8 | 9 | func Test_SystemLauncher(t *testing.T) { 10 | g.ParseConfig("../config.example.yml") 11 | 12 | //Launch() 13 | // 14 | //select {} 15 | } 16 | --------------------------------------------------------------------------------