├── .gitignore ├── .travis.yml ├── AUTHORS.TXT ├── Gopkg.lock ├── Gopkg.toml ├── Makefile ├── README.md ├── add-node.go ├── autocomplete ├── bash_autocomplete └── zsh_autocomplete ├── call.go ├── check.go ├── clusternode.go ├── create.go ├── del-node.go ├── fix.go ├── hacking ├── author.sh ├── config │ ├── redis-6378.conf │ ├── redis-6379.conf │ ├── redis-6380.conf │ ├── redis-7378.conf │ ├── redis-7379.conf │ └── redis-7380.conf └── start-redis.sh ├── import.go ├── info.go ├── main.go ├── rebalance.go ├── redistrib.go ├── reshard.go ├── script ├── .validate ├── clean_godeps.sh ├── lint └── validate-gofmt ├── set-timeout.go ├── utils.go └── vendor └── github.com ├── Sirupsen └── logrus │ ├── .gitignore │ ├── .travis.yml │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── doc.go │ ├── entry.go │ ├── entry_test.go │ ├── exported.go │ ├── formatter.go │ ├── formatter_bench_test.go │ ├── hook_test.go │ ├── hooks.go │ ├── json_formatter.go │ ├── json_formatter_test.go │ ├── logger.go │ ├── logrus.go │ ├── logrus_test.go │ ├── terminal_bsd.go │ ├── terminal_linux.go │ ├── terminal_notwindows.go │ ├── terminal_windows.go │ ├── text_formatter.go │ ├── text_formatter_test.go │ └── writer.go ├── codegangsta └── cli │ ├── .flake8 │ ├── .gitignore │ ├── .travis.yml │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── app.go │ ├── app_test.go │ ├── appveyor.yml │ ├── category.go │ ├── cli.go │ ├── command.go │ ├── command_test.go │ ├── context.go │ ├── context_test.go │ ├── errors.go │ ├── errors_test.go │ ├── flag-types.json │ ├── flag.go │ ├── flag_generated.go │ ├── flag_test.go │ ├── funcs.go │ ├── generate-flag-types │ ├── help.go │ ├── help_test.go │ ├── helpers_test.go │ ├── helpers_unix_test.go │ ├── helpers_windows_test.go │ └── runtests └── garyburd └── redigo ├── .travis.yml ├── LICENSE ├── README.markdown ├── internal ├── commandinfo.go └── commandinfo_test.go └── redis ├── conn.go ├── conn_test.go ├── doc.go ├── go16.go ├── go17.go ├── go18.go ├── log.go ├── pool.go ├── pool_test.go ├── pubsub.go ├── pubsub_example_test.go ├── pubsub_test.go ├── redis.go ├── redis_test.go ├── reply.go ├── reply_test.go ├── scan.go ├── scan_test.go ├── script.go ├── script_test.go ├── test_test.go └── zpop_example_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | redis-trib* 2 | tags 3 | vendor/pkg/* 4 | .idea 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | go: 5 | - 1.5.3 6 | - 1.6 7 | - tip 8 | 9 | env: 10 | global: 11 | - GO15VENDOREXPERIMENT=1 12 | 13 | #install: true 14 | script: 15 | - make validate 16 | - make test 17 | - make bin 18 | 19 | notifications: 20 | email: 21 | on_success: change 22 | on_failure: always 23 | 24 | matrix: 25 | allow_failures: 26 | - go: tip 27 | 28 | -------------------------------------------------------------------------------- /AUTHORS.TXT: -------------------------------------------------------------------------------- 1 | soarpenguin 2 | Andy Royle 3 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | name = "github.com/Sirupsen/logrus" 6 | packages = ["."] 7 | revision = "418b41d23a1bf978c06faea5313ba194650ac088" 8 | version = "v0.8.7" 9 | 10 | [[projects]] 11 | name = "github.com/codegangsta/cli" 12 | packages = ["."] 13 | revision = "cfb38830724cc34fedffe9a2a29fb54fa9169cd1" 14 | version = "v1.20.0" 15 | 16 | [[projects]] 17 | name = "github.com/garyburd/redigo" 18 | packages = [ 19 | "internal", 20 | "redis" 21 | ] 22 | revision = "d1ed5c67e5794de818ea85e6b522fda02623a484" 23 | version = "v1.4.0" 24 | 25 | [solve-meta] 26 | analyzer-name = "dep" 27 | analyzer-version = 1 28 | inputs-digest = "267b7a636210772a55deb6224c3b0e54dc1b2a655b87f4ba4f092328978b0d0a" 29 | solver-name = "gps-cdcl" 30 | solver-version = 1 31 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 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 | name = "github.com/Sirupsen/logrus" 30 | version = "0.8.7" 31 | 32 | [[constraint]] 33 | name = "github.com/codegangsta/cli" 34 | version = "1.20.0" 35 | 36 | [[constraint]] 37 | name = "github.com/garyburd/redigo" 38 | version = "1.4.0" 39 | 40 | [prune] 41 | go-tests = true 42 | unused-packages = true 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: help 2 | 3 | HOST_GOLANG_VERSION := $(shell go version | cut -d ' ' -f3 | cut -c 3-) 4 | MODULE := redis-trib 5 | ifneq (,$(wildcard .git/.*)) 6 | COMMIT = $(shell git rev-parse HEAD 2> /dev/null || true) 7 | VERSION = $(shell git describe --tags --abbrev=0 2> /dev/null) 8 | else 9 | COMMIT = "unknown" 10 | VERSION = "unknown" 11 | endif 12 | 13 | #Replaces ":" (*nix), ";" (windows) with newline for easy parsing 14 | GOPATHS=$(shell echo ${GOPATH} | tr ":" "\n" | tr ";" "\n") 15 | 16 | # See Golang issue re: '-trimpath': https://github.com/golang/go/issues/13809 17 | GO_GCFLAGS=$(shell \ 18 | set -- ${GOPATHS}; \ 19 | echo "-gcflags=-trimpath=$${1}"; \ 20 | ) 21 | 22 | GO_ASMFLAGS=$(shell \ 23 | set -- ${GOPATHS}; \ 24 | echo "-asmflags=-trimpath=$${1}"; \ 25 | ) 26 | 27 | #export GOPATH := $(shell cd ./ && pwd)/vendor:$(GOPATH) 28 | 29 | ## Make bin for $MODULE. 30 | bin: 31 | @echo "GOVERSION: ${HOST_GOLANG_VERSION}" 32 | @echo "GOPATH:" $$GOPATH 33 | go build ${GO_GCFLAGS} ${GO_ASMFLAGS} -i -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION}" -o ${MODULE} . 34 | 35 | ## Build debug trace for $MODULE. 36 | debug: 37 | @echo "GOVERSION: ${HOST_GOLANG_VERSION}" 38 | @echo "GOPATH:" $$GOPATH 39 | go build -n -v -i -ldflags "-X main.gitCommit=${COMMIT} -X main.version=${VERSION}" -o ${MODULE} . 40 | 41 | ## Make static link bin for $MODULE. 42 | static-bin: 43 | @echo "GOVERSION:" ${HOST_GOLANG_VERSION} 44 | @echo "GOPATH:" $$GOPATH 45 | go build ${GO_GCFLAGS} ${GO_ASMFLAGS} -i -ldflags "-w -extldflags -static -X main.gitCommit=${COMMIT} -X main.version=${VERSION}" -o ${MODULE} . 46 | 47 | ## Get dep tool for managing dependencies for Go projects. 48 | dep: 49 | go get -u github.com/golang/dep/cmd/dep 50 | 51 | ## Get dep tool and init project. 52 | depinit: dep 53 | dep init 54 | dep ensure 55 | 56 | ## Get vet go tools. 57 | vet: 58 | go get golang.org/x/tools/cmd/vet 59 | 60 | ## Validate this go project. 61 | validate: 62 | script/validate-gofmt 63 | #go vet ./... 64 | 65 | ## Run test case for this go project. 66 | test: 67 | go test $(go list ./... | grep -v '/vendor/') 68 | 69 | ## Clean everything (including stray volumes). 70 | clean: 71 | # find . -name '*.created' -exec rm -f {} + 72 | -rm -rf var 73 | -rm -f ${MODULE} 74 | 75 | help: # Some kind of magic from https://gist.github.com/rcmachado/af3db315e31383502660 76 | $(info Available targets) 77 | @awk '/^[a-zA-Z\-\_0-9]+:/ { \ 78 | nb = sub( /^## /, "", helpMsg ); \ 79 | if(nb == 0) { \ 80 | helpMsg = $$0; \ 81 | nb = sub( /^[^:]*:.* ## /, "", helpMsg ); \ 82 | } \ 83 | if (nb) { \ 84 | h = sub( /[^ ]*MODULE/, "'${MODULE}'", helpMsg ); \ 85 | printf " \033[1;31m%-" width "s\033[0m %s\n", $$1, helpMsg; \ 86 | } \ 87 | } \ 88 | { helpMsg = $$0 }' \ 89 | width=$$(grep -o '^[a-zA-Z_0-9]\+:' $(MAKEFILE_LIST) | wc -L 2> /dev/null) \ 90 | $(MAKEFILE_LIST) 91 | 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **redis-trib** _redis cluster Command Line tool_ 2 | [![Build Status](https://travis-ci.org/soarpenguin/redis-trib.svg?branch=master)](https://travis-ci.org/soarpenguin/redis-trib) 3 | 4 | Create and administrate your [Redis Cluster][cluster-tutorial] from the Command Line. 5 | 6 | Inspired heavily by [redis-trib.go][] and the original [redis-trib.rb][]. 7 | 8 | ## Dependencies 9 | 10 | * [redigo][] 11 | * [cli][] 12 | 13 | Dependencies are handled by [govendor][], simple install it and type `govendor add +external` to fetch them. 14 | 15 | ## Install 16 | 17 | #### Restore project env in first build 18 | ```console 19 | $ git clone https://github.com/soarpenguin/redis-trib.git 20 | $ cd redis-trib 21 | $ make govendor 22 | $ make bin 23 | $ PROG=./redis-trib source ./autocomplete/bash_autocomplete 24 | ``` 25 | 26 | #### Build the code 27 | ```console 28 | $ cd redis-trib 29 | $ make bin 30 | ``` 31 | 32 | ## Usage 33 | 34 | ```console 35 | NAME: 36 | redis-trib - Redis Cluster command line utility. 37 | 38 | For check, fix, reshard, del-node, set-timeout you can specify the host and port 39 | of any working node in the cluster. 40 | 41 | USAGE: 42 | redis-trib [global options] command [command options] [arguments...] 43 | 44 | VERSION: 45 | v0.1.0 46 | commit: 533d96aba4b0e73649dcf81209156230698666aa 47 | 48 | AUTHOR(S): 49 | soarpenguin 50 | 51 | COMMANDS: 52 | add-node, add add a new redis node to existed cluster. 53 | call run command in redis cluster. 54 | check check the redis cluster. 55 | create create a new redis cluster. 56 | del-node, del del a redis node from existed cluster. 57 | fix fix the redis cluster. 58 | import import operation for redis cluster. 59 | info display the info of redis cluster. 60 | rebalance rebalance the redis cluster. 61 | reshard reshard the redis cluster. 62 | set-timeout set timeout configure for redis cluster. 63 | 64 | GLOBAL OPTIONS: 65 | --debug enable debug output for logging 66 | --verbose verbose global flag for output. 67 | --log value set the log file path where internal debug information is written 68 | --log-format value set the format used by logs ('text' (default), or 'json') (default: "text") 69 | --help, -h show help 70 | --version, -v print the version 71 | ``` 72 | 73 | [cluster-tutorial]: http://redis.io/topics/cluster-tutorial 74 | [redis-trib.go]: https://github.com/badboy/redis-trib.go 75 | [redis-trib.rb]: https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb 76 | [redigo]: https://github.com/garyburd/redigo/ 77 | [cli]: https://github.com/codegangsta/cli 78 | [govendor]: https://github.com/kardianos/govendor 79 | -------------------------------------------------------------------------------- /add-node.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/codegangsta/cli" 9 | ) 10 | 11 | // add-node new_host:new_port existing_host:existing_port 12 | // --slave 13 | // --master-id 14 | var addNodeCommand = cli.Command{ 15 | Name: "add-node", 16 | Aliases: []string{"add"}, 17 | Usage: "add a new redis node to existed cluster.", 18 | Description: `The add-node command add a node to redis cluster.`, 19 | ArgsUsage: `new_host:new_port existing_host:existing_port`, 20 | Flags: []cli.Flag{ 21 | cli.BoolFlag{ 22 | Name: "slave", 23 | Usage: `Slave flag for node join a existed cluster. 24 | 25 | $ redis-trib add-node <--slave> new_host:new_port existing_host:existing_port`, 26 | }, 27 | cli.StringFlag{ 28 | Name: "master-id", 29 | Value: "", 30 | Usage: `Master id for slave node to meet. 31 | 32 | $ redis-trib add-node <--slave --master-id arg> new_host:new_port existing_host:existing_port`, 33 | }, 34 | }, 35 | Action: func(context *cli.Context) error { 36 | if context.NArg() < 2 { 37 | fmt.Printf("Incorrect Usage.\n\n") 38 | cli.ShowCommandHelp(context, "add-node") 39 | logrus.Fatalf("Must provide \"new_host:new_port existing_host:existing_port\" for add-node command!") 40 | } 41 | 42 | rt := NewRedisTrib() 43 | if err := rt.AddNodeClusterCmd(context); err != nil { 44 | return err 45 | } 46 | return nil 47 | }, 48 | } 49 | 50 | func (self *RedisTrib) AddNodeClusterCmd(context *cli.Context) error { 51 | var newaddr string 52 | var addr string 53 | var masterID string 54 | var master *ClusterNode 55 | 56 | if newaddr = context.Args().Get(0); newaddr == "" { 57 | return errors.New("please check new_host:new_port for add-node command") 58 | } else if addr = context.Args().Get(1); addr == "" { 59 | return errors.New("please check existing_host:existing_port for add-node command") 60 | } 61 | 62 | logrus.Printf(">>> Adding node %s to cluster %s", newaddr, addr) 63 | // Check the existing cluster 64 | // Load cluster information 65 | if err := self.LoadClusterInfoFromNode(addr); err != nil { 66 | return err 67 | } 68 | self.CheckCluster(false) 69 | 70 | // If --master-id was specified, try to resolve it now so that we 71 | // abort before starting with the node configuration. 72 | if context.Bool("slave") { 73 | masterID = context.String("master-id") 74 | if masterID != "" { 75 | master = self.GetNodeByName(masterID) 76 | if master == nil { 77 | logrus.Errorf("No such master ID %s", masterID) 78 | } 79 | } else { 80 | master = self.GetMasterWithLeastReplicas() 81 | if master == nil { 82 | logrus.Errorf("Can't selected a master node!") 83 | } else { 84 | logrus.Printf("Automatically selected master %s", master.String()) 85 | } 86 | } 87 | } 88 | 89 | // Add the new node 90 | newNode := NewClusterNode(newaddr) 91 | newNode.Connect(true) 92 | if !newNode.AssertCluster() { // quit if not in cluster mode 93 | logrus.Fatalf("Node %s is not configured as a cluster node.", newNode.String()) 94 | } 95 | 96 | if err := newNode.LoadInfo(false); err != nil { 97 | logrus.Fatalf("Load new node %s info failed: %s!", newaddr, err.Error()) 98 | } 99 | newNode.AssertEmpty() 100 | self.AddNode(newNode) 101 | 102 | // Send CLUSTER FORGET to all the nodes but the node to remove 103 | logrus.Printf(">>> Send CLUSTER MEET to node %s to make it join the cluster", newNode.String()) 104 | if _, err := newNode.ClusterAddNode(addr); err != nil { 105 | logrus.Fatalf("Add new node %s failed: %s!", newaddr, err.Error()) 106 | } 107 | 108 | // Additional configuration is needed if the node is added as 109 | // a slave. 110 | if context.Bool("slave") { 111 | self.WaitClusterJoin() 112 | if master != nil { 113 | logrus.Printf(">>> Configure node as replica of %s.", master.String()) 114 | newNode.ClusterReplicateWithNodeID(master.Name()) 115 | } else { 116 | logrus.Fatalf("Master node is nil, can't get master info.") 117 | } 118 | } 119 | logrus.Printf("[OK] New node added correctly.") 120 | return nil 121 | } 122 | -------------------------------------------------------------------------------- /autocomplete/bash_autocomplete: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | : ${PROG:=$(basename ${BASH_SOURCE})} 4 | 5 | _cli_bash_autocomplete() { 6 | local cur opts base 7 | COMPREPLY=() 8 | cur="${COMP_WORDS[COMP_CWORD]}" 9 | opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion ) 10 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 11 | return 0 12 | } 13 | 14 | complete -F _cli_bash_autocomplete $PROG 15 | -------------------------------------------------------------------------------- /autocomplete/zsh_autocomplete: -------------------------------------------------------------------------------- 1 | autoload -U compinit && compinit 2 | autoload -U bashcompinit && bashcompinit 3 | 4 | script_dir=$(dirname $0) 5 | source ${script_dir}/bash_autocomplete 6 | -------------------------------------------------------------------------------- /call.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/Sirupsen/logrus" 9 | "github.com/codegangsta/cli" 10 | ) 11 | 12 | // call host:port command arg arg .. arg 13 | var callCommand = cli.Command{ 14 | Name: "call", 15 | Usage: "run command in redis cluster.", 16 | ArgsUsage: `host:port command arg arg .. arg`, 17 | Description: `The call command for call cmd in every redis cluster node.`, 18 | Action: func(context *cli.Context) error { 19 | if context.NArg() < 2 { 20 | fmt.Printf("Incorrect Usage.\n\n") 21 | cli.ShowCommandHelp(context, "call") 22 | logrus.Fatalf("Must provide \"host:port command\" for call command!") 23 | } 24 | 25 | rt := NewRedisTrib() 26 | if err := rt.CallClusterCmd(context); err != nil { 27 | return err 28 | } 29 | return nil 30 | }, 31 | } 32 | 33 | func (self *RedisTrib) CallClusterCmd(context *cli.Context) error { 34 | var addr string 35 | 36 | if addr = context.Args().Get(0); addr == "" { 37 | return errors.New("please check host:port for call command") 38 | } 39 | 40 | if err := self.LoadClusterInfoFromNode(addr); err != nil { 41 | return err 42 | } 43 | 44 | cmd := strings.ToUpper(context.Args().Get(1)) 45 | cmdArgs := ToInterfaceArray(context.Args()[2:]) 46 | 47 | logrus.Printf(">>> Calling %s %s", cmd, cmdArgs) 48 | _, err := self.EachRunCommandAndPrint(cmd, cmdArgs...) 49 | if err != nil { 50 | logrus.Errorf("Command failed: %s", err) 51 | return err 52 | } 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /check.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/codegangsta/cli" 9 | ) 10 | 11 | // check host:port 12 | var checkCommand = cli.Command{ 13 | Name: "check", 14 | Usage: "check the redis cluster.", 15 | ArgsUsage: `host:port`, 16 | Description: `The check command check for redis cluster.`, 17 | Action: func(context *cli.Context) error { 18 | if context.NArg() != 1 { 19 | fmt.Printf("Incorrect Usage.\n\n") 20 | cli.ShowCommandHelp(context, "check") 21 | logrus.Fatalf("Must provide host:port for check command!") 22 | } 23 | 24 | rt := NewRedisTrib() 25 | if err := rt.CheckClusterCmd(context); err != nil { 26 | return err 27 | } 28 | return nil 29 | }, 30 | } 31 | 32 | func (self *RedisTrib) CheckClusterCmd(context *cli.Context) error { 33 | var addr string 34 | 35 | if addr = context.Args().Get(0); addr == "" { 36 | return errors.New("please check host:port for check command") 37 | } 38 | 39 | if err := self.LoadClusterInfoFromNode(addr); err != nil { 40 | return err 41 | } 42 | 43 | self.CheckCluster(false) 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /create.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/codegangsta/cli" 9 | ) 10 | 11 | // create host1:port1 ... hostN:portN 12 | var createCommand = cli.Command{ 13 | Name: "create", 14 | Usage: "create a new redis cluster.", 15 | ArgsUsage: ``, 16 | Description: `The create command create a redis cluster.`, 17 | Flags: []cli.Flag{ 18 | cli.IntFlag{ 19 | Name: "replicas, r", 20 | Value: 0, 21 | Usage: `Slave number for every master created, the default value is none. 22 | 23 | $ redis-trib create <--replicas 1> `, 24 | }, 25 | }, 26 | Action: func(context *cli.Context) error { 27 | if context.NArg() < 1 { 28 | fmt.Printf("Incorrect Usage.\n\n") 29 | cli.ShowCommandHelp(context, "create") 30 | logrus.Fatalf("Must provide at least one \"host:port\" for create command!") 31 | } 32 | 33 | rt := NewRedisTrib() 34 | if err := rt.CreateClusterCmd(context); err != nil { 35 | return err 36 | } 37 | return nil 38 | }, 39 | } 40 | 41 | func (self *RedisTrib) CreateClusterCmd(context *cli.Context) error { 42 | self.SetReplicasNum(context.Int("replicas")) 43 | 44 | logrus.Printf(">>> Creating cluster") 45 | for _, addr := range context.Args() { 46 | if addr == "" { 47 | continue 48 | } 49 | node := NewClusterNode(addr) 50 | node.Connect(true) 51 | if !node.AssertCluster() { 52 | logrus.Fatalf("Node %s is not configured as a cluster node.", node.String()) 53 | } 54 | node.LoadInfo(false) 55 | node.AssertEmpty() 56 | self.AddNode(node) 57 | } 58 | 59 | self.CheckCreateParameters() 60 | logrus.Printf(">>> Performing hash slots allocation on %d nodes...", len(self.Nodes())) 61 | self.AllocSlots() 62 | self.ShowNodes() 63 | YesOrDie("Can I set the above configuration?") 64 | self.FlushNodesConfig() 65 | logrus.Printf(">>> Nodes configuration updated") 66 | logrus.Printf(">>> Assign a different config epoch to each node") 67 | self.AssignConfigEpoch() 68 | logrus.Printf(">>> Sending CLUSTER MEET messages to join the cluster") 69 | self.JoinCluster() 70 | 71 | // Give one second for the join to start, in order to avoid that 72 | // wait_cluster_join will find all the nodes agree about the config as 73 | // they are still empty with unassigned slots. 74 | time.Sleep(time.Second * 1) 75 | self.WaitClusterJoin() 76 | self.FlushNodesConfig() // Useful for the replicas 77 | self.CheckCluster(false) 78 | return nil 79 | } 80 | 81 | func (self *RedisTrib) CheckCreateParameters() bool { 82 | repOpt := self.ReplicasNum() 83 | masters := len(self.Nodes()) / (repOpt + 1) 84 | 85 | if masters < 3 { 86 | logrus.Fatalf("*** ERROR: Invalid configuration for cluster creation.\n"+ 87 | "\t *** Redis Cluster requires at least 3 master nodes.\n"+ 88 | "\t *** This is not possible with %d nodes and %d replicas per node.\n"+ 89 | "\t *** At least %d nodes are required.", len(self.Nodes()), repOpt, 3*(repOpt+1)) 90 | } 91 | return true 92 | } 93 | 94 | func (self *RedisTrib) FlushNodesConfig() { 95 | for _, node := range self.Nodes() { 96 | node.FlushNodeConfig() 97 | } 98 | } 99 | 100 | func (self *RedisTrib) JoinCluster() { 101 | var first *ClusterNode = nil 102 | var addr string 103 | 104 | for _, node := range self.Nodes() { 105 | if first == nil { 106 | first = node 107 | addr = fmt.Sprintf("%s:%d", node.Host(), node.Port()) 108 | continue 109 | } 110 | node.ClusterAddNode(addr) 111 | } 112 | } 113 | 114 | func (self *RedisTrib) AllocSlots() { 115 | // TODO: 116 | var masters [](*ClusterNode) 117 | nodeNum := len(self.Nodes()) 118 | mastersNum := len(self.Nodes()) / (self.ReplicasNum() + 1) 119 | 120 | // The first step is to split instances by IP. This is useful as 121 | // we'll try to allocate master nodes in different physical machines 122 | // (as much as possible) and to allocate slaves of a given master in 123 | // different physical machines as well. 124 | // 125 | // This code assumes just that if the IP is different, than it is more 126 | // likely that the instance is running in a different physical host 127 | // or at least a different virtual machine. 128 | var ips map[string][](*ClusterNode) 129 | ips = make(map[string][](*ClusterNode)) 130 | for _, node := range self.Nodes() { 131 | ips[node.Name()] = append(ips[node.Name()], node) 132 | } 133 | 134 | // Select master instances 135 | logrus.Printf("Using %d masters:", mastersNum) 136 | var interleaved [](*ClusterNode) 137 | stop := false 138 | for !stop { 139 | // Take one node from each IP until we run out of nodes 140 | // across every IP. 141 | for name, nodes := range ips { 142 | if len(nodes) == 0 { 143 | // if this IP has no remaining nodes, check for termination 144 | if len(interleaved) == nodeNum { 145 | // stop when 'interleaved' has accumulated all nodes 146 | stop = true 147 | continue 148 | } 149 | } else { 150 | // else, move one node from this IP to 'interleaved' 151 | interleaved = append(interleaved, nodes[0]) 152 | ips[name] = nodes[1:] 153 | } 154 | } 155 | } 156 | 157 | masters = interleaved[:mastersNum] 158 | interleaved = interleaved[mastersNum:] 159 | nodeNum -= mastersNum 160 | 161 | for _, node := range masters { 162 | logrus.Printf(" -> %s", node.String()) 163 | } 164 | 165 | // Alloc slots on masters 166 | slotsPerNode := float64(ClusterHashSlots) / float64(mastersNum) 167 | first := 0 168 | cursor := 0.0 169 | for index, node := range masters { 170 | last := Round(cursor + slotsPerNode - 1) 171 | if last > ClusterHashSlots || index == len(masters)-1 { 172 | last = ClusterHashSlots - 1 173 | } 174 | 175 | if last < first { 176 | last = first 177 | } 178 | 179 | node.AddSlots(first, last) 180 | first = last + 1 181 | cursor += slotsPerNode 182 | } 183 | // Select N replicas for every master. 184 | // We try to split the replicas among all the IPs with spare nodes 185 | // trying to avoid the host where the master is running, if possible. 186 | // 187 | // Note we loop two times. The first loop assigns the requested 188 | // number of replicas to each master. The second loop assigns any 189 | // remaining instances as extra replicas to masters. Some masters 190 | // may end up with more than their requested number of replicas, but 191 | // all nodes will be used. 192 | assignVerbose := false 193 | assignedReplicas := 0 194 | var slave *ClusterNode 195 | var node *ClusterNode 196 | types := []string{"required", "unused"} 197 | 198 | for _, assign := range types { 199 | for _, m := range masters { 200 | assignedReplicas = 0 201 | for assignedReplicas < self.ReplicasNum() { 202 | if nodeNum == 0 { 203 | break 204 | } 205 | if assignVerbose { 206 | if assign == "required" { 207 | logrus.Printf("Requesting total of %d replicas (%d replicas assigned so far with %d total remaining).", 208 | self.ReplicasNum(), assignedReplicas, nodeNum) 209 | } else if assign == "unused" { 210 | logrus.Printf("Assigning extra instance to replication role too (%d remaining).", nodeNum) 211 | } 212 | } 213 | 214 | // Return the first node not matching our current master 215 | index := getNodeFromSlice(m, interleaved) 216 | if index != -1 { 217 | node = interleaved[index] 218 | } else { 219 | node = nil 220 | } 221 | 222 | // If we found a node, use it as a best-first match. 223 | // Otherwise, we didn't find a node on a different IP, so we 224 | // go ahead and use a same-IP replica. 225 | if node != nil { 226 | slave = node 227 | interleaved = append(interleaved[:index], interleaved[index+1:]...) 228 | } else { 229 | slave, interleaved = interleaved[0], interleaved[1:] 230 | } 231 | slave.SetReplicate(m.Name()) 232 | nodeNum -= 1 233 | assignedReplicas += 1 234 | logrus.Printf("Adding replica %s to %s", slave.String(), m.String()) 235 | 236 | // If we are in the "assign extra nodes" loop, 237 | // we want to assign one extra replica to each 238 | // master before repeating masters. 239 | // This break lets us assign extra replicas to masters 240 | // in a round-robin way. 241 | if assign == "unused" { 242 | break 243 | } 244 | } 245 | } 246 | } 247 | return 248 | } 249 | 250 | func getNodeFromSlice(m *ClusterNode, nodes [](*ClusterNode)) (index int) { 251 | if len(nodes) < 1 { 252 | return -1 253 | } 254 | 255 | for i, node := range nodes { 256 | if m.Host() != node.Host() { 257 | return i 258 | } 259 | } 260 | 261 | return -1 262 | } 263 | -------------------------------------------------------------------------------- /del-node.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/Sirupsen/logrus" 9 | "github.com/codegangsta/cli" 10 | ) 11 | 12 | // del-node host:port node_id 13 | var delNodeCommand = cli.Command{ 14 | Name: "del-node", 15 | Aliases: []string{"del"}, 16 | Usage: "del a redis node from existed cluster.", 17 | ArgsUsage: `host:port node_id`, 18 | Description: `The del-node command delete a node from redis cluster.`, 19 | Action: func(context *cli.Context) error { 20 | if context.NArg() != 2 { 21 | fmt.Printf("Incorrect Usage.\n\n") 22 | cli.ShowCommandHelp(context, "del-node") 23 | logrus.Fatalf("Must provide \"host:port node_id\" for del-node command!") 24 | } 25 | 26 | rt := NewRedisTrib() 27 | if err := rt.DelNodeClusterCmd(context); err != nil { 28 | return err 29 | } 30 | return nil 31 | }, 32 | } 33 | 34 | func (self *RedisTrib) DelNodeClusterCmd(context *cli.Context) error { 35 | var addr string 36 | var nodeid string 37 | 38 | if addr = context.Args().Get(0); addr == "" { 39 | return errors.New("please check host:port for del-node command") 40 | } else if nodeid = context.Args().Get(1); nodeid == "" { 41 | return errors.New("please check node_id for del-node command") 42 | } 43 | 44 | nodeid = strings.ToLower(nodeid) 45 | logrus.Printf(">>> Removing node %s from cluster %s", nodeid, addr) 46 | 47 | // Load cluster information 48 | if err := self.LoadClusterInfoFromNode(addr); err != nil { 49 | return err 50 | } 51 | 52 | // Check if the node exists and is not empty 53 | node := self.GetNodeByName(nodeid) 54 | if node == nil { 55 | logrus.Fatalf("No such node ID %s", nodeid) 56 | } 57 | 58 | if len(node.Slots()) > 0 { 59 | logrus.Fatalf("Node %s is not empty! Reshard data away and try again.", node.String()) 60 | } 61 | // Send CLUSTER FORGET to all the nodes but the node to remove 62 | logrus.Printf(">>> Sending CLUSTER FORGET messages to the cluster...") 63 | for _, n := range self.Nodes() { 64 | if n == nil || n == node { 65 | continue 66 | } 67 | 68 | if n.Replicate() != "" && strings.ToLower(n.Replicate()) == nodeid { 69 | master := self.GetMasterWithLeastReplicas() 70 | if master != nil { 71 | logrus.Printf(">>> %s as replica of %s", n.String(), master.String()) 72 | if _, err := n.ClusterReplicateWithNodeID(master.Name()); err != nil { 73 | logrus.Errorf("%s", err.Error()) 74 | } 75 | } 76 | } 77 | 78 | if _, err := n.ClusterForgetNodeID(nodeid); err != nil { 79 | logrus.Errorf("%s", err.Error()) 80 | } 81 | } 82 | // Finally shutdown the node 83 | logrus.Printf(">>> SHUTDOWN the node.") 84 | if err := node.ClusterNodeShutdown(); err != nil { 85 | return err 86 | } 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /fix.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/codegangsta/cli" 9 | ) 10 | 11 | // fix host:port 12 | // --timeout 13 | var fixCommand = cli.Command{ 14 | Name: "fix", 15 | Usage: "fix the redis cluster.", 16 | ArgsUsage: `host:port`, 17 | Description: `The fix command for fix the redis cluster.`, 18 | Flags: []cli.Flag{ 19 | cli.IntFlag{ 20 | Name: "timeout, t", 21 | Value: MigrateDefaultTimeout, 22 | Usage: `timeout for fix the redis cluster.`, 23 | }, 24 | }, 25 | Action: func(context *cli.Context) error { 26 | if context.NArg() != 1 { 27 | fmt.Printf("Incorrect Usage.\n\n") 28 | cli.ShowCommandHelp(context, "fix") 29 | logrus.Fatalf("Must provide at least \"host:port\" for fix command!") 30 | } 31 | 32 | rt := NewRedisTrib() 33 | if err := rt.FixClusterCmd(context); err != nil { 34 | return err 35 | } 36 | return nil 37 | }, 38 | } 39 | 40 | func (self *RedisTrib) FixClusterCmd(context *cli.Context) error { 41 | var addr string 42 | 43 | if addr = context.Args().Get(0); addr == "" { 44 | return errors.New("please check host:port for fix command") 45 | } 46 | 47 | self.SetFix(true) 48 | timeout := context.Int("timeout") 49 | self.SetTimeout(timeout) 50 | if err := self.LoadClusterInfoFromNode(addr); err != nil { 51 | return err 52 | } 53 | 54 | self.CheckCluster(false) 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /hacking/author.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # script from http://stackoverflow.com/questions/12133583 3 | set -e 4 | 5 | # Get a list of authors ordered by number of commits 6 | # and remove the commit count column 7 | AUTHORS=$(git --no-pager shortlog -nse --no-merges | cut -f 2- ) 8 | if [ -z "$AUTHORS" ] ; then 9 | echo "Authors list was empty" 10 | exit 1 11 | fi 12 | 13 | # Display the authors list and write it to the file 14 | echo "$AUTHORS" | tee "$(git rev-parse --show-toplevel)/AUTHORS.TXT" 15 | -------------------------------------------------------------------------------- /hacking/start-redis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export PS4='+ [`basename ${BASH_SOURCE[0]}`:$LINENO ${FUNCNAME[0]} \D{%F %T} $$ ] ' 4 | 5 | # install redis use brew 6 | # brew install redis@3.2 7 | 8 | # export redis-server cmd path 9 | export PATH=${PATH}:/usr/local/opt/redis@3.2/bin/ 10 | 11 | MYNAME="${0##*/}" 12 | CURDIR=$(cd "$(dirname "$0")"; pwd) 13 | ACTION="" 14 | 15 | ## redis instance data store path 16 | DBCACHEPATH="/usr/local/var/db/redis" 17 | 18 | usage() { 19 | cat << USAGE 20 | Usage: bash ${MYNAME} [-h] 21 | action {startall|stopall} 22 | 23 | Redis start/stop control scripts. 24 | 25 | Optional arguments: 26 | -h, --help show this help message and exit 27 | 28 | Require: 29 | action {startall|stopall} 30 | 31 | USAGE 32 | 33 | exit 1 34 | } 35 | 36 | # 37 | # Parses command-line options. 38 | # usage: parse_options "$@" || exit $? 39 | # 40 | parse_options() { 41 | declare -a argv 42 | 43 | while [[ $# -gt 0 ]]; do 44 | case $1 in 45 | -h|--help) 46 | usage 47 | exit 48 | ;; 49 | --) 50 | shift 51 | argv=("${argv[@]}" "${@}") 52 | break 53 | ;; 54 | -*) 55 | echo "command line: unrecognized option $1" >&2 56 | return 1 57 | ;; 58 | *) 59 | argv=("${argv[@]}" "${1}") 60 | shift 61 | ;; 62 | esac 63 | done 64 | 65 | case ${#argv[@]} in 66 | 1) 67 | ACTION="${argv[0]}" 68 | ;; 69 | 0|*) 70 | usage 1>&2 71 | return 1 72 | ;; 73 | esac 74 | } 75 | 76 | startall() { 77 | for i in `ls $CURDIR/config | grep conf`; do 78 | cfgFile="${CURDIR}/config/$i" 79 | redis-server $cfgFile & 80 | done 81 | } 82 | 83 | stopall() { 84 | for i in `ps aux | grep redis-server | grep -v grep | awk '{print $2}'`; do 85 | echo "Starting to stop redis: $i" 86 | kill $i; 87 | done 88 | } 89 | 90 | remove() { 91 | echo "Starting Stop all instance." 92 | stopall 93 | 94 | sleep 20 95 | echo "Starting remove redis history data." 96 | if [ x$DBCACHEPATH != "x" ]; then 97 | for f in `ls $DBCACHEPATH/*.rdb`; do 98 | rm -rf "$f" 99 | done 100 | fi 101 | } 102 | 103 | ################################## main route ################################# 104 | parse_options "${@}" || usage 105 | 106 | case "${ACTION}" in 107 | startall) 108 | #rh_status_q && exit 0 109 | ${ACTION} 110 | ;; 111 | stopall) 112 | #rh_status_q || exit 0 113 | ${ACTION} 114 | #pstatus && forcestop -9 115 | ;; 116 | remove) 117 | ${ACTION} 118 | ;; 119 | *) 120 | echo "Usage: $0 {startall|stopall}" 121 | exit 2 122 | esac 123 | exit $? 124 | 125 | #./redis-trib create --replicas 1 127.0.0.1:6380 127.0.0.1:6379 127.0.0.1:6378 127.0.0.1:7380 127.0.0.1:7379 127.0.0.1:7378 126 | -------------------------------------------------------------------------------- /import.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/codegangsta/cli" 9 | "github.com/garyburd/redigo/redis" 10 | ) 11 | 12 | // import host:port 13 | // --from 14 | // --copy 15 | // --replace 16 | var importCommand = cli.Command{ 17 | Name: "import", 18 | Usage: "import operation for redis cluster.", 19 | ArgsUsage: `host:port`, 20 | Description: `The import command for import data from one to another node.`, 21 | Flags: []cli.Flag{ 22 | cli.StringFlag{ 23 | Name: "from", 24 | Usage: `Start slot redis cluster.`, 25 | }, 26 | cli.BoolFlag{ 27 | Name: "copy", 28 | Usage: `Copy flag for import operation.`, 29 | }, 30 | cli.BoolFlag{ 31 | Name: "replace", 32 | Usage: `Replace flag for import operation.`, 33 | }, 34 | }, 35 | Action: func(context *cli.Context) error { 36 | if context.NArg() != 1 { 37 | fmt.Printf("Incorrect Usage.\n\n") 38 | cli.ShowCommandHelp(context, "import") 39 | logrus.Fatalf("Must provide \"host:port\" for import command!") 40 | } 41 | 42 | rt := NewRedisTrib() 43 | if err := rt.ImportClusterCmd(context); err != nil { 44 | return err 45 | } 46 | return nil 47 | }, 48 | } 49 | 50 | func (self *RedisTrib) ImportClusterCmd(context *cli.Context) error { 51 | var addr string 52 | var source string 53 | 54 | if source = context.String("from"); source == "" { 55 | logrus.Fatalf("Option \"--from\" is required for import command!") 56 | } else if addr = context.Args().Get(0); addr == "" { 57 | return errors.New("please check host:port for import command") 58 | } 59 | 60 | useCopy := context.Bool("copy") 61 | useReplace := context.Bool("replace") 62 | 63 | logrus.Printf(">>> Importing data from %s to cluster %s", source, addr) 64 | 65 | // Load nodes info before parsing options, otherwise we can't 66 | // handle --weight. 67 | if err := self.LoadClusterInfoFromNode(addr); err != nil { 68 | return err 69 | } 70 | 71 | // Check cluster, only proceed if it looks sane. 72 | self.CheckCluster(false) 73 | 74 | // Connect to the source node. 75 | logrus.Printf(">>> Connecting to the source Redis instance") 76 | srcNode := NewClusterNode(source) 77 | 78 | if srcNode.AssertCluster() { 79 | logrus.Errorf("The source node should not be a cluster node.") 80 | } 81 | dbsize, _ := srcNode.Dbsize() 82 | logrus.Printf("*** Importing %d keys from DB 0", dbsize) 83 | 84 | // Build a slot -> node map 85 | slots := make(map[int]*ClusterNode) 86 | for _, node := range self.Nodes() { 87 | for key, _ := range node.Slots() { 88 | slots[key] = node 89 | } 90 | } 91 | 92 | // Use SCAN to iterate over the keys, migrating to the 93 | // right node as needed. 94 | var keys []string 95 | cursor := 0 96 | for { 97 | // we scan with our iter offset, starting at 0 98 | if arr, err := redis.MultiBulk(srcNode.R().Do("SCAN", cursor)); err != nil { 99 | logrus.Errorf("Do scan in import cmd failed: %s", err.Error()) 100 | } else { 101 | // now we get the iter and the keys from the multi-bulk reply 102 | cursor, _ = redis.Int(arr[0], nil) 103 | keys, _ = redis.Strings(arr[1], nil) 104 | } 105 | // check if we need to stop... 106 | if cursor == 0 { 107 | break 108 | } 109 | 110 | var cmd []interface{} 111 | for _, key := range keys { 112 | slot := Key2Slot(key) 113 | target := slots[int(slot)] 114 | logrus.Printf("Migrating %s to %s - OK", key, target.String()) 115 | 116 | cmd = append(cmd, target.Host(), target.Port(), key, 0, MigrateDefaultTimeout) 117 | 118 | if useCopy { 119 | cmd = append(cmd, useCopy) 120 | } 121 | 122 | if useReplace { 123 | cmd = append(cmd, useReplace) 124 | } 125 | 126 | if _, err := srcNode.Call("migrate", cmd...); err != nil { 127 | logrus.Printf("Migrating %s to %s - %s", key, target.String(), err.Error()) 128 | } else { 129 | logrus.Printf("Migrating %s to %s - OK", key, target.String()) 130 | } 131 | } 132 | } 133 | return nil 134 | } 135 | -------------------------------------------------------------------------------- /info.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/Sirupsen/logrus" 8 | "github.com/codegangsta/cli" 9 | ) 10 | 11 | // info host:port 12 | var infoCommand = cli.Command{ 13 | Name: "info", 14 | Usage: "display the info of redis cluster.", 15 | ArgsUsage: `host:port`, 16 | Description: `The info command get info from redis cluster.`, 17 | Action: func(context *cli.Context) error { 18 | if context.NArg() != 1 { 19 | fmt.Printf("Incorrect Usage.\n\n") 20 | cli.ShowCommandHelp(context, "info") 21 | logrus.Fatalf("Must provide host:port for info command!") 22 | } 23 | 24 | rt := NewRedisTrib() 25 | if err := rt.InfoClusterCmd(context); err != nil { 26 | return err 27 | } 28 | return nil 29 | }, 30 | } 31 | 32 | func (self *RedisTrib) InfoClusterCmd(context *cli.Context) error { 33 | var addr string 34 | 35 | if addr = context.Args().Get(0); addr == "" { 36 | return errors.New("please check host:port for info command") 37 | } 38 | 39 | if err := self.LoadClusterInfoFromNode(addr); err != nil { 40 | return err 41 | } 42 | 43 | self.ShowClusterInfo() 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/Sirupsen/logrus" 9 | "github.com/codegangsta/cli" 10 | ) 11 | 12 | // version will be the hash that the binary was built from 13 | // and will be populated by the Makefile 14 | var version = "" 15 | 16 | // gitCommit will be the hash that the binary was built from 17 | // and will be populated by the Makefile 18 | var gitCommit = "" 19 | 20 | const ( 21 | // name holds the name of this program 22 | name = "redis-trib" 23 | usage = `Redis Cluster command line utility. 24 | 25 | For check, fix, reshard, del-node, set-timeout you can specify the host and port 26 | of any working node in the cluster.` 27 | ) 28 | 29 | // runtimeFlags is the list of supported global command-line flags 30 | var runtimeFlags = []cli.Flag{ 31 | cli.BoolFlag{ 32 | Name: "debug", 33 | Usage: "enable debug output for logging", 34 | }, 35 | cli.BoolFlag{ 36 | Name: "verbose", 37 | Usage: "verbose global flag for output.", 38 | }, 39 | cli.StringFlag{ 40 | Name: "log", 41 | Value: "", 42 | Usage: "set the log file path where internal debug information is written", 43 | }, 44 | cli.StringFlag{ 45 | Name: "log-format", 46 | Value: "text", 47 | Usage: "set the format used by logs ('text' (default), or 'json')", 48 | }, 49 | } 50 | 51 | // runtimeBeforeSubcommands is the function to run before command-line 52 | // parsing occurs. 53 | var runtimeBeforeSubcommands = beforeSubcommands 54 | 55 | // runtimeCommandNotFound is the function to handle an invalid sub-command. 56 | var runtimeCommandNotFound = commandNotFound 57 | 58 | // runtimeCommands is all sub-command 59 | var runtimeCommands = []cli.Command{ 60 | addNodeCommand, 61 | callCommand, 62 | checkCommand, 63 | createCommand, 64 | delNodeCommand, 65 | fixCommand, 66 | importCommand, 67 | infoCommand, 68 | rebalanceCommand, 69 | reshardCommand, 70 | setTimeoutCommand, 71 | } 72 | 73 | func beforeSubcommands(context *cli.Context) error { 74 | if context.GlobalBool("debug") { 75 | logrus.SetLevel(logrus.DebugLevel) 76 | } 77 | 78 | if context.GlobalBool("verbose") { 79 | os.Setenv("ENV_MODE_VERBOSE", "true") 80 | } 81 | 82 | if path := context.GlobalString("log"); path != "" { 83 | f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666) 84 | if err != nil { 85 | return err 86 | } 87 | logrus.SetOutput(f) 88 | } 89 | 90 | switch context.GlobalString("log-format") { 91 | case "text": 92 | // retain logrus's default. 93 | case "json": 94 | logrus.SetFormatter(new(logrus.JSONFormatter)) 95 | default: 96 | logrus.Fatalf("unknown log-format %q", context.GlobalString("log-format")) 97 | } 98 | return nil 99 | } 100 | 101 | // function called when an invalid command is specified which causes the 102 | // runtime to error. 103 | func commandNotFound(c *cli.Context, command string) { 104 | err := fmt.Errorf("invalid command %q", command) 105 | fatal(err) 106 | } 107 | 108 | // makeVersionString returns a multi-line string describing the runtime version. 109 | func makeVersionString() string { 110 | v := []string{ 111 | version, 112 | } 113 | if gitCommit != "" { 114 | v = append(v, fmt.Sprintf("commit: %s", gitCommit)) 115 | } 116 | 117 | return strings.Join(v, "\n") 118 | } 119 | 120 | func main() { 121 | app := cli.NewApp() 122 | 123 | app.Name = name 124 | app.Writer = os.Stdout 125 | app.Usage = usage 126 | app.Version = makeVersionString() 127 | app.Flags = runtimeFlags 128 | app.Author = "soarpenguin" 129 | app.Email = "soarpenguin@gmail.com" 130 | app.EnableBashCompletion = true 131 | app.CommandNotFound = runtimeCommandNotFound 132 | app.Before = runtimeBeforeSubcommands 133 | app.Commands = runtimeCommands 134 | 135 | if err := app.Run(os.Args); err != nil { 136 | fatal(err) 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /reshard.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "os" 8 | "strconv" 9 | "strings" 10 | 11 | "github.com/Sirupsen/logrus" 12 | "github.com/codegangsta/cli" 13 | ) 14 | 15 | // reshard host:port 16 | // --from 17 | // --to 18 | // --slots 19 | // --yes 20 | // --timeout 21 | // --pipeline 22 | var reshardCommand = cli.Command{ 23 | Name: "reshard", 24 | Usage: "reshard the redis cluster.", 25 | ArgsUsage: `host:port`, 26 | Description: `The reshard command for reshard a redis cluster.`, 27 | Flags: []cli.Flag{ 28 | cli.StringFlag{ 29 | Name: "from", 30 | Usage: `Start slot number for reshard redis cluster.`, 31 | }, 32 | cli.StringFlag{ 33 | Name: "to", 34 | Usage: `Dest slot number for reshard redis cluster.`, 35 | }, 36 | cli.IntFlag{ 37 | Name: "slots", 38 | Usage: `Slots for reshard redis cluster.`, 39 | }, 40 | cli.BoolFlag{ 41 | Name: "yes", 42 | Usage: `Auto agree the config for reshard.`, 43 | }, 44 | cli.IntFlag{ 45 | Name: "timeout", 46 | Usage: `Timeout for reshard the redis cluster.`, 47 | }, 48 | cli.StringFlag{ 49 | Name: "pipeline", 50 | Value: "", 51 | Usage: `Pipeline for reshard redis cluster.`, 52 | }, 53 | }, 54 | Action: func(context *cli.Context) error { 55 | if context.NArg() != 1 { 56 | fmt.Printf("Incorrect Usage.\n\n") 57 | cli.ShowCommandHelp(context, "reshard") 58 | logrus.Fatalf("Must provide at least \"host:port\" for reshard command!") 59 | } 60 | rt := NewRedisTrib() 61 | if err := rt.ReshardClusterCmd(context); err != nil { 62 | return err 63 | } 64 | return nil 65 | }, 66 | } 67 | 68 | func (self *RedisTrib) ReshardClusterCmd(context *cli.Context) error { 69 | var addr string 70 | 71 | if addr = context.Args().Get(0); addr == "" { 72 | return errors.New("please check host:port for reshard command") 73 | } 74 | 75 | if err := self.LoadClusterInfoFromNode(addr); err != nil { 76 | return err 77 | } 78 | 79 | self.CheckCluster(false) 80 | 81 | if len(self.Errors()) > 0 { 82 | logrus.Fatalf("*** Please fix your cluster problem before resharding.") 83 | } 84 | 85 | if context.Int("timeout") > 0 { 86 | self.SetTimeout(context.Int("timeout")) 87 | } 88 | 89 | // Get number of slots 90 | var numSlots int 91 | if context.Int("slots") != 0 { 92 | numSlots = context.Int("slots") 93 | } else { 94 | numSlots = 0 95 | reader := bufio.NewReader(os.Stdin) 96 | for { 97 | if numSlots <= 0 || numSlots > ClusterHashSlots { 98 | fmt.Printf("How many slots do you want to move (from 1 to %d)? ", ClusterHashSlots) 99 | text, _ := reader.ReadString('\n') 100 | num, err := strconv.ParseInt(strings.TrimSpace(text), 10, 0) 101 | if err != nil { 102 | continue 103 | } 104 | numSlots = int(num) 105 | } else { 106 | break 107 | } 108 | } 109 | } 110 | 111 | // Get the target instance 112 | var target *ClusterNode 113 | if context.String("to") != "" { 114 | target = self.GetNodeByName(context.String("to")) 115 | 116 | if target == nil || target.HasFlag("slave") { 117 | logrus.Fatalf("*** The specified node is not known or not a master, please retry.") 118 | } 119 | } else { 120 | target = nil 121 | reader := bufio.NewReader(os.Stdin) 122 | 123 | for { 124 | if target != nil { 125 | break 126 | } 127 | fmt.Printf("What is the receiving node ID? ") 128 | text, _ := reader.ReadString('\n') 129 | target = self.GetNodeByName(strings.TrimSpace(text)) 130 | 131 | if target == nil || target.HasFlag("slave") { 132 | logrus.Printf("*** The specified node is not known or not a master, please retry.") 133 | target = nil 134 | } 135 | } 136 | } 137 | 138 | // Get the source instances 139 | var sources []interface{} 140 | from := strings.TrimSpace(context.String("from")) 141 | if from != "" { 142 | srcArray := strings.Split(from, ",") 143 | 144 | for _, nodeID := range srcArray { 145 | nodeID = strings.TrimSpace(nodeID) 146 | if nodeID == "all" { 147 | sources = sources[:0] 148 | sources = append(sources, "all") 149 | break 150 | } else { 151 | node := self.GetNodeByName(nodeID) 152 | if node == nil || node.HasFlag("slave") { 153 | logrus.Fatalf("*** The specified node is not known or not a master, please retry.") 154 | } 155 | sources = append(sources, node) 156 | } 157 | } 158 | } else { 159 | logrus.Printf("Please enter all the source node IDs.\n" + 160 | "\t Type 'all' to use all the nodes as source nodes for the hash slots.\n" + 161 | "\t Type 'done' once you entered all the source nodes IDs.") 162 | 163 | reader := bufio.NewReader(os.Stdin) 164 | for { 165 | fmt.Printf("Source node #%d:", len(sources)+1) 166 | text, _ := reader.ReadString('\n') 167 | text = strings.TrimSpace(text) 168 | src := self.GetNodeByName(text) 169 | 170 | if text == "done" { 171 | break 172 | } else if text == "all" { 173 | sources = sources[:0] 174 | sources = append(sources, "all") 175 | break 176 | } else if src == nil || src.HasFlag("slave") { 177 | logrus.Warningf("*** The specified node is not known or not a master, please retry.") 178 | } else if src.Name() == target.Name() { 179 | logrus.Warningf("*** It is not possible to use the target node as source node.") 180 | } else { 181 | sources = append(sources, src) 182 | } 183 | } 184 | } 185 | 186 | if len(sources) <= 0 { 187 | logrus.Fatalf("*** No source nodes given, operation aborted") 188 | } 189 | 190 | if len(sources) == 1 { 191 | first := sources[0] 192 | 193 | // Handle soures == all. 194 | str, found := first.(string) 195 | if found && str == "all" { 196 | sources = sources[:0] 197 | 198 | for _, node := range self.Nodes() { 199 | if node.Name() == target.Name() || node.HasFlag("slave") { 200 | continue 201 | } 202 | sources = append(sources, node) 203 | } 204 | } 205 | } 206 | 207 | // Check if the destination node is the same of any source nodes. 208 | for _, node := range sources { 209 | if node != nil { 210 | if cnode, ok := node.(ClusterNode); ok { 211 | if cnode.Name() == target.Name() { 212 | logrus.Fatalf("*** Target node is also listed among the source nodes!") 213 | } 214 | } 215 | } 216 | } 217 | 218 | logrus.Printf("Ready to move %d slots.", numSlots) 219 | logrus.Printf(" Source nodes:") 220 | var srcs ClusterArray 221 | for _, node := range sources { 222 | if cnode, ok := node.(ClusterNode); ok { 223 | fmt.Printf("\t%s", cnode.InfoString()) 224 | srcs = append(srcs, cnode) 225 | } 226 | } 227 | logrus.Printf(" Destination node: %s", target.InfoString()) 228 | 229 | // TODO: ComputeReshardTable 230 | reshardTable := self.ComputeReshardTable(srcs, numSlots) 231 | logrus.Printf(" Resharding plan:") 232 | self.ShowReshardTable(reshardTable) 233 | 234 | if !context.Bool("yes") { 235 | fmt.Printf("Do you want to proceed with the proposed reshard plan (yes/no)? ") 236 | reader := bufio.NewReader(os.Stdin) 237 | text, _ := reader.ReadString('\n') 238 | 239 | if !strings.EqualFold(strings.TrimSpace(text), "yes") { 240 | logrus.Fatalf("*** Aborting...") 241 | } 242 | } 243 | 244 | pipeline := MigrateDefaultPipeline 245 | if context.String("pipeline") != "" { 246 | pnum, err := strconv.Atoi(context.String("pipeline")) 247 | if err == nil { 248 | pipeline = pnum 249 | } 250 | } 251 | opts := &MoveOpts{ 252 | Dots: true, 253 | Pipeline: pipeline, 254 | } 255 | // TODO: Move slots 256 | for _, e := range reshardTable { 257 | // move slot 258 | self.MoveSlot(e, target, opts) 259 | } 260 | 261 | return nil 262 | } 263 | -------------------------------------------------------------------------------- /script/.validate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e -o pipefail 4 | 5 | if [ -z "$VALIDATE_UPSTREAM" ]; then 6 | # this is kind of an expensive check, so let's not do this twice if we 7 | # are running more than one validate bundlescript 8 | 9 | VALIDATE_REPO='https://github.com/soarpenguin/redis-trib.git' 10 | VALIDATE_BRANCH='master' 11 | 12 | if [ "$TRAVIS" = 'true' -a "$TRAVIS_PULL_REQUEST" != 'false' ]; then 13 | VALIDATE_REPO="https://github.com/${TRAVIS_REPO_SLUG}.git" 14 | VALIDATE_BRANCH="${TRAVIS_BRANCH}" 15 | fi 16 | 17 | VALIDATE_HEAD="$(git rev-parse --verify HEAD)" 18 | 19 | git fetch -q "$VALIDATE_REPO" "refs/heads/$VALIDATE_BRANCH" 20 | VALIDATE_UPSTREAM="$(git rev-parse --verify FETCH_HEAD)" 21 | 22 | VALIDATE_COMMIT_LOG="$VALIDATE_UPSTREAM..$VALIDATE_HEAD" 23 | VALIDATE_COMMIT_DIFF="$VALIDATE_UPSTREAM...$VALIDATE_HEAD" 24 | 25 | validate_diff() { 26 | if [ "$VALIDATE_UPSTREAM" != "$VALIDATE_HEAD" ]; then 27 | git diff "$VALIDATE_COMMIT_DIFF" "$@" 28 | fi 29 | } 30 | validate_log() { 31 | if [ "$VALIDATE_UPSTREAM" != "$VALIDATE_HEAD" ]; then 32 | git log "$VALIDATE_COMMIT_LOG" "$@" 33 | fi 34 | } 35 | fi 36 | -------------------------------------------------------------------------------- /script/clean_godeps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Removes unncessary files from the vendor directories 4 | # 5 | # A list for files to be removed is used instead of excluding files. 6 | # The reason is that this makes the setup simpler and prevents 7 | # from removing files by accident 8 | # 9 | # In general it should always be checked manually which files were removed. 10 | # For example some projects like stretchr/testify have two LICENSE files 11 | # with different names, where it is ok that one is removed. 12 | # 13 | # We keep the CHANGELOG in as this makes it easy visible when updating dependencies 14 | # on what has changed. 15 | 16 | # In general the following files should be kept: 17 | # * .go 18 | # * LICENSE* CHANGELOG*, PATENT*, CONTRIBUTORS*, README* 19 | # * `.s`, `.c`, `.cpp`, `.cc`, `.c++` 20 | # * `.m`, `.mm`, `.m++` 21 | 22 | # Finds all vendor directories 23 | DIR_LIST=`find . -type d -name Godeps` 24 | 25 | ## Removing all yaml files which are normally config files (travis.yml) 26 | find $DIR_LIST -type f -name "*.yml" -exec rm -r {} \; 27 | 28 | ## Removing all golang test files 29 | find $DIR_LIST -type f -name "*_test.go" -exec rm -r {} \; 30 | 31 | ## Removing all files starting with a dot (like .gitignore) 32 | find $DIR_LIST -type f -name ".*" -exec rm -r {} \; 33 | 34 | ## Removing all .txt files which are normally test data or docs 35 | ## Excluding files mentioned above as e.x. nranchev/go-libGeoIP has the license in a .txt file 36 | find $DIR_LIST -type f -name "*.txt" -a ! \( -iname "LICENSE.*" -o -iname "CHANGELOG.*" -o -iname "PATENT.*" -o -iname "CONTRIBUTORS.*" -o -iname "README.*" \) -exec rm -r {} \; 37 | 38 | ## Removing all *.cfg files 39 | find $DIR_LIST -type f -name "*.cfg" -exec rm -r {} \; 40 | 41 | ## Removing all *.bat files 42 | find $DIR_LIST -type f -name "*.bat" -exec rm -r {} \; 43 | 44 | # Remove directories used for versioning 45 | find $DIR_LIST -type d -name ".bzr" -o -name ".git" -exec rm -rf {} \; 46 | -------------------------------------------------------------------------------- /script/lint: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | source "${SCRIPTDIR}/.validate" 5 | 6 | IFS=$'\n' 7 | files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | grep -v '^vendor/' || true) ) 8 | unset IFS 9 | 10 | errors=() 11 | for f in "${files[@]}"; do 12 | failedLint=$(golint "$f") 13 | if [ "$failedLint" ]; then 14 | errors+=( "$failedLint" ) 15 | fi 16 | done 17 | 18 | if [ ${#errors[@]} -eq 0 ]; then 19 | echo 'Congratulations! All Go source files have been linted.' 20 | else 21 | { 22 | echo "Errors from golint:" 23 | for err in "${errors[@]}"; do 24 | echo "$err" 25 | done 26 | echo 27 | echo 'Please fix the above errors. You can test via "golint" and commit the result.' 28 | echo 29 | } >&2 30 | false 31 | fi 32 | -------------------------------------------------------------------------------- /script/validate-gofmt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | source "${SCRIPTDIR}/.validate" 5 | 6 | IFS=$'\n' 7 | files=( $(validate_diff --diff-filter=ACMR --name-only -- '*.go' | 8 | grep -v '^vendor/' | 9 | grep -v '^Godeps/' || true) ) 10 | unset IFS 11 | 12 | badFiles=() 13 | for f in "${files[@]}"; do 14 | # we use "git show" here to validate that what's committed is formatted 15 | if [ "$(git show "$VALIDATE_HEAD:$f" | gofmt -s -l)" ]; then 16 | badFiles+=( "$f" ) 17 | fi 18 | done 19 | 20 | if [ ${#badFiles[@]} -eq 0 ]; then 21 | echo 'Congratulations! All Go source files are properly formatted.' 22 | else 23 | { 24 | echo "These files are not properly gofmt'd:" 25 | for f in "${badFiles[@]}"; do 26 | echo " - $f" 27 | done 28 | echo 29 | echo 'Please reformat the above files using "gofmt -s -w" and commit the result.' 30 | echo 31 | } >&2 32 | false 33 | fi 34 | -------------------------------------------------------------------------------- /set-timeout.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/Sirupsen/logrus" 9 | "github.com/codegangsta/cli" 10 | ) 11 | 12 | // set-timeout host:port milliseconds 13 | var setTimeoutCommand = cli.Command{ 14 | Name: "set-timeout", 15 | Usage: "set timeout configure for redis cluster.", 16 | ArgsUsage: `host:port milliseconds`, 17 | Description: `The set-timeout command set a timeout for redis cluster.`, 18 | Action: func(context *cli.Context) error { 19 | if context.NArg() != 2 { 20 | fmt.Printf("Incorrect Usage.\n\n") 21 | cli.ShowCommandHelp(context, "set-timeout") 22 | logrus.Fatalf("Must provide \"host:port milliseconds\" for set-timeout command!") 23 | } 24 | 25 | rt := NewRedisTrib() 26 | if err := rt.SetTimeoutClusterCmd(context); err != nil { 27 | return err 28 | } 29 | return nil 30 | }, 31 | } 32 | 33 | func (self *RedisTrib) SetTimeoutClusterCmd(context *cli.Context) error { 34 | var addr string 35 | 36 | if addr = context.Args().Get(0); addr == "" { 37 | return errors.New("please check host:port for info command") 38 | } 39 | 40 | timeout := context.Args().Get(1) 41 | millisec, err := strconv.ParseInt(timeout, 0, 32) 42 | if err != nil { 43 | logrus.Fatalf("Please check the timeout format is number: %s", err.Error()) 44 | } else if millisec < 100 { 45 | logrus.Fatalf("Setting a node timeout of less than 100 milliseconds is a bad idea.") 46 | } 47 | 48 | // Load cluster information 49 | if err := self.LoadClusterInfoFromNode(addr); err != nil { 50 | return err 51 | } 52 | okCount := 0 53 | errCount := 0 54 | 55 | // Send CLUSTER FORGET to all the nodes but the node to remove 56 | logrus.Printf(">>> Reconfiguring node timeout in every cluster node...") 57 | 58 | for _, node := range self.Nodes() { 59 | if _, err := node.Call("CONFIG", "set", "cluster-node-timeout", millisec); err != nil { 60 | logrus.Errorf("ERR setting node-timeot in set operation for %s: %s", node.String(), err.Error()) 61 | errCount += 1 62 | } else { 63 | if _, err := node.Call("CONFIG", "rewrite"); err != nil { 64 | logrus.Errorf("ERR setting node-timeot in rewrite operation for %s: %s", node.String(), err.Error()) 65 | errCount += 1 66 | } else { 67 | logrus.Printf("*** New timeout set for %s", node.NodeString()) 68 | okCount += 1 69 | } 70 | } 71 | } 72 | 73 | logrus.Printf(">>> New node timeout set. %d OK, %d ERR.", okCount, errCount) 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "math" 7 | "os" 8 | "strings" 9 | 10 | "github.com/Sirupsen/logrus" 11 | ) 12 | 13 | // fatal prints the error's details if it is a libcontainer specific error type 14 | // then exits the program with an exit status of 1. 15 | func fatal(err error) { 16 | // make sure the error is written to the logger 17 | logrus.Error(err) 18 | fmt.Fprintln(os.Stderr, err) 19 | os.Exit(1) 20 | } 21 | 22 | func Uniq(list []string) []string { 23 | uniqset := make(map[string]bool, len(list)) 24 | for _, x := range list { 25 | uniqset[x] = true 26 | } 27 | result := make([]string, 0, len(uniqset)) 28 | for x := range uniqset { 29 | result = append(result, x) 30 | } 31 | return result 32 | } 33 | 34 | func MergeNumArray2NumRange(array []int) string { 35 | var i = 0 36 | var result = "" 37 | 38 | for j, value := range array { 39 | if j == len(array)-1 { 40 | if i == j { 41 | result += fmt.Sprintf("%d", array[j]) 42 | } else { 43 | result += fmt.Sprintf("%d-%d", array[i], array[j]) 44 | } 45 | break 46 | } 47 | 48 | if value != array[j+1]-1 { 49 | if j == i { 50 | result += fmt.Sprintf("%d,", array[i]) 51 | } else { 52 | result += fmt.Sprintf("%d-%d,", array[i], array[j]) 53 | } 54 | i = j + 1 55 | } 56 | } 57 | 58 | return result 59 | } 60 | 61 | func ToInterfaceArray(in []string) []interface{} { 62 | result := make([]interface{}, len(in)) 63 | 64 | for i, val := range in { 65 | result[i] = interface{}(val) 66 | } 67 | 68 | return result 69 | } 70 | 71 | func ToStringArray(in []interface{}) []string { 72 | result := make([]string, len(in)) 73 | 74 | for i, val := range in { 75 | result[i] = fmt.Sprintf("%s", val) 76 | } 77 | 78 | return result 79 | } 80 | 81 | func YesOrDie(msg string) { 82 | fmt.Printf("%s (type 'yes' to accept): ", msg) 83 | 84 | reader := bufio.NewReader(os.Stdin) 85 | text, _ := reader.ReadString('\n') 86 | 87 | if !strings.EqualFold(strings.TrimSpace(text), "yes") { 88 | logrus.Fatalf("*** Aborting...") 89 | } 90 | } 91 | 92 | func Round(num float64) int { 93 | return int(num + math.Copysign(0.5, num)) 94 | } 95 | 96 | func ClusterNodeArray2String(nodes [](*ClusterNode)) (result string) { 97 | for _, node := range nodes { 98 | if node != nil { 99 | result += node.String() + "," 100 | } 101 | } 102 | 103 | if len(result) > 0 { 104 | strings.TrimRight(result, ",") 105 | } 106 | 107 | return result 108 | } 109 | 110 | func NumArray2String(nums []int) (result string) { 111 | if len(nums) > 0 { 112 | result = fmt.Sprintf("%d", nums[0]) 113 | for _, id := range nums[1:] { 114 | result += fmt.Sprintf("%s,%d", result, id) 115 | } 116 | if len(result) > 0 { 117 | strings.TrimRight(result, ",") 118 | } 119 | } 120 | return result 121 | } 122 | 123 | /* CRC16 implementation according to CCITT standards. 124 | * 125 | * Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the 126 | * following parameters: 127 | * 128 | * Name : "XMODEM", also known as "ZMODEM", "CRC-16/ACORN" 129 | * Width : 16 bit 130 | * Poly : 1021 (That is actually x^16 + x^12 + x^5 + 1) 131 | * Initialization : 0000 132 | * Reflect Input byte : False 133 | * Reflect Output CRC : False 134 | * Xor constant to output CRC : 0000 135 | * Output for "123456789" : 31C3 136 | */ 137 | 138 | var crc16tab = [...]uint16{ 139 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 140 | 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 141 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 142 | 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 143 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 144 | 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 145 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 146 | 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 147 | 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 148 | 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 149 | 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 150 | 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 151 | 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 152 | 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 153 | 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 154 | 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 155 | 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 156 | 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 157 | 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 158 | 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 159 | 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 160 | 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 161 | 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 162 | 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 163 | 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 164 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 165 | 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 166 | 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 167 | 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 168 | 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 169 | 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 170 | 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, 171 | } 172 | 173 | func crc16(s string) uint16 { 174 | var crc uint16 175 | for i := 0; i < len(s); i++ { 176 | b := s[i] 177 | crc = (crc << 8) ^ crc16tab[byte(crc>>8)^b] 178 | } 179 | return crc 180 | } 181 | 182 | const ( 183 | HASHTAG_START = "{" 184 | HASHTAG_END = "}" 185 | DEFAULT_SLOT_NUM = 16384 186 | ) 187 | 188 | // Turn a key name into the corrisponding Redis Cluster slot. 189 | func Key2Slot(key string) uint16 { 190 | // Only hash what is inside {...} if there is such a pattern in the key. 191 | // Note that the specification requires the content that is between 192 | // the first { and the first } after the first {. If we found {} without 193 | // nothing in the middle, the whole key is hashed as usually. 194 | hashKey := key 195 | 196 | start := strings.Index(key, HASHTAG_START) 197 | if start >= 0 { 198 | end := strings.LastIndex(key, HASHTAG_END) 199 | if end >= 0 && start < end { 200 | hashKey = key[start:end] 201 | } 202 | } 203 | 204 | return crc16(hashKey) % DEFAULT_SLOT_NUM 205 | } 206 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/.gitignore: -------------------------------------------------------------------------------- 1 | logrus 2 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.2 4 | - 1.3 5 | - 1.4 6 | - tip 7 | install: 8 | - go get -t ./... 9 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.8.7 2 | 3 | * logrus/core: fix possible race (#216) 4 | * logrus/doc: small typo fixes and doc improvements 5 | 6 | 7 | # 0.8.6 8 | 9 | * hooks/raven: allow passing an initialized client 10 | 11 | # 0.8.5 12 | 13 | * logrus/core: revert #208 14 | 15 | # 0.8.4 16 | 17 | * formatter/text: fix data race (#218) 18 | 19 | # 0.8.3 20 | 21 | * logrus/core: fix entry log level (#208) 22 | * logrus/core: improve performance of text formatter by 40% 23 | * logrus/core: expose `LevelHooks` type 24 | * logrus/core: add support for DragonflyBSD and NetBSD 25 | * formatter/text: print structs more verbosely 26 | 27 | # 0.8.2 28 | 29 | * logrus: fix more Fatal family functions 30 | 31 | # 0.8.1 32 | 33 | * logrus: fix not exiting on `Fatalf` and `Fatalln` 34 | 35 | # 0.8.0 36 | 37 | * logrus: defaults to stderr instead of stdout 38 | * hooks/sentry: add special field for `*http.Request` 39 | * formatter/text: ignore Windows for colors 40 | 41 | # 0.7.3 42 | 43 | * formatter/\*: allow configuration of timestamp layout 44 | 45 | # 0.7.2 46 | 47 | * formatter/text: Add configuration option for time format (#158) 48 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Simon Eskildsen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package logrus is a structured logger for Go, completely API compatible with the standard library logger. 3 | 4 | 5 | The simplest way to use Logrus is simply the package-level exported logger: 6 | 7 | package main 8 | 9 | import ( 10 | log "github.com/Sirupsen/logrus" 11 | ) 12 | 13 | func main() { 14 | log.WithFields(log.Fields{ 15 | "animal": "walrus", 16 | "number": 1, 17 | "size": 10, 18 | }).Info("A walrus appears") 19 | } 20 | 21 | Output: 22 | time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 23 | 24 | For a full guide visit https://github.com/Sirupsen/logrus 25 | */ 26 | package logrus 27 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/entry.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "os" 8 | "time" 9 | ) 10 | 11 | // Defines the key when adding errors using WithError. 12 | var ErrorKey = "error" 13 | 14 | // An entry is the final or intermediate Logrus logging entry. It contains all 15 | // the fields passed with WithField{,s}. It's finally logged when Debug, Info, 16 | // Warn, Error, Fatal or Panic is called on it. These objects can be reused and 17 | // passed around as much as you wish to avoid field duplication. 18 | type Entry struct { 19 | Logger *Logger 20 | 21 | // Contains all the fields set by the user. 22 | Data Fields 23 | 24 | // Time at which the log entry was created 25 | Time time.Time 26 | 27 | // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic 28 | Level Level 29 | 30 | // Message passed to Debug, Info, Warn, Error, Fatal or Panic 31 | Message string 32 | } 33 | 34 | func NewEntry(logger *Logger) *Entry { 35 | return &Entry{ 36 | Logger: logger, 37 | // Default is three fields, give a little extra room 38 | Data: make(Fields, 5), 39 | } 40 | } 41 | 42 | // Returns a reader for the entry, which is a proxy to the formatter. 43 | func (entry *Entry) Reader() (*bytes.Buffer, error) { 44 | serialized, err := entry.Logger.Formatter.Format(entry) 45 | return bytes.NewBuffer(serialized), err 46 | } 47 | 48 | // Returns the string representation from the reader and ultimately the 49 | // formatter. 50 | func (entry *Entry) String() (string, error) { 51 | reader, err := entry.Reader() 52 | if err != nil { 53 | return "", err 54 | } 55 | 56 | return reader.String(), err 57 | } 58 | 59 | // Add an error as single field (using the key defined in ErrorKey) to the Entry. 60 | func (entry *Entry) WithError(err error) *Entry { 61 | return entry.WithField(ErrorKey, err) 62 | } 63 | 64 | // Add a single field to the Entry. 65 | func (entry *Entry) WithField(key string, value interface{}) *Entry { 66 | return entry.WithFields(Fields{key: value}) 67 | } 68 | 69 | // Add a map of fields to the Entry. 70 | func (entry *Entry) WithFields(fields Fields) *Entry { 71 | data := Fields{} 72 | for k, v := range entry.Data { 73 | data[k] = v 74 | } 75 | for k, v := range fields { 76 | data[k] = v 77 | } 78 | return &Entry{Logger: entry.Logger, Data: data} 79 | } 80 | 81 | // This function is not declared with a pointer value because otherwise 82 | // race conditions will occur when using multiple goroutines 83 | func (entry Entry) log(level Level, msg string) { 84 | entry.Time = time.Now() 85 | entry.Level = level 86 | entry.Message = msg 87 | 88 | if err := entry.Logger.Hooks.Fire(level, &entry); err != nil { 89 | entry.Logger.mu.Lock() 90 | fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) 91 | entry.Logger.mu.Unlock() 92 | } 93 | 94 | reader, err := entry.Reader() 95 | if err != nil { 96 | entry.Logger.mu.Lock() 97 | fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) 98 | entry.Logger.mu.Unlock() 99 | } 100 | 101 | entry.Logger.mu.Lock() 102 | defer entry.Logger.mu.Unlock() 103 | 104 | _, err = io.Copy(entry.Logger.Out, reader) 105 | if err != nil { 106 | fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) 107 | } 108 | 109 | // To avoid Entry#log() returning a value that only would make sense for 110 | // panic() to use in Entry#Panic(), we avoid the allocation by checking 111 | // directly here. 112 | if level <= PanicLevel { 113 | panic(&entry) 114 | } 115 | } 116 | 117 | func (entry *Entry) Debug(args ...interface{}) { 118 | if entry.Logger.Level >= DebugLevel { 119 | entry.log(DebugLevel, fmt.Sprint(args...)) 120 | } 121 | } 122 | 123 | func (entry *Entry) Print(args ...interface{}) { 124 | entry.Info(args...) 125 | } 126 | 127 | func (entry *Entry) Info(args ...interface{}) { 128 | if entry.Logger.Level >= InfoLevel { 129 | entry.log(InfoLevel, fmt.Sprint(args...)) 130 | } 131 | } 132 | 133 | func (entry *Entry) Warn(args ...interface{}) { 134 | if entry.Logger.Level >= WarnLevel { 135 | entry.log(WarnLevel, fmt.Sprint(args...)) 136 | } 137 | } 138 | 139 | func (entry *Entry) Warning(args ...interface{}) { 140 | entry.Warn(args...) 141 | } 142 | 143 | func (entry *Entry) Error(args ...interface{}) { 144 | if entry.Logger.Level >= ErrorLevel { 145 | entry.log(ErrorLevel, fmt.Sprint(args...)) 146 | } 147 | } 148 | 149 | func (entry *Entry) Fatal(args ...interface{}) { 150 | if entry.Logger.Level >= FatalLevel { 151 | entry.log(FatalLevel, fmt.Sprint(args...)) 152 | } 153 | os.Exit(1) 154 | } 155 | 156 | func (entry *Entry) Panic(args ...interface{}) { 157 | if entry.Logger.Level >= PanicLevel { 158 | entry.log(PanicLevel, fmt.Sprint(args...)) 159 | } 160 | panic(fmt.Sprint(args...)) 161 | } 162 | 163 | // Entry Printf family functions 164 | 165 | func (entry *Entry) Debugf(format string, args ...interface{}) { 166 | if entry.Logger.Level >= DebugLevel { 167 | entry.Debug(fmt.Sprintf(format, args...)) 168 | } 169 | } 170 | 171 | func (entry *Entry) Infof(format string, args ...interface{}) { 172 | if entry.Logger.Level >= InfoLevel { 173 | entry.Info(fmt.Sprintf(format, args...)) 174 | } 175 | } 176 | 177 | func (entry *Entry) Printf(format string, args ...interface{}) { 178 | entry.Infof(format, args...) 179 | } 180 | 181 | func (entry *Entry) Warnf(format string, args ...interface{}) { 182 | if entry.Logger.Level >= WarnLevel { 183 | entry.Warn(fmt.Sprintf(format, args...)) 184 | } 185 | } 186 | 187 | func (entry *Entry) Warningf(format string, args ...interface{}) { 188 | entry.Warnf(format, args...) 189 | } 190 | 191 | func (entry *Entry) Errorf(format string, args ...interface{}) { 192 | if entry.Logger.Level >= ErrorLevel { 193 | entry.Error(fmt.Sprintf(format, args...)) 194 | } 195 | } 196 | 197 | func (entry *Entry) Fatalf(format string, args ...interface{}) { 198 | if entry.Logger.Level >= FatalLevel { 199 | entry.Fatal(fmt.Sprintf(format, args...)) 200 | } 201 | os.Exit(1) 202 | } 203 | 204 | func (entry *Entry) Panicf(format string, args ...interface{}) { 205 | if entry.Logger.Level >= PanicLevel { 206 | entry.Panic(fmt.Sprintf(format, args...)) 207 | } 208 | } 209 | 210 | // Entry Println family functions 211 | 212 | func (entry *Entry) Debugln(args ...interface{}) { 213 | if entry.Logger.Level >= DebugLevel { 214 | entry.Debug(entry.sprintlnn(args...)) 215 | } 216 | } 217 | 218 | func (entry *Entry) Infoln(args ...interface{}) { 219 | if entry.Logger.Level >= InfoLevel { 220 | entry.Info(entry.sprintlnn(args...)) 221 | } 222 | } 223 | 224 | func (entry *Entry) Println(args ...interface{}) { 225 | entry.Infoln(args...) 226 | } 227 | 228 | func (entry *Entry) Warnln(args ...interface{}) { 229 | if entry.Logger.Level >= WarnLevel { 230 | entry.Warn(entry.sprintlnn(args...)) 231 | } 232 | } 233 | 234 | func (entry *Entry) Warningln(args ...interface{}) { 235 | entry.Warnln(args...) 236 | } 237 | 238 | func (entry *Entry) Errorln(args ...interface{}) { 239 | if entry.Logger.Level >= ErrorLevel { 240 | entry.Error(entry.sprintlnn(args...)) 241 | } 242 | } 243 | 244 | func (entry *Entry) Fatalln(args ...interface{}) { 245 | if entry.Logger.Level >= FatalLevel { 246 | entry.Fatal(entry.sprintlnn(args...)) 247 | } 248 | os.Exit(1) 249 | } 250 | 251 | func (entry *Entry) Panicln(args ...interface{}) { 252 | if entry.Logger.Level >= PanicLevel { 253 | entry.Panic(entry.sprintlnn(args...)) 254 | } 255 | } 256 | 257 | // Sprintlnn => Sprint no newline. This is to get the behavior of how 258 | // fmt.Sprintln where spaces are always added between operands, regardless of 259 | // their type. Instead of vendoring the Sprintln implementation to spare a 260 | // string allocation, we do the simplest thing. 261 | func (entry *Entry) sprintlnn(args ...interface{}) string { 262 | msg := fmt.Sprintln(args...) 263 | return msg[:len(msg)-1] 264 | } 265 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/entry_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestEntryWithError(t *testing.T) { 12 | 13 | assert := assert.New(t) 14 | 15 | defer func() { 16 | ErrorKey = "error" 17 | }() 18 | 19 | err := fmt.Errorf("kaboom at layer %d", 4711) 20 | 21 | assert.Equal(err, WithError(err).Data["error"]) 22 | 23 | logger := New() 24 | logger.Out = &bytes.Buffer{} 25 | entry := NewEntry(logger) 26 | 27 | assert.Equal(err, entry.WithError(err).Data["error"]) 28 | 29 | ErrorKey = "err" 30 | 31 | assert.Equal(err, entry.WithError(err).Data["err"]) 32 | 33 | } 34 | 35 | func TestEntryPanicln(t *testing.T) { 36 | errBoom := fmt.Errorf("boom time") 37 | 38 | defer func() { 39 | p := recover() 40 | assert.NotNil(t, p) 41 | 42 | switch pVal := p.(type) { 43 | case *Entry: 44 | assert.Equal(t, "kaboom", pVal.Message) 45 | assert.Equal(t, errBoom, pVal.Data["err"]) 46 | default: 47 | t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) 48 | } 49 | }() 50 | 51 | logger := New() 52 | logger.Out = &bytes.Buffer{} 53 | entry := NewEntry(logger) 54 | entry.WithField("err", errBoom).Panicln("kaboom") 55 | } 56 | 57 | func TestEntryPanicf(t *testing.T) { 58 | errBoom := fmt.Errorf("boom again") 59 | 60 | defer func() { 61 | p := recover() 62 | assert.NotNil(t, p) 63 | 64 | switch pVal := p.(type) { 65 | case *Entry: 66 | assert.Equal(t, "kaboom true", pVal.Message) 67 | assert.Equal(t, errBoom, pVal.Data["err"]) 68 | default: 69 | t.Fatalf("want type *Entry, got %T: %#v", pVal, pVal) 70 | } 71 | }() 72 | 73 | logger := New() 74 | logger.Out = &bytes.Buffer{} 75 | entry := NewEntry(logger) 76 | entry.WithField("err", errBoom).Panicf("kaboom %v", true) 77 | } 78 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/exported.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | var ( 8 | // std is the name of the standard logger in stdlib `log` 9 | std = New() 10 | ) 11 | 12 | func StandardLogger() *Logger { 13 | return std 14 | } 15 | 16 | // SetOutput sets the standard logger output. 17 | func SetOutput(out io.Writer) { 18 | std.mu.Lock() 19 | defer std.mu.Unlock() 20 | std.Out = out 21 | } 22 | 23 | // SetFormatter sets the standard logger formatter. 24 | func SetFormatter(formatter Formatter) { 25 | std.mu.Lock() 26 | defer std.mu.Unlock() 27 | std.Formatter = formatter 28 | } 29 | 30 | // SetLevel sets the standard logger level. 31 | func SetLevel(level Level) { 32 | std.mu.Lock() 33 | defer std.mu.Unlock() 34 | std.Level = level 35 | } 36 | 37 | // GetLevel returns the standard logger level. 38 | func GetLevel() Level { 39 | std.mu.Lock() 40 | defer std.mu.Unlock() 41 | return std.Level 42 | } 43 | 44 | // AddHook adds a hook to the standard logger hooks. 45 | func AddHook(hook Hook) { 46 | std.mu.Lock() 47 | defer std.mu.Unlock() 48 | std.Hooks.Add(hook) 49 | } 50 | 51 | // WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key. 52 | func WithError(err error) *Entry { 53 | return std.WithField(ErrorKey, err) 54 | } 55 | 56 | // WithField creates an entry from the standard logger and adds a field to 57 | // it. If you want multiple fields, use `WithFields`. 58 | // 59 | // Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal 60 | // or Panic on the Entry it returns. 61 | func WithField(key string, value interface{}) *Entry { 62 | return std.WithField(key, value) 63 | } 64 | 65 | // WithFields creates an entry from the standard logger and adds multiple 66 | // fields to it. This is simply a helper for `WithField`, invoking it 67 | // once for each field. 68 | // 69 | // Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal 70 | // or Panic on the Entry it returns. 71 | func WithFields(fields Fields) *Entry { 72 | return std.WithFields(fields) 73 | } 74 | 75 | // Debug logs a message at level Debug on the standard logger. 76 | func Debug(args ...interface{}) { 77 | std.Debug(args...) 78 | } 79 | 80 | // Print logs a message at level Info on the standard logger. 81 | func Print(args ...interface{}) { 82 | std.Print(args...) 83 | } 84 | 85 | // Info logs a message at level Info on the standard logger. 86 | func Info(args ...interface{}) { 87 | std.Info(args...) 88 | } 89 | 90 | // Warn logs a message at level Warn on the standard logger. 91 | func Warn(args ...interface{}) { 92 | std.Warn(args...) 93 | } 94 | 95 | // Warning logs a message at level Warn on the standard logger. 96 | func Warning(args ...interface{}) { 97 | std.Warning(args...) 98 | } 99 | 100 | // Error logs a message at level Error on the standard logger. 101 | func Error(args ...interface{}) { 102 | std.Error(args...) 103 | } 104 | 105 | // Panic logs a message at level Panic on the standard logger. 106 | func Panic(args ...interface{}) { 107 | std.Panic(args...) 108 | } 109 | 110 | // Fatal logs a message at level Fatal on the standard logger. 111 | func Fatal(args ...interface{}) { 112 | std.Fatal(args...) 113 | } 114 | 115 | // Debugf logs a message at level Debug on the standard logger. 116 | func Debugf(format string, args ...interface{}) { 117 | std.Debugf(format, args...) 118 | } 119 | 120 | // Printf logs a message at level Info on the standard logger. 121 | func Printf(format string, args ...interface{}) { 122 | std.Printf(format, args...) 123 | } 124 | 125 | // Infof logs a message at level Info on the standard logger. 126 | func Infof(format string, args ...interface{}) { 127 | std.Infof(format, args...) 128 | } 129 | 130 | // Warnf logs a message at level Warn on the standard logger. 131 | func Warnf(format string, args ...interface{}) { 132 | std.Warnf(format, args...) 133 | } 134 | 135 | // Warningf logs a message at level Warn on the standard logger. 136 | func Warningf(format string, args ...interface{}) { 137 | std.Warningf(format, args...) 138 | } 139 | 140 | // Errorf logs a message at level Error on the standard logger. 141 | func Errorf(format string, args ...interface{}) { 142 | std.Errorf(format, args...) 143 | } 144 | 145 | // Panicf logs a message at level Panic on the standard logger. 146 | func Panicf(format string, args ...interface{}) { 147 | std.Panicf(format, args...) 148 | } 149 | 150 | // Fatalf logs a message at level Fatal on the standard logger. 151 | func Fatalf(format string, args ...interface{}) { 152 | std.Fatalf(format, args...) 153 | } 154 | 155 | // Debugln logs a message at level Debug on the standard logger. 156 | func Debugln(args ...interface{}) { 157 | std.Debugln(args...) 158 | } 159 | 160 | // Println logs a message at level Info on the standard logger. 161 | func Println(args ...interface{}) { 162 | std.Println(args...) 163 | } 164 | 165 | // Infoln logs a message at level Info on the standard logger. 166 | func Infoln(args ...interface{}) { 167 | std.Infoln(args...) 168 | } 169 | 170 | // Warnln logs a message at level Warn on the standard logger. 171 | func Warnln(args ...interface{}) { 172 | std.Warnln(args...) 173 | } 174 | 175 | // Warningln logs a message at level Warn on the standard logger. 176 | func Warningln(args ...interface{}) { 177 | std.Warningln(args...) 178 | } 179 | 180 | // Errorln logs a message at level Error on the standard logger. 181 | func Errorln(args ...interface{}) { 182 | std.Errorln(args...) 183 | } 184 | 185 | // Panicln logs a message at level Panic on the standard logger. 186 | func Panicln(args ...interface{}) { 187 | std.Panicln(args...) 188 | } 189 | 190 | // Fatalln logs a message at level Fatal on the standard logger. 191 | func Fatalln(args ...interface{}) { 192 | std.Fatalln(args...) 193 | } 194 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/formatter.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import "time" 4 | 5 | const DefaultTimestampFormat = time.RFC3339 6 | 7 | // The Formatter interface is used to implement a custom Formatter. It takes an 8 | // `Entry`. It exposes all the fields, including the default ones: 9 | // 10 | // * `entry.Data["msg"]`. The message passed from Info, Warn, Error .. 11 | // * `entry.Data["time"]`. The timestamp. 12 | // * `entry.Data["level"]. The level the entry was logged at. 13 | // 14 | // Any additional fields added with `WithField` or `WithFields` are also in 15 | // `entry.Data`. Format is expected to return an array of bytes which are then 16 | // logged to `logger.Out`. 17 | type Formatter interface { 18 | Format(*Entry) ([]byte, error) 19 | } 20 | 21 | // This is to not silently overwrite `time`, `msg` and `level` fields when 22 | // dumping it. If this code wasn't there doing: 23 | // 24 | // logrus.WithField("level", 1).Info("hello") 25 | // 26 | // Would just silently drop the user provided level. Instead with this code 27 | // it'll logged as: 28 | // 29 | // {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} 30 | // 31 | // It's not exported because it's still using Data in an opinionated way. It's to 32 | // avoid code duplication between the two default formatters. 33 | func prefixFieldClashes(data Fields) { 34 | _, ok := data["time"] 35 | if ok { 36 | data["fields.time"] = data["time"] 37 | } 38 | 39 | _, ok = data["msg"] 40 | if ok { 41 | data["fields.msg"] = data["msg"] 42 | } 43 | 44 | _, ok = data["level"] 45 | if ok { 46 | data["fields.level"] = data["level"] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/formatter_bench_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | // smallFields is a small size data set for benchmarking 10 | var smallFields = Fields{ 11 | "foo": "bar", 12 | "baz": "qux", 13 | "one": "two", 14 | "three": "four", 15 | } 16 | 17 | // largeFields is a large size data set for benchmarking 18 | var largeFields = Fields{ 19 | "foo": "bar", 20 | "baz": "qux", 21 | "one": "two", 22 | "three": "four", 23 | "five": "six", 24 | "seven": "eight", 25 | "nine": "ten", 26 | "eleven": "twelve", 27 | "thirteen": "fourteen", 28 | "fifteen": "sixteen", 29 | "seventeen": "eighteen", 30 | "nineteen": "twenty", 31 | "a": "b", 32 | "c": "d", 33 | "e": "f", 34 | "g": "h", 35 | "i": "j", 36 | "k": "l", 37 | "m": "n", 38 | "o": "p", 39 | "q": "r", 40 | "s": "t", 41 | "u": "v", 42 | "w": "x", 43 | "y": "z", 44 | "this": "will", 45 | "make": "thirty", 46 | "entries": "yeah", 47 | } 48 | 49 | var errorFields = Fields{ 50 | "foo": fmt.Errorf("bar"), 51 | "baz": fmt.Errorf("qux"), 52 | } 53 | 54 | func BenchmarkErrorTextFormatter(b *testing.B) { 55 | doBenchmark(b, &TextFormatter{DisableColors: true}, errorFields) 56 | } 57 | 58 | func BenchmarkSmallTextFormatter(b *testing.B) { 59 | doBenchmark(b, &TextFormatter{DisableColors: true}, smallFields) 60 | } 61 | 62 | func BenchmarkLargeTextFormatter(b *testing.B) { 63 | doBenchmark(b, &TextFormatter{DisableColors: true}, largeFields) 64 | } 65 | 66 | func BenchmarkSmallColoredTextFormatter(b *testing.B) { 67 | doBenchmark(b, &TextFormatter{ForceColors: true}, smallFields) 68 | } 69 | 70 | func BenchmarkLargeColoredTextFormatter(b *testing.B) { 71 | doBenchmark(b, &TextFormatter{ForceColors: true}, largeFields) 72 | } 73 | 74 | func BenchmarkSmallJSONFormatter(b *testing.B) { 75 | doBenchmark(b, &JSONFormatter{}, smallFields) 76 | } 77 | 78 | func BenchmarkLargeJSONFormatter(b *testing.B) { 79 | doBenchmark(b, &JSONFormatter{}, largeFields) 80 | } 81 | 82 | func doBenchmark(b *testing.B, formatter Formatter, fields Fields) { 83 | entry := &Entry{ 84 | Time: time.Time{}, 85 | Level: InfoLevel, 86 | Message: "message", 87 | Data: fields, 88 | } 89 | var d []byte 90 | var err error 91 | for i := 0; i < b.N; i++ { 92 | d, err = formatter.Format(entry) 93 | if err != nil { 94 | b.Fatal(err) 95 | } 96 | b.SetBytes(int64(len(d))) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/hook_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | type TestHook struct { 10 | Fired bool 11 | } 12 | 13 | func (hook *TestHook) Fire(entry *Entry) error { 14 | hook.Fired = true 15 | return nil 16 | } 17 | 18 | func (hook *TestHook) Levels() []Level { 19 | return []Level{ 20 | DebugLevel, 21 | InfoLevel, 22 | WarnLevel, 23 | ErrorLevel, 24 | FatalLevel, 25 | PanicLevel, 26 | } 27 | } 28 | 29 | func TestHookFires(t *testing.T) { 30 | hook := new(TestHook) 31 | 32 | LogAndAssertJSON(t, func(log *Logger) { 33 | log.Hooks.Add(hook) 34 | assert.Equal(t, hook.Fired, false) 35 | 36 | log.Print("test") 37 | }, func(fields Fields) { 38 | assert.Equal(t, hook.Fired, true) 39 | }) 40 | } 41 | 42 | type ModifyHook struct { 43 | } 44 | 45 | func (hook *ModifyHook) Fire(entry *Entry) error { 46 | entry.Data["wow"] = "whale" 47 | return nil 48 | } 49 | 50 | func (hook *ModifyHook) Levels() []Level { 51 | return []Level{ 52 | DebugLevel, 53 | InfoLevel, 54 | WarnLevel, 55 | ErrorLevel, 56 | FatalLevel, 57 | PanicLevel, 58 | } 59 | } 60 | 61 | func TestHookCanModifyEntry(t *testing.T) { 62 | hook := new(ModifyHook) 63 | 64 | LogAndAssertJSON(t, func(log *Logger) { 65 | log.Hooks.Add(hook) 66 | log.WithField("wow", "elephant").Print("test") 67 | }, func(fields Fields) { 68 | assert.Equal(t, fields["wow"], "whale") 69 | }) 70 | } 71 | 72 | func TestCanFireMultipleHooks(t *testing.T) { 73 | hook1 := new(ModifyHook) 74 | hook2 := new(TestHook) 75 | 76 | LogAndAssertJSON(t, func(log *Logger) { 77 | log.Hooks.Add(hook1) 78 | log.Hooks.Add(hook2) 79 | 80 | log.WithField("wow", "elephant").Print("test") 81 | }, func(fields Fields) { 82 | assert.Equal(t, fields["wow"], "whale") 83 | assert.Equal(t, hook2.Fired, true) 84 | }) 85 | } 86 | 87 | type ErrorHook struct { 88 | Fired bool 89 | } 90 | 91 | func (hook *ErrorHook) Fire(entry *Entry) error { 92 | hook.Fired = true 93 | return nil 94 | } 95 | 96 | func (hook *ErrorHook) Levels() []Level { 97 | return []Level{ 98 | ErrorLevel, 99 | } 100 | } 101 | 102 | func TestErrorHookShouldntFireOnInfo(t *testing.T) { 103 | hook := new(ErrorHook) 104 | 105 | LogAndAssertJSON(t, func(log *Logger) { 106 | log.Hooks.Add(hook) 107 | log.Info("test") 108 | }, func(fields Fields) { 109 | assert.Equal(t, hook.Fired, false) 110 | }) 111 | } 112 | 113 | func TestErrorHookShouldFireOnError(t *testing.T) { 114 | hook := new(ErrorHook) 115 | 116 | LogAndAssertJSON(t, func(log *Logger) { 117 | log.Hooks.Add(hook) 118 | log.Error("test") 119 | }, func(fields Fields) { 120 | assert.Equal(t, hook.Fired, true) 121 | }) 122 | } 123 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/hooks.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | // A hook to be fired when logging on the logging levels returned from 4 | // `Levels()` on your implementation of the interface. Note that this is not 5 | // fired in a goroutine or a channel with workers, you should handle such 6 | // functionality yourself if your call is non-blocking and you don't wish for 7 | // the logging calls for levels returned from `Levels()` to block. 8 | type Hook interface { 9 | Levels() []Level 10 | Fire(*Entry) error 11 | } 12 | 13 | // Internal type for storing the hooks on a logger instance. 14 | type LevelHooks map[Level][]Hook 15 | 16 | // Add a hook to an instance of logger. This is called with 17 | // `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. 18 | func (hooks LevelHooks) Add(hook Hook) { 19 | for _, level := range hook.Levels() { 20 | hooks[level] = append(hooks[level], hook) 21 | } 22 | } 23 | 24 | // Fire all the hooks for the passed level. Used by `entry.log` to fire 25 | // appropriate hooks for a log entry. 26 | func (hooks LevelHooks) Fire(level Level, entry *Entry) error { 27 | for _, hook := range hooks[level] { 28 | if err := hook.Fire(entry); err != nil { 29 | return err 30 | } 31 | } 32 | 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/json_formatter.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | type JSONFormatter struct { 9 | // TimestampFormat sets the format used for marshaling timestamps. 10 | TimestampFormat string 11 | } 12 | 13 | func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { 14 | data := make(Fields, len(entry.Data)+3) 15 | for k, v := range entry.Data { 16 | switch v := v.(type) { 17 | case error: 18 | // Otherwise errors are ignored by `encoding/json` 19 | // https://github.com/Sirupsen/logrus/issues/137 20 | data[k] = v.Error() 21 | default: 22 | data[k] = v 23 | } 24 | } 25 | prefixFieldClashes(data) 26 | 27 | timestampFormat := f.TimestampFormat 28 | if timestampFormat == "" { 29 | timestampFormat = DefaultTimestampFormat 30 | } 31 | 32 | data["time"] = entry.Time.Format(timestampFormat) 33 | data["msg"] = entry.Message 34 | data["level"] = entry.Level.String() 35 | 36 | serialized, err := json.Marshal(data) 37 | if err != nil { 38 | return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err) 39 | } 40 | return append(serialized, '\n'), nil 41 | } 42 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/json_formatter_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | 7 | "testing" 8 | ) 9 | 10 | func TestErrorNotLost(t *testing.T) { 11 | formatter := &JSONFormatter{} 12 | 13 | b, err := formatter.Format(WithField("error", errors.New("wild walrus"))) 14 | if err != nil { 15 | t.Fatal("Unable to format entry: ", err) 16 | } 17 | 18 | entry := make(map[string]interface{}) 19 | err = json.Unmarshal(b, &entry) 20 | if err != nil { 21 | t.Fatal("Unable to unmarshal formatted entry: ", err) 22 | } 23 | 24 | if entry["error"] != "wild walrus" { 25 | t.Fatal("Error field not set") 26 | } 27 | } 28 | 29 | func TestErrorNotLostOnFieldNotNamedError(t *testing.T) { 30 | formatter := &JSONFormatter{} 31 | 32 | b, err := formatter.Format(WithField("omg", errors.New("wild walrus"))) 33 | if err != nil { 34 | t.Fatal("Unable to format entry: ", err) 35 | } 36 | 37 | entry := make(map[string]interface{}) 38 | err = json.Unmarshal(b, &entry) 39 | if err != nil { 40 | t.Fatal("Unable to unmarshal formatted entry: ", err) 41 | } 42 | 43 | if entry["omg"] != "wild walrus" { 44 | t.Fatal("Error field not set") 45 | } 46 | } 47 | 48 | func TestFieldClashWithTime(t *testing.T) { 49 | formatter := &JSONFormatter{} 50 | 51 | b, err := formatter.Format(WithField("time", "right now!")) 52 | if err != nil { 53 | t.Fatal("Unable to format entry: ", err) 54 | } 55 | 56 | entry := make(map[string]interface{}) 57 | err = json.Unmarshal(b, &entry) 58 | if err != nil { 59 | t.Fatal("Unable to unmarshal formatted entry: ", err) 60 | } 61 | 62 | if entry["fields.time"] != "right now!" { 63 | t.Fatal("fields.time not set to original time field") 64 | } 65 | 66 | if entry["time"] != "0001-01-01T00:00:00Z" { 67 | t.Fatal("time field not set to current time, was: ", entry["time"]) 68 | } 69 | } 70 | 71 | func TestFieldClashWithMsg(t *testing.T) { 72 | formatter := &JSONFormatter{} 73 | 74 | b, err := formatter.Format(WithField("msg", "something")) 75 | if err != nil { 76 | t.Fatal("Unable to format entry: ", err) 77 | } 78 | 79 | entry := make(map[string]interface{}) 80 | err = json.Unmarshal(b, &entry) 81 | if err != nil { 82 | t.Fatal("Unable to unmarshal formatted entry: ", err) 83 | } 84 | 85 | if entry["fields.msg"] != "something" { 86 | t.Fatal("fields.msg not set to original msg field") 87 | } 88 | } 89 | 90 | func TestFieldClashWithLevel(t *testing.T) { 91 | formatter := &JSONFormatter{} 92 | 93 | b, err := formatter.Format(WithField("level", "something")) 94 | if err != nil { 95 | t.Fatal("Unable to format entry: ", err) 96 | } 97 | 98 | entry := make(map[string]interface{}) 99 | err = json.Unmarshal(b, &entry) 100 | if err != nil { 101 | t.Fatal("Unable to unmarshal formatted entry: ", err) 102 | } 103 | 104 | if entry["fields.level"] != "something" { 105 | t.Fatal("fields.level not set to original level field") 106 | } 107 | } 108 | 109 | func TestJSONEntryEndsWithNewline(t *testing.T) { 110 | formatter := &JSONFormatter{} 111 | 112 | b, err := formatter.Format(WithField("level", "something")) 113 | if err != nil { 114 | t.Fatal("Unable to format entry: ", err) 115 | } 116 | 117 | if b[len(b)-1] != '\n' { 118 | t.Fatal("Expected JSON log entry to end with a newline") 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/logger.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "io" 5 | "os" 6 | "sync" 7 | ) 8 | 9 | type Logger struct { 10 | // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a 11 | // file, or leave it default which is `os.Stderr`. You can also set this to 12 | // something more adventorous, such as logging to Kafka. 13 | Out io.Writer 14 | // Hooks for the logger instance. These allow firing events based on logging 15 | // levels and log entries. For example, to send errors to an error tracking 16 | // service, log to StatsD or dump the core on fatal errors. 17 | Hooks LevelHooks 18 | // All log entries pass through the formatter before logged to Out. The 19 | // included formatters are `TextFormatter` and `JSONFormatter` for which 20 | // TextFormatter is the default. In development (when a TTY is attached) it 21 | // logs with colors, but to a file it wouldn't. You can easily implement your 22 | // own that implements the `Formatter` interface, see the `README` or included 23 | // formatters for examples. 24 | Formatter Formatter 25 | // The logging level the logger should log at. This is typically (and defaults 26 | // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be 27 | // logged. `logrus.Debug` is useful in 28 | Level Level 29 | // Used to sync writing to the log. 30 | mu sync.Mutex 31 | } 32 | 33 | // Creates a new logger. Configuration should be set by changing `Formatter`, 34 | // `Out` and `Hooks` directly on the default logger instance. You can also just 35 | // instantiate your own: 36 | // 37 | // var log = &Logger{ 38 | // Out: os.Stderr, 39 | // Formatter: new(JSONFormatter), 40 | // Hooks: make(LevelHooks), 41 | // Level: logrus.DebugLevel, 42 | // } 43 | // 44 | // It's recommended to make this a global instance called `log`. 45 | func New() *Logger { 46 | return &Logger{ 47 | Out: os.Stderr, 48 | Formatter: new(TextFormatter), 49 | Hooks: make(LevelHooks), 50 | Level: InfoLevel, 51 | } 52 | } 53 | 54 | // Adds a field to the log entry, note that you it doesn't log until you call 55 | // Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry. 56 | // If you want multiple fields, use `WithFields`. 57 | func (logger *Logger) WithField(key string, value interface{}) *Entry { 58 | return NewEntry(logger).WithField(key, value) 59 | } 60 | 61 | // Adds a struct of fields to the log entry. All it does is call `WithField` for 62 | // each `Field`. 63 | func (logger *Logger) WithFields(fields Fields) *Entry { 64 | return NewEntry(logger).WithFields(fields) 65 | } 66 | 67 | func (logger *Logger) Debugf(format string, args ...interface{}) { 68 | if logger.Level >= DebugLevel { 69 | NewEntry(logger).Debugf(format, args...) 70 | } 71 | } 72 | 73 | func (logger *Logger) Infof(format string, args ...interface{}) { 74 | if logger.Level >= InfoLevel { 75 | NewEntry(logger).Infof(format, args...) 76 | } 77 | } 78 | 79 | func (logger *Logger) Printf(format string, args ...interface{}) { 80 | NewEntry(logger).Printf(format, args...) 81 | } 82 | 83 | func (logger *Logger) Warnf(format string, args ...interface{}) { 84 | if logger.Level >= WarnLevel { 85 | NewEntry(logger).Warnf(format, args...) 86 | } 87 | } 88 | 89 | func (logger *Logger) Warningf(format string, args ...interface{}) { 90 | if logger.Level >= WarnLevel { 91 | NewEntry(logger).Warnf(format, args...) 92 | } 93 | } 94 | 95 | func (logger *Logger) Errorf(format string, args ...interface{}) { 96 | if logger.Level >= ErrorLevel { 97 | NewEntry(logger).Errorf(format, args...) 98 | } 99 | } 100 | 101 | func (logger *Logger) Fatalf(format string, args ...interface{}) { 102 | if logger.Level >= FatalLevel { 103 | NewEntry(logger).Fatalf(format, args...) 104 | } 105 | os.Exit(1) 106 | } 107 | 108 | func (logger *Logger) Panicf(format string, args ...interface{}) { 109 | if logger.Level >= PanicLevel { 110 | NewEntry(logger).Panicf(format, args...) 111 | } 112 | } 113 | 114 | func (logger *Logger) Debug(args ...interface{}) { 115 | if logger.Level >= DebugLevel { 116 | NewEntry(logger).Debug(args...) 117 | } 118 | } 119 | 120 | func (logger *Logger) Info(args ...interface{}) { 121 | if logger.Level >= InfoLevel { 122 | NewEntry(logger).Info(args...) 123 | } 124 | } 125 | 126 | func (logger *Logger) Print(args ...interface{}) { 127 | NewEntry(logger).Info(args...) 128 | } 129 | 130 | func (logger *Logger) Warn(args ...interface{}) { 131 | if logger.Level >= WarnLevel { 132 | NewEntry(logger).Warn(args...) 133 | } 134 | } 135 | 136 | func (logger *Logger) Warning(args ...interface{}) { 137 | if logger.Level >= WarnLevel { 138 | NewEntry(logger).Warn(args...) 139 | } 140 | } 141 | 142 | func (logger *Logger) Error(args ...interface{}) { 143 | if logger.Level >= ErrorLevel { 144 | NewEntry(logger).Error(args...) 145 | } 146 | } 147 | 148 | func (logger *Logger) Fatal(args ...interface{}) { 149 | if logger.Level >= FatalLevel { 150 | NewEntry(logger).Fatal(args...) 151 | } 152 | os.Exit(1) 153 | } 154 | 155 | func (logger *Logger) Panic(args ...interface{}) { 156 | if logger.Level >= PanicLevel { 157 | NewEntry(logger).Panic(args...) 158 | } 159 | } 160 | 161 | func (logger *Logger) Debugln(args ...interface{}) { 162 | if logger.Level >= DebugLevel { 163 | NewEntry(logger).Debugln(args...) 164 | } 165 | } 166 | 167 | func (logger *Logger) Infoln(args ...interface{}) { 168 | if logger.Level >= InfoLevel { 169 | NewEntry(logger).Infoln(args...) 170 | } 171 | } 172 | 173 | func (logger *Logger) Println(args ...interface{}) { 174 | NewEntry(logger).Println(args...) 175 | } 176 | 177 | func (logger *Logger) Warnln(args ...interface{}) { 178 | if logger.Level >= WarnLevel { 179 | NewEntry(logger).Warnln(args...) 180 | } 181 | } 182 | 183 | func (logger *Logger) Warningln(args ...interface{}) { 184 | if logger.Level >= WarnLevel { 185 | NewEntry(logger).Warnln(args...) 186 | } 187 | } 188 | 189 | func (logger *Logger) Errorln(args ...interface{}) { 190 | if logger.Level >= ErrorLevel { 191 | NewEntry(logger).Errorln(args...) 192 | } 193 | } 194 | 195 | func (logger *Logger) Fatalln(args ...interface{}) { 196 | if logger.Level >= FatalLevel { 197 | NewEntry(logger).Fatalln(args...) 198 | } 199 | os.Exit(1) 200 | } 201 | 202 | func (logger *Logger) Panicln(args ...interface{}) { 203 | if logger.Level >= PanicLevel { 204 | NewEntry(logger).Panicln(args...) 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/logrus.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | ) 7 | 8 | // Fields type, used to pass to `WithFields`. 9 | type Fields map[string]interface{} 10 | 11 | // Level type 12 | type Level uint8 13 | 14 | // Convert the Level to a string. E.g. PanicLevel becomes "panic". 15 | func (level Level) String() string { 16 | switch level { 17 | case DebugLevel: 18 | return "debug" 19 | case InfoLevel: 20 | return "info" 21 | case WarnLevel: 22 | return "warning" 23 | case ErrorLevel: 24 | return "error" 25 | case FatalLevel: 26 | return "fatal" 27 | case PanicLevel: 28 | return "panic" 29 | } 30 | 31 | return "unknown" 32 | } 33 | 34 | // ParseLevel takes a string level and returns the Logrus log level constant. 35 | func ParseLevel(lvl string) (Level, error) { 36 | switch lvl { 37 | case "panic": 38 | return PanicLevel, nil 39 | case "fatal": 40 | return FatalLevel, nil 41 | case "error": 42 | return ErrorLevel, nil 43 | case "warn", "warning": 44 | return WarnLevel, nil 45 | case "info": 46 | return InfoLevel, nil 47 | case "debug": 48 | return DebugLevel, nil 49 | } 50 | 51 | var l Level 52 | return l, fmt.Errorf("not a valid logrus Level: %q", lvl) 53 | } 54 | 55 | // These are the different logging levels. You can set the logging level to log 56 | // on your instance of logger, obtained with `logrus.New()`. 57 | const ( 58 | // PanicLevel level, highest level of severity. Logs and then calls panic with the 59 | // message passed to Debug, Info, ... 60 | PanicLevel Level = iota 61 | // FatalLevel level. Logs and then calls `os.Exit(1)`. It will exit even if the 62 | // logging level is set to Panic. 63 | FatalLevel 64 | // ErrorLevel level. Logs. Used for errors that should definitely be noted. 65 | // Commonly used for hooks to send errors to an error tracking service. 66 | ErrorLevel 67 | // WarnLevel level. Non-critical entries that deserve eyes. 68 | WarnLevel 69 | // InfoLevel level. General operational entries about what's going on inside the 70 | // application. 71 | InfoLevel 72 | // DebugLevel level. Usually only enabled when debugging. Very verbose logging. 73 | DebugLevel 74 | ) 75 | 76 | // Won't compile if StdLogger can't be realized by a log.Logger 77 | var ( 78 | _ StdLogger = &log.Logger{} 79 | _ StdLogger = &Entry{} 80 | _ StdLogger = &Logger{} 81 | ) 82 | 83 | // StdLogger is what your logrus-enabled library should take, that way 84 | // it'll accept a stdlib logger and a logrus logger. There's no standard 85 | // interface, this is the closest we get, unfortunately. 86 | type StdLogger interface { 87 | Print(...interface{}) 88 | Printf(string, ...interface{}) 89 | Println(...interface{}) 90 | 91 | Fatal(...interface{}) 92 | Fatalf(string, ...interface{}) 93 | Fatalln(...interface{}) 94 | 95 | Panic(...interface{}) 96 | Panicf(string, ...interface{}) 97 | Panicln(...interface{}) 98 | } 99 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/logrus_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "strconv" 7 | "strings" 8 | "sync" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func LogAndAssertJSON(t *testing.T, log func(*Logger), assertions func(fields Fields)) { 15 | var buffer bytes.Buffer 16 | var fields Fields 17 | 18 | logger := New() 19 | logger.Out = &buffer 20 | logger.Formatter = new(JSONFormatter) 21 | 22 | log(logger) 23 | 24 | err := json.Unmarshal(buffer.Bytes(), &fields) 25 | assert.Nil(t, err) 26 | 27 | assertions(fields) 28 | } 29 | 30 | func LogAndAssertText(t *testing.T, log func(*Logger), assertions func(fields map[string]string)) { 31 | var buffer bytes.Buffer 32 | 33 | logger := New() 34 | logger.Out = &buffer 35 | logger.Formatter = &TextFormatter{ 36 | DisableColors: true, 37 | } 38 | 39 | log(logger) 40 | 41 | fields := make(map[string]string) 42 | for _, kv := range strings.Split(buffer.String(), " ") { 43 | if !strings.Contains(kv, "=") { 44 | continue 45 | } 46 | kvArr := strings.Split(kv, "=") 47 | key := strings.TrimSpace(kvArr[0]) 48 | val := kvArr[1] 49 | if kvArr[1][0] == '"' { 50 | var err error 51 | val, err = strconv.Unquote(val) 52 | assert.NoError(t, err) 53 | } 54 | fields[key] = val 55 | } 56 | assertions(fields) 57 | } 58 | 59 | func TestPrint(t *testing.T) { 60 | LogAndAssertJSON(t, func(log *Logger) { 61 | log.Print("test") 62 | }, func(fields Fields) { 63 | assert.Equal(t, fields["msg"], "test") 64 | assert.Equal(t, fields["level"], "info") 65 | }) 66 | } 67 | 68 | func TestInfo(t *testing.T) { 69 | LogAndAssertJSON(t, func(log *Logger) { 70 | log.Info("test") 71 | }, func(fields Fields) { 72 | assert.Equal(t, fields["msg"], "test") 73 | assert.Equal(t, fields["level"], "info") 74 | }) 75 | } 76 | 77 | func TestWarn(t *testing.T) { 78 | LogAndAssertJSON(t, func(log *Logger) { 79 | log.Warn("test") 80 | }, func(fields Fields) { 81 | assert.Equal(t, fields["msg"], "test") 82 | assert.Equal(t, fields["level"], "warning") 83 | }) 84 | } 85 | 86 | func TestInfolnShouldAddSpacesBetweenStrings(t *testing.T) { 87 | LogAndAssertJSON(t, func(log *Logger) { 88 | log.Infoln("test", "test") 89 | }, func(fields Fields) { 90 | assert.Equal(t, fields["msg"], "test test") 91 | }) 92 | } 93 | 94 | func TestInfolnShouldAddSpacesBetweenStringAndNonstring(t *testing.T) { 95 | LogAndAssertJSON(t, func(log *Logger) { 96 | log.Infoln("test", 10) 97 | }, func(fields Fields) { 98 | assert.Equal(t, fields["msg"], "test 10") 99 | }) 100 | } 101 | 102 | func TestInfolnShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { 103 | LogAndAssertJSON(t, func(log *Logger) { 104 | log.Infoln(10, 10) 105 | }, func(fields Fields) { 106 | assert.Equal(t, fields["msg"], "10 10") 107 | }) 108 | } 109 | 110 | func TestInfoShouldAddSpacesBetweenTwoNonStrings(t *testing.T) { 111 | LogAndAssertJSON(t, func(log *Logger) { 112 | log.Infoln(10, 10) 113 | }, func(fields Fields) { 114 | assert.Equal(t, fields["msg"], "10 10") 115 | }) 116 | } 117 | 118 | func TestInfoShouldNotAddSpacesBetweenStringAndNonstring(t *testing.T) { 119 | LogAndAssertJSON(t, func(log *Logger) { 120 | log.Info("test", 10) 121 | }, func(fields Fields) { 122 | assert.Equal(t, fields["msg"], "test10") 123 | }) 124 | } 125 | 126 | func TestInfoShouldNotAddSpacesBetweenStrings(t *testing.T) { 127 | LogAndAssertJSON(t, func(log *Logger) { 128 | log.Info("test", "test") 129 | }, func(fields Fields) { 130 | assert.Equal(t, fields["msg"], "testtest") 131 | }) 132 | } 133 | 134 | func TestWithFieldsShouldAllowAssignments(t *testing.T) { 135 | var buffer bytes.Buffer 136 | var fields Fields 137 | 138 | logger := New() 139 | logger.Out = &buffer 140 | logger.Formatter = new(JSONFormatter) 141 | 142 | localLog := logger.WithFields(Fields{ 143 | "key1": "value1", 144 | }) 145 | 146 | localLog.WithField("key2", "value2").Info("test") 147 | err := json.Unmarshal(buffer.Bytes(), &fields) 148 | assert.Nil(t, err) 149 | 150 | assert.Equal(t, "value2", fields["key2"]) 151 | assert.Equal(t, "value1", fields["key1"]) 152 | 153 | buffer = bytes.Buffer{} 154 | fields = Fields{} 155 | localLog.Info("test") 156 | err = json.Unmarshal(buffer.Bytes(), &fields) 157 | assert.Nil(t, err) 158 | 159 | _, ok := fields["key2"] 160 | assert.Equal(t, false, ok) 161 | assert.Equal(t, "value1", fields["key1"]) 162 | } 163 | 164 | func TestUserSuppliedFieldDoesNotOverwriteDefaults(t *testing.T) { 165 | LogAndAssertJSON(t, func(log *Logger) { 166 | log.WithField("msg", "hello").Info("test") 167 | }, func(fields Fields) { 168 | assert.Equal(t, fields["msg"], "test") 169 | }) 170 | } 171 | 172 | func TestUserSuppliedMsgFieldHasPrefix(t *testing.T) { 173 | LogAndAssertJSON(t, func(log *Logger) { 174 | log.WithField("msg", "hello").Info("test") 175 | }, func(fields Fields) { 176 | assert.Equal(t, fields["msg"], "test") 177 | assert.Equal(t, fields["fields.msg"], "hello") 178 | }) 179 | } 180 | 181 | func TestUserSuppliedTimeFieldHasPrefix(t *testing.T) { 182 | LogAndAssertJSON(t, func(log *Logger) { 183 | log.WithField("time", "hello").Info("test") 184 | }, func(fields Fields) { 185 | assert.Equal(t, fields["fields.time"], "hello") 186 | }) 187 | } 188 | 189 | func TestUserSuppliedLevelFieldHasPrefix(t *testing.T) { 190 | LogAndAssertJSON(t, func(log *Logger) { 191 | log.WithField("level", 1).Info("test") 192 | }, func(fields Fields) { 193 | assert.Equal(t, fields["level"], "info") 194 | assert.Equal(t, fields["fields.level"], 1.0) // JSON has floats only 195 | }) 196 | } 197 | 198 | func TestDefaultFieldsAreNotPrefixed(t *testing.T) { 199 | LogAndAssertText(t, func(log *Logger) { 200 | ll := log.WithField("herp", "derp") 201 | ll.Info("hello") 202 | ll.Info("bye") 203 | }, func(fields map[string]string) { 204 | for _, fieldName := range []string{"fields.level", "fields.time", "fields.msg"} { 205 | if _, ok := fields[fieldName]; ok { 206 | t.Fatalf("should not have prefixed %q: %v", fieldName, fields) 207 | } 208 | } 209 | }) 210 | } 211 | 212 | func TestDoubleLoggingDoesntPrefixPreviousFields(t *testing.T) { 213 | 214 | var buffer bytes.Buffer 215 | var fields Fields 216 | 217 | logger := New() 218 | logger.Out = &buffer 219 | logger.Formatter = new(JSONFormatter) 220 | 221 | llog := logger.WithField("context", "eating raw fish") 222 | 223 | llog.Info("looks delicious") 224 | 225 | err := json.Unmarshal(buffer.Bytes(), &fields) 226 | assert.NoError(t, err, "should have decoded first message") 227 | assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") 228 | assert.Equal(t, fields["msg"], "looks delicious") 229 | assert.Equal(t, fields["context"], "eating raw fish") 230 | 231 | buffer.Reset() 232 | 233 | llog.Warn("omg it is!") 234 | 235 | err = json.Unmarshal(buffer.Bytes(), &fields) 236 | assert.NoError(t, err, "should have decoded second message") 237 | assert.Equal(t, len(fields), 4, "should only have msg/time/level/context fields") 238 | assert.Equal(t, fields["msg"], "omg it is!") 239 | assert.Equal(t, fields["context"], "eating raw fish") 240 | assert.Nil(t, fields["fields.msg"], "should not have prefixed previous `msg` entry") 241 | 242 | } 243 | 244 | func TestConvertLevelToString(t *testing.T) { 245 | assert.Equal(t, "debug", DebugLevel.String()) 246 | assert.Equal(t, "info", InfoLevel.String()) 247 | assert.Equal(t, "warning", WarnLevel.String()) 248 | assert.Equal(t, "error", ErrorLevel.String()) 249 | assert.Equal(t, "fatal", FatalLevel.String()) 250 | assert.Equal(t, "panic", PanicLevel.String()) 251 | } 252 | 253 | func TestParseLevel(t *testing.T) { 254 | l, err := ParseLevel("panic") 255 | assert.Nil(t, err) 256 | assert.Equal(t, PanicLevel, l) 257 | 258 | l, err = ParseLevel("fatal") 259 | assert.Nil(t, err) 260 | assert.Equal(t, FatalLevel, l) 261 | 262 | l, err = ParseLevel("error") 263 | assert.Nil(t, err) 264 | assert.Equal(t, ErrorLevel, l) 265 | 266 | l, err = ParseLevel("warn") 267 | assert.Nil(t, err) 268 | assert.Equal(t, WarnLevel, l) 269 | 270 | l, err = ParseLevel("warning") 271 | assert.Nil(t, err) 272 | assert.Equal(t, WarnLevel, l) 273 | 274 | l, err = ParseLevel("info") 275 | assert.Nil(t, err) 276 | assert.Equal(t, InfoLevel, l) 277 | 278 | l, err = ParseLevel("debug") 279 | assert.Nil(t, err) 280 | assert.Equal(t, DebugLevel, l) 281 | 282 | l, err = ParseLevel("invalid") 283 | assert.Equal(t, "not a valid logrus Level: \"invalid\"", err.Error()) 284 | } 285 | 286 | func TestGetSetLevelRace(t *testing.T) { 287 | wg := sync.WaitGroup{} 288 | for i := 0; i < 100; i++ { 289 | wg.Add(1) 290 | go func(i int) { 291 | defer wg.Done() 292 | if i%2 == 0 { 293 | SetLevel(InfoLevel) 294 | } else { 295 | GetLevel() 296 | } 297 | }(i) 298 | 299 | } 300 | wg.Wait() 301 | } 302 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/terminal_bsd.go: -------------------------------------------------------------------------------- 1 | // +build darwin freebsd openbsd netbsd dragonfly 2 | 3 | package logrus 4 | 5 | import "syscall" 6 | 7 | const ioctlReadTermios = syscall.TIOCGETA 8 | 9 | type Termios syscall.Termios 10 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/terminal_linux.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2013 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | package logrus 7 | 8 | import "syscall" 9 | 10 | const ioctlReadTermios = syscall.TCGETS 11 | 12 | type Termios syscall.Termios 13 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/terminal_notwindows.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2011 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build linux darwin freebsd openbsd netbsd dragonfly 7 | 8 | package logrus 9 | 10 | import ( 11 | "syscall" 12 | "unsafe" 13 | ) 14 | 15 | // IsTerminal returns true if the given file descriptor is a terminal. 16 | func IsTerminal() bool { 17 | fd := syscall.Stdout 18 | var termios Termios 19 | _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0) 20 | return err == 0 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/terminal_windows.go: -------------------------------------------------------------------------------- 1 | // Based on ssh/terminal: 2 | // Copyright 2011 The Go Authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style 4 | // license that can be found in the LICENSE file. 5 | 6 | // +build windows 7 | 8 | package logrus 9 | 10 | import ( 11 | "syscall" 12 | "unsafe" 13 | ) 14 | 15 | var kernel32 = syscall.NewLazyDLL("kernel32.dll") 16 | 17 | var ( 18 | procGetConsoleMode = kernel32.NewProc("GetConsoleMode") 19 | ) 20 | 21 | // IsTerminal returns true if the given file descriptor is a terminal. 22 | func IsTerminal() bool { 23 | fd := syscall.Stdout 24 | var st uint32 25 | r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0) 26 | return r != 0 && e == 0 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/text_formatter.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "runtime" 7 | "sort" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | const ( 13 | nocolor = 0 14 | red = 31 15 | green = 32 16 | yellow = 33 17 | blue = 34 18 | gray = 37 19 | ) 20 | 21 | var ( 22 | baseTimestamp time.Time 23 | isTerminal bool 24 | ) 25 | 26 | func init() { 27 | baseTimestamp = time.Now() 28 | isTerminal = IsTerminal() 29 | } 30 | 31 | func miniTS() int { 32 | return int(time.Since(baseTimestamp) / time.Second) 33 | } 34 | 35 | type TextFormatter struct { 36 | // Set to true to bypass checking for a TTY before outputting colors. 37 | ForceColors bool 38 | 39 | // Force disabling colors. 40 | DisableColors bool 41 | 42 | // Disable timestamp logging. useful when output is redirected to logging 43 | // system that already adds timestamps. 44 | DisableTimestamp bool 45 | 46 | // Enable logging the full timestamp when a TTY is attached instead of just 47 | // the time passed since beginning of execution. 48 | FullTimestamp bool 49 | 50 | // TimestampFormat to use for display when a full timestamp is printed 51 | TimestampFormat string 52 | 53 | // The fields are sorted by default for a consistent output. For applications 54 | // that log extremely frequently and don't use the JSON formatter this may not 55 | // be desired. 56 | DisableSorting bool 57 | } 58 | 59 | func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { 60 | var keys []string = make([]string, 0, len(entry.Data)) 61 | for k := range entry.Data { 62 | keys = append(keys, k) 63 | } 64 | 65 | if !f.DisableSorting { 66 | sort.Strings(keys) 67 | } 68 | 69 | b := &bytes.Buffer{} 70 | 71 | prefixFieldClashes(entry.Data) 72 | 73 | isColorTerminal := isTerminal && (runtime.GOOS != "windows") 74 | isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors 75 | 76 | timestampFormat := f.TimestampFormat 77 | if timestampFormat == "" { 78 | timestampFormat = DefaultTimestampFormat 79 | } 80 | if isColored { 81 | f.printColored(b, entry, keys, timestampFormat) 82 | } else { 83 | if !f.DisableTimestamp { 84 | f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat)) 85 | } 86 | f.appendKeyValue(b, "level", entry.Level.String()) 87 | f.appendKeyValue(b, "msg", entry.Message) 88 | for _, key := range keys { 89 | f.appendKeyValue(b, key, entry.Data[key]) 90 | } 91 | } 92 | 93 | b.WriteByte('\n') 94 | return b.Bytes(), nil 95 | } 96 | 97 | func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) { 98 | var levelColor int 99 | switch entry.Level { 100 | case DebugLevel: 101 | levelColor = gray 102 | case WarnLevel: 103 | levelColor = yellow 104 | case ErrorLevel, FatalLevel, PanicLevel: 105 | levelColor = red 106 | default: 107 | levelColor = blue 108 | } 109 | 110 | levelText := strings.ToUpper(entry.Level.String())[0:4] 111 | 112 | if !f.FullTimestamp { 113 | fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message) 114 | } else { 115 | fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message) 116 | } 117 | for _, k := range keys { 118 | v := entry.Data[k] 119 | fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v) 120 | } 121 | } 122 | 123 | func needsQuoting(text string) bool { 124 | for _, ch := range text { 125 | if !((ch >= 'a' && ch <= 'z') || 126 | (ch >= 'A' && ch <= 'Z') || 127 | (ch >= '0' && ch <= '9') || 128 | ch == '-' || ch == '.') { 129 | return false 130 | } 131 | } 132 | return true 133 | } 134 | 135 | func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) { 136 | 137 | b.WriteString(key) 138 | b.WriteByte('=') 139 | 140 | switch value := value.(type) { 141 | case string: 142 | if needsQuoting(value) { 143 | b.WriteString(value) 144 | } else { 145 | fmt.Fprintf(b, "%q", value) 146 | } 147 | case error: 148 | errmsg := value.Error() 149 | if needsQuoting(errmsg) { 150 | b.WriteString(errmsg) 151 | } else { 152 | fmt.Fprintf(b, "%q", value) 153 | } 154 | default: 155 | fmt.Fprint(b, value) 156 | } 157 | 158 | b.WriteByte(' ') 159 | } 160 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/text_formatter_test.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | func TestQuoting(t *testing.T) { 11 | tf := &TextFormatter{DisableColors: true} 12 | 13 | checkQuoting := func(q bool, value interface{}) { 14 | b, _ := tf.Format(WithField("test", value)) 15 | idx := bytes.Index(b, ([]byte)("test=")) 16 | cont := bytes.Contains(b[idx+5:], []byte{'"'}) 17 | if cont != q { 18 | if q { 19 | t.Errorf("quoting expected for: %#v", value) 20 | } else { 21 | t.Errorf("quoting not expected for: %#v", value) 22 | } 23 | } 24 | } 25 | 26 | checkQuoting(false, "abcd") 27 | checkQuoting(false, "v1.0") 28 | checkQuoting(false, "1234567890") 29 | checkQuoting(true, "/foobar") 30 | checkQuoting(true, "x y") 31 | checkQuoting(true, "x,y") 32 | checkQuoting(false, errors.New("invalid")) 33 | checkQuoting(true, errors.New("invalid argument")) 34 | } 35 | 36 | func TestTimestampFormat(t *testing.T) { 37 | checkTimeStr := func(format string) { 38 | customFormatter := &TextFormatter{DisableColors: true, TimestampFormat: format} 39 | customStr, _ := customFormatter.Format(WithField("test", "test")) 40 | timeStart := bytes.Index(customStr, ([]byte)("time=")) 41 | timeEnd := bytes.Index(customStr, ([]byte)("level=")) 42 | timeStr := customStr[timeStart+5 : timeEnd-1] 43 | if timeStr[0] == '"' && timeStr[len(timeStr)-1] == '"' { 44 | timeStr = timeStr[1 : len(timeStr)-1] 45 | } 46 | if format == "" { 47 | format = time.RFC3339 48 | } 49 | _, e := time.Parse(format, (string)(timeStr)) 50 | if e != nil { 51 | t.Errorf("time string \"%s\" did not match provided time format \"%s\": %s", timeStr, format, e) 52 | } 53 | } 54 | 55 | checkTimeStr("2006-01-02T15:04:05.000000000Z07:00") 56 | checkTimeStr("Mon Jan _2 15:04:05 2006") 57 | checkTimeStr("") 58 | } 59 | 60 | // TODO add tests for sorting etc., this requires a parser for the text 61 | // formatter output. 62 | -------------------------------------------------------------------------------- /vendor/github.com/Sirupsen/logrus/writer.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "runtime" 7 | ) 8 | 9 | func (logger *Logger) Writer() *io.PipeWriter { 10 | reader, writer := io.Pipe() 11 | 12 | go logger.writerScanner(reader) 13 | runtime.SetFinalizer(writer, writerFinalizer) 14 | 15 | return writer 16 | } 17 | 18 | func (logger *Logger) writerScanner(reader *io.PipeReader) { 19 | scanner := bufio.NewScanner(reader) 20 | for scanner.Scan() { 21 | logger.Print(scanner.Text()) 22 | } 23 | if err := scanner.Err(); err != nil { 24 | logger.Errorf("Error while reading from Writer: %s", err) 25 | } 26 | reader.Close() 27 | } 28 | 29 | func writerFinalizer(writer *io.PipeWriter) { 30 | writer.Close() 31 | } 32 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/.gitignore: -------------------------------------------------------------------------------- 1 | *.coverprofile 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | dist: trusty 4 | osx_image: xcode8.3 5 | go: 1.8.x 6 | 7 | os: 8 | - linux 9 | - osx 10 | 11 | cache: 12 | directories: 13 | - node_modules 14 | 15 | before_script: 16 | - go get github.com/urfave/gfmrun/... || true 17 | - go get golang.org/x/tools/cmd/goimports 18 | - if [ ! -f node_modules/.bin/markdown-toc ] ; then 19 | npm install markdown-toc ; 20 | fi 21 | 22 | script: 23 | - ./runtests gen 24 | - ./runtests vet 25 | - ./runtests test 26 | - ./runtests gfmrun 27 | - ./runtests toc 28 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jeremy Saenz & Contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | os: Windows Server 2016 4 | 5 | image: Visual Studio 2017 6 | 7 | clone_folder: c:\gopath\src\github.com\urfave\cli 8 | 9 | environment: 10 | GOPATH: C:\gopath 11 | GOVERSION: 1.8.x 12 | PYTHON: C:\Python36-x64 13 | PYTHON_VERSION: 3.6.x 14 | PYTHON_ARCH: 64 15 | 16 | install: 17 | - set PATH=%GOPATH%\bin;C:\go\bin;%PATH% 18 | - go version 19 | - go env 20 | - go get github.com/urfave/gfmrun/... 21 | - go get -v -t ./... 22 | 23 | build_script: 24 | - python runtests vet 25 | - python runtests test 26 | - python runtests gfmrun 27 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/category.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // CommandCategories is a slice of *CommandCategory. 4 | type CommandCategories []*CommandCategory 5 | 6 | // CommandCategory is a category containing commands. 7 | type CommandCategory struct { 8 | Name string 9 | Commands Commands 10 | } 11 | 12 | func (c CommandCategories) Less(i, j int) bool { 13 | return c[i].Name < c[j].Name 14 | } 15 | 16 | func (c CommandCategories) Len() int { 17 | return len(c) 18 | } 19 | 20 | func (c CommandCategories) Swap(i, j int) { 21 | c[i], c[j] = c[j], c[i] 22 | } 23 | 24 | // AddCommand adds a command to a category. 25 | func (c CommandCategories) AddCommand(category string, command Command) CommandCategories { 26 | for _, commandCategory := range c { 27 | if commandCategory.Name == category { 28 | commandCategory.Commands = append(commandCategory.Commands, command) 29 | return c 30 | } 31 | } 32 | return append(c, &CommandCategory{Name: category, Commands: []Command{command}}) 33 | } 34 | 35 | // VisibleCommands returns a slice of the Commands with Hidden=false 36 | func (c *CommandCategory) VisibleCommands() []Command { 37 | ret := []Command{} 38 | for _, command := range c.Commands { 39 | if !command.Hidden { 40 | ret = append(ret, command) 41 | } 42 | } 43 | return ret 44 | } 45 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli provides a minimal framework for creating and organizing command line 2 | // Go applications. cli is designed to be easy to understand and write, the most simple 3 | // cli application can be written as follows: 4 | // func main() { 5 | // cli.NewApp().Run(os.Args) 6 | // } 7 | // 8 | // Of course this application does not do much, so let's make this an actual application: 9 | // func main() { 10 | // app := cli.NewApp() 11 | // app.Name = "greet" 12 | // app.Usage = "say a greeting" 13 | // app.Action = func(c *cli.Context) error { 14 | // println("Greetings") 15 | // return nil 16 | // } 17 | // 18 | // app.Run(os.Args) 19 | // } 20 | package cli 21 | 22 | //go:generate python ./generate-flag-types cli -i flag-types.json -o flag_generated.go 23 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/command_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "fmt" 7 | "io/ioutil" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestCommandFlagParsing(t *testing.T) { 13 | cases := []struct { 14 | testArgs []string 15 | skipFlagParsing bool 16 | skipArgReorder bool 17 | expectedErr error 18 | }{ 19 | // Test normal "not ignoring flags" flow 20 | {[]string{"test-cmd", "blah", "blah", "-break"}, false, false, errors.New("flag provided but not defined: -break")}, 21 | 22 | // Test no arg reorder 23 | {[]string{"test-cmd", "blah", "blah", "-break"}, false, true, nil}, 24 | 25 | {[]string{"test-cmd", "blah", "blah"}, true, false, nil}, // Test SkipFlagParsing without any args that look like flags 26 | {[]string{"test-cmd", "blah", "-break"}, true, false, nil}, // Test SkipFlagParsing with random flag arg 27 | {[]string{"test-cmd", "blah", "-help"}, true, false, nil}, // Test SkipFlagParsing with "special" help flag arg 28 | } 29 | 30 | for _, c := range cases { 31 | app := NewApp() 32 | app.Writer = ioutil.Discard 33 | set := flag.NewFlagSet("test", 0) 34 | set.Parse(c.testArgs) 35 | 36 | context := NewContext(app, set, nil) 37 | 38 | command := Command{ 39 | Name: "test-cmd", 40 | Aliases: []string{"tc"}, 41 | Usage: "this is for testing", 42 | Description: "testing", 43 | Action: func(_ *Context) error { return nil }, 44 | SkipFlagParsing: c.skipFlagParsing, 45 | SkipArgReorder: c.skipArgReorder, 46 | } 47 | 48 | err := command.Run(context) 49 | 50 | expect(t, err, c.expectedErr) 51 | expect(t, []string(context.Args()), c.testArgs) 52 | } 53 | } 54 | 55 | func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) { 56 | app := NewApp() 57 | app.Commands = []Command{ 58 | { 59 | Name: "bar", 60 | Before: func(c *Context) error { 61 | return fmt.Errorf("before error") 62 | }, 63 | After: func(c *Context) error { 64 | return fmt.Errorf("after error") 65 | }, 66 | }, 67 | } 68 | 69 | err := app.Run([]string{"foo", "bar"}) 70 | if err == nil { 71 | t.Fatalf("expected to receive error from Run, got none") 72 | } 73 | 74 | if !strings.Contains(err.Error(), "before error") { 75 | t.Errorf("expected text of error from Before method, but got none in \"%v\"", err) 76 | } 77 | if !strings.Contains(err.Error(), "after error") { 78 | t.Errorf("expected text of error from After method, but got none in \"%v\"", err) 79 | } 80 | } 81 | 82 | func TestCommand_Run_BeforeSavesMetadata(t *testing.T) { 83 | var receivedMsgFromAction string 84 | var receivedMsgFromAfter string 85 | 86 | app := NewApp() 87 | app.Commands = []Command{ 88 | { 89 | Name: "bar", 90 | Before: func(c *Context) error { 91 | c.App.Metadata["msg"] = "hello world" 92 | return nil 93 | }, 94 | Action: func(c *Context) error { 95 | msg, ok := c.App.Metadata["msg"] 96 | if !ok { 97 | return errors.New("msg not found") 98 | } 99 | receivedMsgFromAction = msg.(string) 100 | return nil 101 | }, 102 | After: func(c *Context) error { 103 | msg, ok := c.App.Metadata["msg"] 104 | if !ok { 105 | return errors.New("msg not found") 106 | } 107 | receivedMsgFromAfter = msg.(string) 108 | return nil 109 | }, 110 | }, 111 | } 112 | 113 | err := app.Run([]string{"foo", "bar"}) 114 | if err != nil { 115 | t.Fatalf("expected no error from Run, got %s", err) 116 | } 117 | 118 | expectedMsg := "hello world" 119 | 120 | if receivedMsgFromAction != expectedMsg { 121 | t.Fatalf("expected msg from Action to match. Given: %q\nExpected: %q", 122 | receivedMsgFromAction, expectedMsg) 123 | } 124 | if receivedMsgFromAfter != expectedMsg { 125 | t.Fatalf("expected msg from After to match. Given: %q\nExpected: %q", 126 | receivedMsgFromAction, expectedMsg) 127 | } 128 | } 129 | 130 | func TestCommand_OnUsageError_hasCommandContext(t *testing.T) { 131 | app := NewApp() 132 | app.Commands = []Command{ 133 | { 134 | Name: "bar", 135 | Flags: []Flag{ 136 | IntFlag{Name: "flag"}, 137 | }, 138 | OnUsageError: func(c *Context, err error, _ bool) error { 139 | return fmt.Errorf("intercepted in %s: %s", c.Command.Name, err.Error()) 140 | }, 141 | }, 142 | } 143 | 144 | err := app.Run([]string{"foo", "bar", "--flag=wrong"}) 145 | if err == nil { 146 | t.Fatalf("expected to receive error from Run, got none") 147 | } 148 | 149 | if !strings.HasPrefix(err.Error(), "intercepted in bar") { 150 | t.Errorf("Expect an intercepted error, but got \"%v\"", err) 151 | } 152 | } 153 | 154 | func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) { 155 | app := NewApp() 156 | app.Commands = []Command{ 157 | { 158 | Name: "bar", 159 | Flags: []Flag{ 160 | IntFlag{Name: "flag"}, 161 | }, 162 | OnUsageError: func(c *Context, err error, _ bool) error { 163 | if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { 164 | t.Errorf("Expect an invalid value error, but got \"%v\"", err) 165 | } 166 | return errors.New("intercepted: " + err.Error()) 167 | }, 168 | }, 169 | } 170 | 171 | err := app.Run([]string{"foo", "bar", "--flag=wrong"}) 172 | if err == nil { 173 | t.Fatalf("expected to receive error from Run, got none") 174 | } 175 | 176 | if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { 177 | t.Errorf("Expect an intercepted error, but got \"%v\"", err) 178 | } 179 | } 180 | 181 | func TestCommand_OnUsageError_WithSubcommand(t *testing.T) { 182 | app := NewApp() 183 | app.Commands = []Command{ 184 | { 185 | Name: "bar", 186 | Subcommands: []Command{ 187 | { 188 | Name: "baz", 189 | }, 190 | }, 191 | Flags: []Flag{ 192 | IntFlag{Name: "flag"}, 193 | }, 194 | OnUsageError: func(c *Context, err error, _ bool) error { 195 | if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") { 196 | t.Errorf("Expect an invalid value error, but got \"%v\"", err) 197 | } 198 | return errors.New("intercepted: " + err.Error()) 199 | }, 200 | }, 201 | } 202 | 203 | err := app.Run([]string{"foo", "bar", "--flag=wrong"}) 204 | if err == nil { 205 | t.Fatalf("expected to receive error from Run, got none") 206 | } 207 | 208 | if !strings.HasPrefix(err.Error(), "intercepted: invalid value") { 209 | t.Errorf("Expect an intercepted error, but got \"%v\"", err) 210 | } 211 | } 212 | 213 | func TestCommand_Run_SubcommandsCanUseErrWriter(t *testing.T) { 214 | app := NewApp() 215 | app.ErrWriter = ioutil.Discard 216 | app.Commands = []Command{ 217 | { 218 | Name: "bar", 219 | Usage: "this is for testing", 220 | Subcommands: []Command{ 221 | { 222 | Name: "baz", 223 | Usage: "this is for testing", 224 | Action: func(c *Context) error { 225 | if c.App.ErrWriter != ioutil.Discard { 226 | return fmt.Errorf("ErrWriter not passed") 227 | } 228 | 229 | return nil 230 | }, 231 | }, 232 | }, 233 | }, 234 | } 235 | 236 | err := app.Run([]string{"foo", "bar", "baz"}) 237 | if err != nil { 238 | t.Fatal(err) 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/context.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "reflect" 7 | "strings" 8 | "syscall" 9 | ) 10 | 11 | // Context is a type that is passed through to 12 | // each Handler action in a cli application. Context 13 | // can be used to retrieve context-specific Args and 14 | // parsed command-line options. 15 | type Context struct { 16 | App *App 17 | Command Command 18 | shellComplete bool 19 | flagSet *flag.FlagSet 20 | setFlags map[string]bool 21 | parentContext *Context 22 | } 23 | 24 | // NewContext creates a new context. For use in when invoking an App or Command action. 25 | func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context { 26 | c := &Context{App: app, flagSet: set, parentContext: parentCtx} 27 | 28 | if parentCtx != nil { 29 | c.shellComplete = parentCtx.shellComplete 30 | } 31 | 32 | return c 33 | } 34 | 35 | // NumFlags returns the number of flags set 36 | func (c *Context) NumFlags() int { 37 | return c.flagSet.NFlag() 38 | } 39 | 40 | // Set sets a context flag to a value. 41 | func (c *Context) Set(name, value string) error { 42 | c.setFlags = nil 43 | return c.flagSet.Set(name, value) 44 | } 45 | 46 | // GlobalSet sets a context flag to a value on the global flagset 47 | func (c *Context) GlobalSet(name, value string) error { 48 | globalContext(c).setFlags = nil 49 | return globalContext(c).flagSet.Set(name, value) 50 | } 51 | 52 | // IsSet determines if the flag was actually set 53 | func (c *Context) IsSet(name string) bool { 54 | if c.setFlags == nil { 55 | c.setFlags = make(map[string]bool) 56 | 57 | c.flagSet.Visit(func(f *flag.Flag) { 58 | c.setFlags[f.Name] = true 59 | }) 60 | 61 | c.flagSet.VisitAll(func(f *flag.Flag) { 62 | if _, ok := c.setFlags[f.Name]; ok { 63 | return 64 | } 65 | c.setFlags[f.Name] = false 66 | }) 67 | 68 | // XXX hack to support IsSet for flags with EnvVar 69 | // 70 | // There isn't an easy way to do this with the current implementation since 71 | // whether a flag was set via an environment variable is very difficult to 72 | // determine here. Instead, we intend to introduce a backwards incompatible 73 | // change in version 2 to add `IsSet` to the Flag interface to push the 74 | // responsibility closer to where the information required to determine 75 | // whether a flag is set by non-standard means such as environment 76 | // variables is avaliable. 77 | // 78 | // See https://github.com/urfave/cli/issues/294 for additional discussion 79 | flags := c.Command.Flags 80 | if c.Command.Name == "" { // cannot == Command{} since it contains slice types 81 | if c.App != nil { 82 | flags = c.App.Flags 83 | } 84 | } 85 | for _, f := range flags { 86 | eachName(f.GetName(), func(name string) { 87 | if isSet, ok := c.setFlags[name]; isSet || !ok { 88 | return 89 | } 90 | 91 | val := reflect.ValueOf(f) 92 | if val.Kind() == reflect.Ptr { 93 | val = val.Elem() 94 | } 95 | 96 | envVarValue := val.FieldByName("EnvVar") 97 | if !envVarValue.IsValid() { 98 | return 99 | } 100 | 101 | eachName(envVarValue.String(), func(envVar string) { 102 | envVar = strings.TrimSpace(envVar) 103 | if _, ok := syscall.Getenv(envVar); ok { 104 | c.setFlags[name] = true 105 | return 106 | } 107 | }) 108 | }) 109 | } 110 | } 111 | 112 | return c.setFlags[name] 113 | } 114 | 115 | // GlobalIsSet determines if the global flag was actually set 116 | func (c *Context) GlobalIsSet(name string) bool { 117 | ctx := c 118 | if ctx.parentContext != nil { 119 | ctx = ctx.parentContext 120 | } 121 | 122 | for ; ctx != nil; ctx = ctx.parentContext { 123 | if ctx.IsSet(name) { 124 | return true 125 | } 126 | } 127 | return false 128 | } 129 | 130 | // FlagNames returns a slice of flag names used in this context. 131 | func (c *Context) FlagNames() (names []string) { 132 | for _, flag := range c.Command.Flags { 133 | name := strings.Split(flag.GetName(), ",")[0] 134 | if name == "help" { 135 | continue 136 | } 137 | names = append(names, name) 138 | } 139 | return 140 | } 141 | 142 | // GlobalFlagNames returns a slice of global flag names used by the app. 143 | func (c *Context) GlobalFlagNames() (names []string) { 144 | for _, flag := range c.App.Flags { 145 | name := strings.Split(flag.GetName(), ",")[0] 146 | if name == "help" || name == "version" { 147 | continue 148 | } 149 | names = append(names, name) 150 | } 151 | return 152 | } 153 | 154 | // Parent returns the parent context, if any 155 | func (c *Context) Parent() *Context { 156 | return c.parentContext 157 | } 158 | 159 | // value returns the value of the flag coressponding to `name` 160 | func (c *Context) value(name string) interface{} { 161 | return c.flagSet.Lookup(name).Value.(flag.Getter).Get() 162 | } 163 | 164 | // Args contains apps console arguments 165 | type Args []string 166 | 167 | // Args returns the command line arguments associated with the context. 168 | func (c *Context) Args() Args { 169 | args := Args(c.flagSet.Args()) 170 | return args 171 | } 172 | 173 | // NArg returns the number of the command line arguments. 174 | func (c *Context) NArg() int { 175 | return len(c.Args()) 176 | } 177 | 178 | // Get returns the nth argument, or else a blank string 179 | func (a Args) Get(n int) string { 180 | if len(a) > n { 181 | return a[n] 182 | } 183 | return "" 184 | } 185 | 186 | // First returns the first argument, or else a blank string 187 | func (a Args) First() string { 188 | return a.Get(0) 189 | } 190 | 191 | // Tail returns the rest of the arguments (not the first one) 192 | // or else an empty string slice 193 | func (a Args) Tail() []string { 194 | if len(a) >= 2 { 195 | return []string(a)[1:] 196 | } 197 | return []string{} 198 | } 199 | 200 | // Present checks if there are any arguments present 201 | func (a Args) Present() bool { 202 | return len(a) != 0 203 | } 204 | 205 | // Swap swaps arguments at the given indexes 206 | func (a Args) Swap(from, to int) error { 207 | if from >= len(a) || to >= len(a) { 208 | return errors.New("index out of range") 209 | } 210 | a[from], a[to] = a[to], a[from] 211 | return nil 212 | } 213 | 214 | func globalContext(ctx *Context) *Context { 215 | if ctx == nil { 216 | return nil 217 | } 218 | 219 | for { 220 | if ctx.parentContext == nil { 221 | return ctx 222 | } 223 | ctx = ctx.parentContext 224 | } 225 | } 226 | 227 | func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet { 228 | if ctx.parentContext != nil { 229 | ctx = ctx.parentContext 230 | } 231 | for ; ctx != nil; ctx = ctx.parentContext { 232 | if f := ctx.flagSet.Lookup(name); f != nil { 233 | return ctx.flagSet 234 | } 235 | } 236 | return nil 237 | } 238 | 239 | func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) { 240 | switch ff.Value.(type) { 241 | case *StringSlice: 242 | default: 243 | set.Set(name, ff.Value.String()) 244 | } 245 | } 246 | 247 | func normalizeFlags(flags []Flag, set *flag.FlagSet) error { 248 | visited := make(map[string]bool) 249 | set.Visit(func(f *flag.Flag) { 250 | visited[f.Name] = true 251 | }) 252 | for _, f := range flags { 253 | parts := strings.Split(f.GetName(), ",") 254 | if len(parts) == 1 { 255 | continue 256 | } 257 | var ff *flag.Flag 258 | for _, name := range parts { 259 | name = strings.Trim(name, " ") 260 | if visited[name] { 261 | if ff != nil { 262 | return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name) 263 | } 264 | ff = set.Lookup(name) 265 | } 266 | } 267 | if ff == nil { 268 | continue 269 | } 270 | for _, name := range parts { 271 | name = strings.Trim(name, " ") 272 | if !visited[name] { 273 | copyFlag(name, ff, set) 274 | } 275 | } 276 | } 277 | return nil 278 | } 279 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/errors.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | ) 9 | 10 | // OsExiter is the function used when the app exits. If not set defaults to os.Exit. 11 | var OsExiter = os.Exit 12 | 13 | // ErrWriter is used to write errors to the user. This can be anything 14 | // implementing the io.Writer interface and defaults to os.Stderr. 15 | var ErrWriter io.Writer = os.Stderr 16 | 17 | // MultiError is an error that wraps multiple errors. 18 | type MultiError struct { 19 | Errors []error 20 | } 21 | 22 | // NewMultiError creates a new MultiError. Pass in one or more errors. 23 | func NewMultiError(err ...error) MultiError { 24 | return MultiError{Errors: err} 25 | } 26 | 27 | // Error implements the error interface. 28 | func (m MultiError) Error() string { 29 | errs := make([]string, len(m.Errors)) 30 | for i, err := range m.Errors { 31 | errs[i] = err.Error() 32 | } 33 | 34 | return strings.Join(errs, "\n") 35 | } 36 | 37 | type ErrorFormatter interface { 38 | Format(s fmt.State, verb rune) 39 | } 40 | 41 | // ExitCoder is the interface checked by `App` and `Command` for a custom exit 42 | // code 43 | type ExitCoder interface { 44 | error 45 | ExitCode() int 46 | } 47 | 48 | // ExitError fulfills both the builtin `error` interface and `ExitCoder` 49 | type ExitError struct { 50 | exitCode int 51 | message interface{} 52 | } 53 | 54 | // NewExitError makes a new *ExitError 55 | func NewExitError(message interface{}, exitCode int) *ExitError { 56 | return &ExitError{ 57 | exitCode: exitCode, 58 | message: message, 59 | } 60 | } 61 | 62 | // Error returns the string message, fulfilling the interface required by 63 | // `error` 64 | func (ee *ExitError) Error() string { 65 | return fmt.Sprintf("%v", ee.message) 66 | } 67 | 68 | // ExitCode returns the exit code, fulfilling the interface required by 69 | // `ExitCoder` 70 | func (ee *ExitError) ExitCode() int { 71 | return ee.exitCode 72 | } 73 | 74 | // HandleExitCoder checks if the error fulfills the ExitCoder interface, and if 75 | // so prints the error to stderr (if it is non-empty) and calls OsExiter with the 76 | // given exit code. If the given error is a MultiError, then this func is 77 | // called on all members of the Errors slice and calls OsExiter with the last exit code. 78 | func HandleExitCoder(err error) { 79 | if err == nil { 80 | return 81 | } 82 | 83 | if exitErr, ok := err.(ExitCoder); ok { 84 | if err.Error() != "" { 85 | if _, ok := exitErr.(ErrorFormatter); ok { 86 | fmt.Fprintf(ErrWriter, "%+v\n", err) 87 | } else { 88 | fmt.Fprintln(ErrWriter, err) 89 | } 90 | } 91 | OsExiter(exitErr.ExitCode()) 92 | return 93 | } 94 | 95 | if multiErr, ok := err.(MultiError); ok { 96 | code := handleMultiError(multiErr) 97 | OsExiter(code) 98 | return 99 | } 100 | } 101 | 102 | func handleMultiError(multiErr MultiError) int { 103 | code := 1 104 | for _, merr := range multiErr.Errors { 105 | if multiErr2, ok := merr.(MultiError); ok { 106 | code = handleMultiError(multiErr2) 107 | } else { 108 | fmt.Fprintln(ErrWriter, merr) 109 | if exitErr, ok := merr.(ExitCoder); ok { 110 | code = exitErr.ExitCode() 111 | } 112 | } 113 | } 114 | return code 115 | } 116 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/errors_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "testing" 8 | ) 9 | 10 | func TestHandleExitCoder_nil(t *testing.T) { 11 | exitCode := 0 12 | called := false 13 | 14 | OsExiter = func(rc int) { 15 | if !called { 16 | exitCode = rc 17 | called = true 18 | } 19 | } 20 | 21 | defer func() { OsExiter = fakeOsExiter }() 22 | 23 | HandleExitCoder(nil) 24 | 25 | expect(t, exitCode, 0) 26 | expect(t, called, false) 27 | } 28 | 29 | func TestHandleExitCoder_ExitCoder(t *testing.T) { 30 | exitCode := 0 31 | called := false 32 | 33 | OsExiter = func(rc int) { 34 | if !called { 35 | exitCode = rc 36 | called = true 37 | } 38 | } 39 | 40 | defer func() { OsExiter = fakeOsExiter }() 41 | 42 | HandleExitCoder(NewExitError("galactic perimeter breach", 9)) 43 | 44 | expect(t, exitCode, 9) 45 | expect(t, called, true) 46 | } 47 | 48 | func TestHandleExitCoder_MultiErrorWithExitCoder(t *testing.T) { 49 | exitCode := 0 50 | called := false 51 | 52 | OsExiter = func(rc int) { 53 | if !called { 54 | exitCode = rc 55 | called = true 56 | } 57 | } 58 | 59 | defer func() { OsExiter = fakeOsExiter }() 60 | 61 | exitErr := NewExitError("galactic perimeter breach", 9) 62 | exitErr2 := NewExitError("last ExitCoder", 11) 63 | err := NewMultiError(errors.New("wowsa"), errors.New("egad"), exitErr, exitErr2) 64 | HandleExitCoder(err) 65 | 66 | expect(t, exitCode, 11) 67 | expect(t, called, true) 68 | } 69 | 70 | // make a stub to not import pkg/errors 71 | type ErrorWithFormat struct { 72 | error 73 | } 74 | 75 | func NewErrorWithFormat(m string) *ErrorWithFormat { 76 | return &ErrorWithFormat{error: errors.New(m)} 77 | } 78 | 79 | func (f *ErrorWithFormat) Format(s fmt.State, verb rune) { 80 | fmt.Fprintf(s, "This the format: %v", f.error) 81 | } 82 | 83 | func TestHandleExitCoder_ErrorWithFormat(t *testing.T) { 84 | called := false 85 | 86 | OsExiter = func(rc int) { 87 | if !called { 88 | called = true 89 | } 90 | } 91 | ErrWriter = &bytes.Buffer{} 92 | 93 | defer func() { 94 | OsExiter = fakeOsExiter 95 | ErrWriter = fakeErrWriter 96 | }() 97 | 98 | err := NewExitError(NewErrorWithFormat("I am formatted"), 1) 99 | HandleExitCoder(err) 100 | 101 | expect(t, called, true) 102 | expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: I am formatted\n") 103 | } 104 | 105 | func TestHandleExitCoder_MultiErrorWithFormat(t *testing.T) { 106 | called := false 107 | 108 | OsExiter = func(rc int) { 109 | if !called { 110 | called = true 111 | } 112 | } 113 | ErrWriter = &bytes.Buffer{} 114 | 115 | defer func() { OsExiter = fakeOsExiter }() 116 | 117 | err := NewMultiError(NewErrorWithFormat("err1"), NewErrorWithFormat("err2")) 118 | HandleExitCoder(err) 119 | 120 | expect(t, called, true) 121 | expect(t, ErrWriter.(*bytes.Buffer).String(), "This the format: err1\nThis the format: err2\n") 122 | } 123 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/flag-types.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Bool", 4 | "type": "bool", 5 | "value": false, 6 | "context_default": "false", 7 | "parser": "strconv.ParseBool(f.Value.String())" 8 | }, 9 | { 10 | "name": "BoolT", 11 | "type": "bool", 12 | "value": false, 13 | "doctail": " that is true by default", 14 | "context_default": "false", 15 | "parser": "strconv.ParseBool(f.Value.String())" 16 | }, 17 | { 18 | "name": "Duration", 19 | "type": "time.Duration", 20 | "doctail": " (see https://golang.org/pkg/time/#ParseDuration)", 21 | "context_default": "0", 22 | "parser": "time.ParseDuration(f.Value.String())" 23 | }, 24 | { 25 | "name": "Float64", 26 | "type": "float64", 27 | "context_default": "0", 28 | "parser": "strconv.ParseFloat(f.Value.String(), 64)" 29 | }, 30 | { 31 | "name": "Generic", 32 | "type": "Generic", 33 | "dest": false, 34 | "context_default": "nil", 35 | "context_type": "interface{}" 36 | }, 37 | { 38 | "name": "Int64", 39 | "type": "int64", 40 | "context_default": "0", 41 | "parser": "strconv.ParseInt(f.Value.String(), 0, 64)" 42 | }, 43 | { 44 | "name": "Int", 45 | "type": "int", 46 | "context_default": "0", 47 | "parser": "strconv.ParseInt(f.Value.String(), 0, 64)", 48 | "parser_cast": "int(parsed)" 49 | }, 50 | { 51 | "name": "IntSlice", 52 | "type": "*IntSlice", 53 | "dest": false, 54 | "context_default": "nil", 55 | "context_type": "[]int", 56 | "parser": "(f.Value.(*IntSlice)).Value(), error(nil)" 57 | }, 58 | { 59 | "name": "Int64Slice", 60 | "type": "*Int64Slice", 61 | "dest": false, 62 | "context_default": "nil", 63 | "context_type": "[]int64", 64 | "parser": "(f.Value.(*Int64Slice)).Value(), error(nil)" 65 | }, 66 | { 67 | "name": "String", 68 | "type": "string", 69 | "context_default": "\"\"", 70 | "parser": "f.Value.String(), error(nil)" 71 | }, 72 | { 73 | "name": "StringSlice", 74 | "type": "*StringSlice", 75 | "dest": false, 76 | "context_default": "nil", 77 | "context_type": "[]string", 78 | "parser": "(f.Value.(*StringSlice)).Value(), error(nil)" 79 | }, 80 | { 81 | "name": "Uint64", 82 | "type": "uint64", 83 | "context_default": "0", 84 | "parser": "strconv.ParseUint(f.Value.String(), 0, 64)" 85 | }, 86 | { 87 | "name": "Uint", 88 | "type": "uint", 89 | "context_default": "0", 90 | "parser": "strconv.ParseUint(f.Value.String(), 0, 64)", 91 | "parser_cast": "uint(parsed)" 92 | } 93 | ] 94 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/funcs.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | // BashCompleteFunc is an action to execute when the bash-completion flag is set 4 | type BashCompleteFunc func(*Context) 5 | 6 | // BeforeFunc is an action to execute before any subcommands are run, but after 7 | // the context is ready if a non-nil error is returned, no subcommands are run 8 | type BeforeFunc func(*Context) error 9 | 10 | // AfterFunc is an action to execute after any subcommands are run, but after the 11 | // subcommand has finished it is run even if Action() panics 12 | type AfterFunc func(*Context) error 13 | 14 | // ActionFunc is the action to execute when no subcommands are specified 15 | type ActionFunc func(*Context) error 16 | 17 | // CommandNotFoundFunc is executed if the proper command cannot be found 18 | type CommandNotFoundFunc func(*Context, string) 19 | 20 | // OnUsageErrorFunc is executed if an usage error occurs. This is useful for displaying 21 | // customized usage error messages. This function is able to replace the 22 | // original error messages. If this function is not set, the "Incorrect usage" 23 | // is displayed and the execution is interrupted. 24 | type OnUsageErrorFunc func(context *Context, err error, isSubcommand bool) error 25 | 26 | // FlagStringFunc is used by the help generation to display a flag, which is 27 | // expected to be a single line. 28 | type FlagStringFunc func(Flag) string 29 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/helpers_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "os" 5 | "reflect" 6 | "runtime" 7 | "strings" 8 | "testing" 9 | ) 10 | 11 | var ( 12 | wd, _ = os.Getwd() 13 | ) 14 | 15 | func expect(t *testing.T, a interface{}, b interface{}) { 16 | _, fn, line, _ := runtime.Caller(1) 17 | fn = strings.Replace(fn, wd+"/", "", -1) 18 | 19 | if !reflect.DeepEqual(a, b) { 20 | t.Errorf("(%s:%d) Expected %v (type %v) - Got %v (type %v)", fn, line, b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 21 | } 22 | } 23 | 24 | func refute(t *testing.T, a interface{}, b interface{}) { 25 | if reflect.DeepEqual(a, b) { 26 | t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/helpers_unix_test.go: -------------------------------------------------------------------------------- 1 | // +build darwin dragonfly freebsd linux netbsd openbsd solaris 2 | 3 | package cli 4 | 5 | import "os" 6 | 7 | func clearenv() { 8 | os.Clearenv() 9 | } 10 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/helpers_windows_test.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | ) 7 | 8 | // os.Clearenv() doesn't actually unset variables on Windows 9 | // See: https://github.com/golang/go/issues/17902 10 | func clearenv() { 11 | for _, s := range os.Environ() { 12 | for j := 1; j < len(s); j++ { 13 | if s[j] == '=' { 14 | keyp, _ := syscall.UTF16PtrFromString(s[0:j]) 15 | syscall.SetEnvironmentVariable(keyp, nil) 16 | break 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /vendor/github.com/codegangsta/cli/runtests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | 4 | import argparse 5 | import os 6 | import sys 7 | import tempfile 8 | 9 | from subprocess import check_call, check_output 10 | 11 | 12 | PACKAGE_NAME = os.environ.get( 13 | 'CLI_PACKAGE_NAME', 'github.com/urfave/cli' 14 | ) 15 | 16 | 17 | def main(sysargs=sys.argv[:]): 18 | targets = { 19 | 'vet': _vet, 20 | 'test': _test, 21 | 'gfmrun': _gfmrun, 22 | 'toc': _toc, 23 | 'gen': _gen, 24 | } 25 | 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument( 28 | 'target', nargs='?', choices=tuple(targets.keys()), default='test' 29 | ) 30 | args = parser.parse_args(sysargs[1:]) 31 | 32 | targets[args.target]() 33 | return 0 34 | 35 | 36 | def _test(): 37 | if check_output('go version'.split()).split()[2] < 'go1.2': 38 | _run('go test -v .') 39 | return 40 | 41 | coverprofiles = [] 42 | for subpackage in ['', 'altsrc']: 43 | coverprofile = 'cli.coverprofile' 44 | if subpackage != '': 45 | coverprofile = '{}.coverprofile'.format(subpackage) 46 | 47 | coverprofiles.append(coverprofile) 48 | 49 | _run('go test -v'.split() + [ 50 | '-coverprofile={}'.format(coverprofile), 51 | ('{}/{}'.format(PACKAGE_NAME, subpackage)).rstrip('/') 52 | ]) 53 | 54 | combined_name = _combine_coverprofiles(coverprofiles) 55 | _run('go tool cover -func={}'.format(combined_name)) 56 | os.remove(combined_name) 57 | 58 | 59 | def _gfmrun(): 60 | go_version = check_output('go version'.split()).split()[2] 61 | if go_version < 'go1.3': 62 | print('runtests: skip on {}'.format(go_version), file=sys.stderr) 63 | return 64 | _run(['gfmrun', '-c', str(_gfmrun_count()), '-s', 'README.md']) 65 | 66 | 67 | def _vet(): 68 | _run('go vet ./...') 69 | 70 | 71 | def _toc(): 72 | _run('node_modules/.bin/markdown-toc -i README.md') 73 | _run('git diff --exit-code') 74 | 75 | 76 | def _gen(): 77 | go_version = check_output('go version'.split()).split()[2] 78 | if go_version < 'go1.5': 79 | print('runtests: skip on {}'.format(go_version), file=sys.stderr) 80 | return 81 | 82 | _run('go generate ./...') 83 | _run('git diff --exit-code') 84 | 85 | 86 | def _run(command): 87 | if hasattr(command, 'split'): 88 | command = command.split() 89 | print('runtests: {}'.format(' '.join(command)), file=sys.stderr) 90 | check_call(command) 91 | 92 | 93 | def _gfmrun_count(): 94 | with open('README.md') as infile: 95 | lines = infile.read().splitlines() 96 | return len(filter(_is_go_runnable, lines)) 97 | 98 | 99 | def _is_go_runnable(line): 100 | return line.startswith('package main') 101 | 102 | 103 | def _combine_coverprofiles(coverprofiles): 104 | combined = tempfile.NamedTemporaryFile( 105 | suffix='.coverprofile', delete=False 106 | ) 107 | combined.write('mode: set\n') 108 | 109 | for coverprofile in coverprofiles: 110 | with open(coverprofile, 'r') as infile: 111 | for line in infile.readlines(): 112 | if not line.startswith('mode: '): 113 | combined.write(line) 114 | 115 | combined.flush() 116 | name = combined.name 117 | combined.close() 118 | return name 119 | 120 | 121 | if __name__ == '__main__': 122 | sys.exit(main()) 123 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | services: 4 | - redis-server 5 | 6 | go: 7 | - 1.4 8 | - 1.5 9 | - 1.6 10 | - 1.7 11 | - 1.8 12 | - 1.9 13 | - tip 14 | 15 | script: 16 | - go get -t -v ./... 17 | - diff -u <(echo -n) <(gofmt -d .) 18 | - go vet $(go list ./... | grep -v /vendor/) 19 | - go test -v -race ./... 20 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/README.markdown: -------------------------------------------------------------------------------- 1 | Redigo 2 | ====== 3 | 4 | [![Build Status](https://travis-ci.org/garyburd/redigo.svg?branch=master)](https://travis-ci.org/garyburd/redigo) 5 | [![GoDoc](https://godoc.org/github.com/garyburd/redigo/redis?status.svg)](https://godoc.org/github.com/garyburd/redigo/redis) 6 | 7 | Redigo is a [Go](http://golang.org/) client for the [Redis](http://redis.io/) database. 8 | 9 | Features 10 | ------- 11 | 12 | * A [Print-like](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Executing_Commands) API with support for all Redis commands. 13 | * [Pipelining](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Pipelining), including pipelined transactions. 14 | * [Publish/Subscribe](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Publish_and_Subscribe). 15 | * [Connection pooling](http://godoc.org/github.com/garyburd/redigo/redis#Pool). 16 | * [Script helper type](http://godoc.org/github.com/garyburd/redigo/redis#Script) with optimistic use of EVALSHA. 17 | * [Helper functions](http://godoc.org/github.com/garyburd/redigo/redis#hdr-Reply_Helpers) for working with command replies. 18 | 19 | Documentation 20 | ------------- 21 | 22 | - [API Reference](http://godoc.org/github.com/garyburd/redigo/redis) 23 | - [FAQ](https://github.com/garyburd/redigo/wiki/FAQ) 24 | - [Examples](https://godoc.org/github.com/garyburd/redigo/redis#pkg-examples) 25 | 26 | Installation 27 | ------------ 28 | 29 | Install Redigo using the "go get" command: 30 | 31 | go get github.com/garyburd/redigo/redis 32 | 33 | The Go distribution is Redigo's only dependency. 34 | 35 | Related Projects 36 | ---------------- 37 | 38 | - [rafaeljusto/redigomock](https://godoc.org/github.com/rafaeljusto/redigomock) - A mock library for Redigo. 39 | - [chasex/redis-go-cluster](https://github.com/chasex/redis-go-cluster) - A Redis cluster client implementation. 40 | - [FZambia/go-sentinel](https://github.com/FZambia/go-sentinel) - Redis Sentinel support for Redigo 41 | - [PuerkitoBio/redisc](https://github.com/PuerkitoBio/redisc) - Redis Cluster client built on top of Redigo 42 | 43 | Contributing 44 | ------------ 45 | 46 | See [CONTRIBUTING.md](https://github.com/garyburd/redigo/blob/master/.github/CONTRIBUTING.md). 47 | 48 | License 49 | ------- 50 | 51 | Redigo is available under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html). 52 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/internal/commandinfo.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package internal // import "github.com/garyburd/redigo/internal" 16 | 17 | import ( 18 | "strings" 19 | ) 20 | 21 | const ( 22 | WatchState = 1 << iota 23 | MultiState 24 | SubscribeState 25 | MonitorState 26 | ) 27 | 28 | type CommandInfo struct { 29 | Set, Clear int 30 | } 31 | 32 | var commandInfos = map[string]CommandInfo{ 33 | "WATCH": {Set: WatchState}, 34 | "UNWATCH": {Clear: WatchState}, 35 | "MULTI": {Set: MultiState}, 36 | "EXEC": {Clear: WatchState | MultiState}, 37 | "DISCARD": {Clear: WatchState | MultiState}, 38 | "PSUBSCRIBE": {Set: SubscribeState}, 39 | "SUBSCRIBE": {Set: SubscribeState}, 40 | "MONITOR": {Set: MonitorState}, 41 | } 42 | 43 | func init() { 44 | for n, ci := range commandInfos { 45 | commandInfos[strings.ToLower(n)] = ci 46 | } 47 | } 48 | 49 | func LookupCommandInfo(commandName string) CommandInfo { 50 | if ci, ok := commandInfos[commandName]; ok { 51 | return ci 52 | } 53 | return commandInfos[strings.ToUpper(commandName)] 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/internal/commandinfo_test.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import "testing" 4 | 5 | func TestLookupCommandInfo(t *testing.T) { 6 | for _, n := range []string{"watch", "WATCH", "wAtch"} { 7 | if LookupCommandInfo(n) == (CommandInfo{}) { 8 | t.Errorf("LookupCommandInfo(%q) = CommandInfo{}, expected non-zero value", n) 9 | } 10 | } 11 | } 12 | 13 | func benchmarkLookupCommandInfo(b *testing.B, names ...string) { 14 | for i := 0; i < b.N; i++ { 15 | for _, c := range names { 16 | LookupCommandInfo(c) 17 | } 18 | } 19 | } 20 | 21 | func BenchmarkLookupCommandInfoCorrectCase(b *testing.B) { 22 | benchmarkLookupCommandInfo(b, "watch", "WATCH", "monitor", "MONITOR") 23 | } 24 | 25 | func BenchmarkLookupCommandInfoMixedCase(b *testing.B) { 26 | benchmarkLookupCommandInfo(b, "wAtch", "WeTCH", "monItor", "MONiTOR") 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | // Package redis is a client for the Redis database. 16 | // 17 | // The Redigo FAQ (https://github.com/garyburd/redigo/wiki/FAQ) contains more 18 | // documentation about this package. 19 | // 20 | // Connections 21 | // 22 | // The Conn interface is the primary interface for working with Redis. 23 | // Applications create connections by calling the Dial, DialWithTimeout or 24 | // NewConn functions. In the future, functions will be added for creating 25 | // sharded and other types of connections. 26 | // 27 | // The application must call the connection Close method when the application 28 | // is done with the connection. 29 | // 30 | // Executing Commands 31 | // 32 | // The Conn interface has a generic method for executing Redis commands: 33 | // 34 | // Do(commandName string, args ...interface{}) (reply interface{}, err error) 35 | // 36 | // The Redis command reference (http://redis.io/commands) lists the available 37 | // commands. An example of using the Redis APPEND command is: 38 | // 39 | // n, err := conn.Do("APPEND", "key", "value") 40 | // 41 | // The Do method converts command arguments to bulk strings for transmission 42 | // to the server as follows: 43 | // 44 | // Go Type Conversion 45 | // []byte Sent as is 46 | // string Sent as is 47 | // int, int64 strconv.FormatInt(v) 48 | // float64 strconv.FormatFloat(v, 'g', -1, 64) 49 | // bool true -> "1", false -> "0" 50 | // nil "" 51 | // all other types fmt.Fprint(w, v) 52 | // 53 | // Redis command reply types are represented using the following Go types: 54 | // 55 | // Redis type Go type 56 | // error redis.Error 57 | // integer int64 58 | // simple string string 59 | // bulk string []byte or nil if value not present. 60 | // array []interface{} or nil if value not present. 61 | // 62 | // Use type assertions or the reply helper functions to convert from 63 | // interface{} to the specific Go type for the command result. 64 | // 65 | // Pipelining 66 | // 67 | // Connections support pipelining using the Send, Flush and Receive methods. 68 | // 69 | // Send(commandName string, args ...interface{}) error 70 | // Flush() error 71 | // Receive() (reply interface{}, err error) 72 | // 73 | // Send writes the command to the connection's output buffer. Flush flushes the 74 | // connection's output buffer to the server. Receive reads a single reply from 75 | // the server. The following example shows a simple pipeline. 76 | // 77 | // c.Send("SET", "foo", "bar") 78 | // c.Send("GET", "foo") 79 | // c.Flush() 80 | // c.Receive() // reply from SET 81 | // v, err = c.Receive() // reply from GET 82 | // 83 | // The Do method combines the functionality of the Send, Flush and Receive 84 | // methods. The Do method starts by writing the command and flushing the output 85 | // buffer. Next, the Do method receives all pending replies including the reply 86 | // for the command just sent by Do. If any of the received replies is an error, 87 | // then Do returns the error. If there are no errors, then Do returns the last 88 | // reply. If the command argument to the Do method is "", then the Do method 89 | // will flush the output buffer and receive pending replies without sending a 90 | // command. 91 | // 92 | // Use the Send and Do methods to implement pipelined transactions. 93 | // 94 | // c.Send("MULTI") 95 | // c.Send("INCR", "foo") 96 | // c.Send("INCR", "bar") 97 | // r, err := c.Do("EXEC") 98 | // fmt.Println(r) // prints [1, 1] 99 | // 100 | // Concurrency 101 | // 102 | // Connections support one concurrent caller to the Receive method and one 103 | // concurrent caller to the Send and Flush methods. No other concurrency is 104 | // supported including concurrent calls to the Do method. 105 | // 106 | // For full concurrent access to Redis, use the thread-safe Pool to get, use 107 | // and release a connection from within a goroutine. Connections returned from 108 | // a Pool have the concurrency restrictions described in the previous 109 | // paragraph. 110 | // 111 | // Publish and Subscribe 112 | // 113 | // Use the Send, Flush and Receive methods to implement Pub/Sub subscribers. 114 | // 115 | // c.Send("SUBSCRIBE", "example") 116 | // c.Flush() 117 | // for { 118 | // reply, err := c.Receive() 119 | // if err != nil { 120 | // return err 121 | // } 122 | // // process pushed message 123 | // } 124 | // 125 | // The PubSubConn type wraps a Conn with convenience methods for implementing 126 | // subscribers. The Subscribe, PSubscribe, Unsubscribe and PUnsubscribe methods 127 | // send and flush a subscription management command. The receive method 128 | // converts a pushed message to convenient types for use in a type switch. 129 | // 130 | // psc := redis.PubSubConn{Conn: c} 131 | // psc.Subscribe("example") 132 | // for { 133 | // switch v := psc.Receive().(type) { 134 | // case redis.Message: 135 | // fmt.Printf("%s: message: %s\n", v.Channel, v.Data) 136 | // case redis.Subscription: 137 | // fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count) 138 | // case error: 139 | // return v 140 | // } 141 | // } 142 | // 143 | // Reply Helpers 144 | // 145 | // The Bool, Int, Bytes, String, Strings and Values functions convert a reply 146 | // to a value of a specific type. To allow convenient wrapping of calls to the 147 | // connection Do and Receive methods, the functions take a second argument of 148 | // type error. If the error is non-nil, then the helper function returns the 149 | // error. If the error is nil, the function converts the reply to the specified 150 | // type: 151 | // 152 | // exists, err := redis.Bool(c.Do("EXISTS", "foo")) 153 | // if err != nil { 154 | // // handle error return from c.Do or type conversion error. 155 | // } 156 | // 157 | // The Scan function converts elements of a array reply to Go types: 158 | // 159 | // var value1 int 160 | // var value2 string 161 | // reply, err := redis.Values(c.Do("MGET", "key1", "key2")) 162 | // if err != nil { 163 | // // handle error 164 | // } 165 | // if _, err := redis.Scan(reply, &value1, &value2); err != nil { 166 | // // handle error 167 | // } 168 | // 169 | // Errors 170 | // 171 | // Connection methods return error replies from the server as type redis.Error. 172 | // 173 | // Call the connection Err() method to determine if the connection encountered 174 | // non-recoverable error such as a network error or protocol parsing error. If 175 | // Err() returns a non-nil value, then the connection is not usable and should 176 | // be closed. 177 | package redis // import "github.com/garyburd/redigo/redis" 178 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/go16.go: -------------------------------------------------------------------------------- 1 | // +build !go1.7 2 | 3 | package redis 4 | 5 | import "crypto/tls" 6 | 7 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 8 | return &tls.Config{ 9 | Rand: cfg.Rand, 10 | Time: cfg.Time, 11 | Certificates: cfg.Certificates, 12 | NameToCertificate: cfg.NameToCertificate, 13 | GetCertificate: cfg.GetCertificate, 14 | RootCAs: cfg.RootCAs, 15 | NextProtos: cfg.NextProtos, 16 | ServerName: cfg.ServerName, 17 | ClientAuth: cfg.ClientAuth, 18 | ClientCAs: cfg.ClientCAs, 19 | InsecureSkipVerify: cfg.InsecureSkipVerify, 20 | CipherSuites: cfg.CipherSuites, 21 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 22 | ClientSessionCache: cfg.ClientSessionCache, 23 | MinVersion: cfg.MinVersion, 24 | MaxVersion: cfg.MaxVersion, 25 | CurvePreferences: cfg.CurvePreferences, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/go17.go: -------------------------------------------------------------------------------- 1 | // +build go1.7,!go1.8 2 | 3 | package redis 4 | 5 | import "crypto/tls" 6 | 7 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 8 | return &tls.Config{ 9 | Rand: cfg.Rand, 10 | Time: cfg.Time, 11 | Certificates: cfg.Certificates, 12 | NameToCertificate: cfg.NameToCertificate, 13 | GetCertificate: cfg.GetCertificate, 14 | RootCAs: cfg.RootCAs, 15 | NextProtos: cfg.NextProtos, 16 | ServerName: cfg.ServerName, 17 | ClientAuth: cfg.ClientAuth, 18 | ClientCAs: cfg.ClientCAs, 19 | InsecureSkipVerify: cfg.InsecureSkipVerify, 20 | CipherSuites: cfg.CipherSuites, 21 | PreferServerCipherSuites: cfg.PreferServerCipherSuites, 22 | ClientSessionCache: cfg.ClientSessionCache, 23 | MinVersion: cfg.MinVersion, 24 | MaxVersion: cfg.MaxVersion, 25 | CurvePreferences: cfg.CurvePreferences, 26 | DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled, 27 | Renegotiation: cfg.Renegotiation, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/go18.go: -------------------------------------------------------------------------------- 1 | // +build go1.8 2 | 3 | package redis 4 | 5 | import "crypto/tls" 6 | 7 | func cloneTLSConfig(cfg *tls.Config) *tls.Config { 8 | return cfg.Clone() 9 | } 10 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "log" 21 | "time" 22 | ) 23 | 24 | var ( 25 | _ ConnWithTimeout = (*loggingConn)(nil) 26 | ) 27 | 28 | // NewLoggingConn returns a logging wrapper around a connection. 29 | func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn { 30 | if prefix != "" { 31 | prefix = prefix + "." 32 | } 33 | return &loggingConn{conn, logger, prefix} 34 | } 35 | 36 | type loggingConn struct { 37 | Conn 38 | logger *log.Logger 39 | prefix string 40 | } 41 | 42 | func (c *loggingConn) Close() error { 43 | err := c.Conn.Close() 44 | var buf bytes.Buffer 45 | fmt.Fprintf(&buf, "%sClose() -> (%v)", c.prefix, err) 46 | c.logger.Output(2, buf.String()) 47 | return err 48 | } 49 | 50 | func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) { 51 | const chop = 32 52 | switch v := v.(type) { 53 | case []byte: 54 | if len(v) > chop { 55 | fmt.Fprintf(buf, "%q...", v[:chop]) 56 | } else { 57 | fmt.Fprintf(buf, "%q", v) 58 | } 59 | case string: 60 | if len(v) > chop { 61 | fmt.Fprintf(buf, "%q...", v[:chop]) 62 | } else { 63 | fmt.Fprintf(buf, "%q", v) 64 | } 65 | case []interface{}: 66 | if len(v) == 0 { 67 | buf.WriteString("[]") 68 | } else { 69 | sep := "[" 70 | fin := "]" 71 | if len(v) > chop { 72 | v = v[:chop] 73 | fin = "...]" 74 | } 75 | for _, vv := range v { 76 | buf.WriteString(sep) 77 | c.printValue(buf, vv) 78 | sep = ", " 79 | } 80 | buf.WriteString(fin) 81 | } 82 | default: 83 | fmt.Fprint(buf, v) 84 | } 85 | } 86 | 87 | func (c *loggingConn) print(method, commandName string, args []interface{}, reply interface{}, err error) { 88 | var buf bytes.Buffer 89 | fmt.Fprintf(&buf, "%s%s(", c.prefix, method) 90 | if method != "Receive" { 91 | buf.WriteString(commandName) 92 | for _, arg := range args { 93 | buf.WriteString(", ") 94 | c.printValue(&buf, arg) 95 | } 96 | } 97 | buf.WriteString(") -> (") 98 | if method != "Send" { 99 | c.printValue(&buf, reply) 100 | buf.WriteString(", ") 101 | } 102 | fmt.Fprintf(&buf, "%v)", err) 103 | c.logger.Output(3, buf.String()) 104 | } 105 | 106 | func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, error) { 107 | reply, err := c.Conn.Do(commandName, args...) 108 | c.print("Do", commandName, args, reply, err) 109 | return reply, err 110 | } 111 | 112 | func (c *loggingConn) DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (interface{}, error) { 113 | reply, err := DoWithTimeout(c.Conn, timeout, commandName, args...) 114 | c.print("DoWithTimeout", commandName, args, reply, err) 115 | return reply, err 116 | } 117 | 118 | func (c *loggingConn) Send(commandName string, args ...interface{}) error { 119 | err := c.Conn.Send(commandName, args...) 120 | c.print("Send", commandName, args, nil, err) 121 | return err 122 | } 123 | 124 | func (c *loggingConn) Receive() (interface{}, error) { 125 | reply, err := c.Conn.Receive() 126 | c.print("Receive", "", nil, reply, err) 127 | return reply, err 128 | } 129 | 130 | func (c *loggingConn) ReceiveWithTimeout(timeout time.Duration) (interface{}, error) { 131 | reply, err := ReceiveWithTimeout(c.Conn, timeout) 132 | c.print("ReceiveWithTimeout", "", nil, reply, err) 133 | return reply, err 134 | } 135 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pubsub.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "errors" 19 | "time" 20 | ) 21 | 22 | // Subscription represents a subscribe or unsubscribe notification. 23 | type Subscription struct { 24 | // Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe" 25 | Kind string 26 | 27 | // The channel that was changed. 28 | Channel string 29 | 30 | // The current number of subscriptions for connection. 31 | Count int 32 | } 33 | 34 | // Message represents a message notification. 35 | type Message struct { 36 | // The originating channel. 37 | Channel string 38 | 39 | // The message data. 40 | Data []byte 41 | } 42 | 43 | // PMessage represents a pmessage notification. 44 | type PMessage struct { 45 | // The matched pattern. 46 | Pattern string 47 | 48 | // The originating channel. 49 | Channel string 50 | 51 | // The message data. 52 | Data []byte 53 | } 54 | 55 | // Pong represents a pubsub pong notification. 56 | type Pong struct { 57 | Data string 58 | } 59 | 60 | // PubSubConn wraps a Conn with convenience methods for subscribers. 61 | type PubSubConn struct { 62 | Conn Conn 63 | } 64 | 65 | // Close closes the connection. 66 | func (c PubSubConn) Close() error { 67 | return c.Conn.Close() 68 | } 69 | 70 | // Subscribe subscribes the connection to the specified channels. 71 | func (c PubSubConn) Subscribe(channel ...interface{}) error { 72 | c.Conn.Send("SUBSCRIBE", channel...) 73 | return c.Conn.Flush() 74 | } 75 | 76 | // PSubscribe subscribes the connection to the given patterns. 77 | func (c PubSubConn) PSubscribe(channel ...interface{}) error { 78 | c.Conn.Send("PSUBSCRIBE", channel...) 79 | return c.Conn.Flush() 80 | } 81 | 82 | // Unsubscribe unsubscribes the connection from the given channels, or from all 83 | // of them if none is given. 84 | func (c PubSubConn) Unsubscribe(channel ...interface{}) error { 85 | c.Conn.Send("UNSUBSCRIBE", channel...) 86 | return c.Conn.Flush() 87 | } 88 | 89 | // PUnsubscribe unsubscribes the connection from the given patterns, or from all 90 | // of them if none is given. 91 | func (c PubSubConn) PUnsubscribe(channel ...interface{}) error { 92 | c.Conn.Send("PUNSUBSCRIBE", channel...) 93 | return c.Conn.Flush() 94 | } 95 | 96 | // Ping sends a PING to the server with the specified data. 97 | // 98 | // The connection must be subscribed to at least one channel or pattern when 99 | // calling this method. 100 | func (c PubSubConn) Ping(data string) error { 101 | c.Conn.Send("PING", data) 102 | return c.Conn.Flush() 103 | } 104 | 105 | // Receive returns a pushed message as a Subscription, Message, PMessage, Pong 106 | // or error. The return value is intended to be used directly in a type switch 107 | // as illustrated in the PubSubConn example. 108 | func (c PubSubConn) Receive() interface{} { 109 | return c.receiveInternal(c.Conn.Receive()) 110 | } 111 | 112 | // ReceiveWithTimeout is like Receive, but it allows the application to 113 | // override the connection's default timeout. 114 | func (c PubSubConn) ReceiveWithTimeout(timeout time.Duration) interface{} { 115 | return c.receiveInternal(ReceiveWithTimeout(c.Conn, timeout)) 116 | } 117 | 118 | func (c PubSubConn) receiveInternal(replyArg interface{}, errArg error) interface{} { 119 | reply, err := Values(replyArg, errArg) 120 | if err != nil { 121 | return err 122 | } 123 | 124 | var kind string 125 | reply, err = Scan(reply, &kind) 126 | if err != nil { 127 | return err 128 | } 129 | 130 | switch kind { 131 | case "message": 132 | var m Message 133 | if _, err := Scan(reply, &m.Channel, &m.Data); err != nil { 134 | return err 135 | } 136 | return m 137 | case "pmessage": 138 | var pm PMessage 139 | if _, err := Scan(reply, &pm.Pattern, &pm.Channel, &pm.Data); err != nil { 140 | return err 141 | } 142 | return pm 143 | case "subscribe", "psubscribe", "unsubscribe", "punsubscribe": 144 | s := Subscription{Kind: kind} 145 | if _, err := Scan(reply, &s.Channel, &s.Count); err != nil { 146 | return err 147 | } 148 | return s 149 | case "pong": 150 | var p Pong 151 | if _, err := Scan(reply, &p.Data); err != nil { 152 | return err 153 | } 154 | return p 155 | } 156 | return errors.New("redigo: unknown pubsub notification") 157 | } 158 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pubsub_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | // +build go1.7 16 | 17 | package redis_test 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "time" 23 | 24 | "github.com/garyburd/redigo/redis" 25 | ) 26 | 27 | // listenPubSubChannels listens for messages on Redis pubsub channels. The 28 | // onStart function is called after the channels are subscribed. The onMessage 29 | // function is called for each message. 30 | func listenPubSubChannels(ctx context.Context, redisServerAddr string, 31 | onStart func() error, 32 | onMessage func(channel string, data []byte) error, 33 | channels ...string) error { 34 | // A ping is set to the server with this period to test for the health of 35 | // the connection and server. 36 | const healthCheckPeriod = time.Minute 37 | 38 | c, err := redis.Dial("tcp", redisServerAddr, 39 | // Read timeout on server should be greater than ping period. 40 | redis.DialReadTimeout(healthCheckPeriod+10*time.Second), 41 | redis.DialWriteTimeout(10*time.Second)) 42 | if err != nil { 43 | return err 44 | } 45 | defer c.Close() 46 | 47 | psc := redis.PubSubConn{Conn: c} 48 | 49 | if err := psc.Subscribe(redis.Args{}.AddFlat(channels)...); err != nil { 50 | return err 51 | } 52 | 53 | done := make(chan error, 1) 54 | 55 | // Start a goroutine to receive notifications from the server. 56 | go func() { 57 | for { 58 | switch n := psc.Receive().(type) { 59 | case error: 60 | done <- n 61 | return 62 | case redis.Message: 63 | if err := onMessage(n.Channel, n.Data); err != nil { 64 | done <- err 65 | return 66 | } 67 | case redis.Subscription: 68 | switch n.Count { 69 | case len(channels): 70 | // Notify application when all channels are subscribed. 71 | if err := onStart(); err != nil { 72 | done <- err 73 | return 74 | } 75 | case 0: 76 | // Return from the goroutine when all channels are unsubscribed. 77 | done <- nil 78 | return 79 | } 80 | } 81 | } 82 | }() 83 | 84 | ticker := time.NewTicker(healthCheckPeriod) 85 | defer ticker.Stop() 86 | loop: 87 | for err == nil { 88 | select { 89 | case <-ticker.C: 90 | // Send ping to test health of connection and server. If 91 | // corresponding pong is not received, then receive on the 92 | // connection will timeout and the receive goroutine will exit. 93 | if err = psc.Ping(""); err != nil { 94 | break loop 95 | } 96 | case <-ctx.Done(): 97 | break loop 98 | case err := <-done: 99 | // Return error from the receive goroutine. 100 | return err 101 | } 102 | } 103 | 104 | // Signal the receiving goroutine to exit by unsubscribing from all channels. 105 | psc.Unsubscribe() 106 | 107 | // Wait for goroutine to complete. 108 | return <-done 109 | } 110 | 111 | func publish() { 112 | c, err := dial() 113 | if err != nil { 114 | fmt.Println(err) 115 | return 116 | } 117 | defer c.Close() 118 | 119 | c.Do("PUBLISH", "c1", "hello") 120 | c.Do("PUBLISH", "c2", "world") 121 | c.Do("PUBLISH", "c1", "goodbye") 122 | } 123 | 124 | // This example shows how receive pubsub notifications with cancelation and 125 | // health checks. 126 | func ExamplePubSubConn() { 127 | redisServerAddr, err := serverAddr() 128 | if err != nil { 129 | fmt.Println(err) 130 | return 131 | } 132 | 133 | ctx, cancel := context.WithCancel(context.Background()) 134 | 135 | err = listenPubSubChannels(ctx, 136 | redisServerAddr, 137 | func() error { 138 | // The start callback is a good place to backfill missed 139 | // notifications. For the purpose of this example, a goroutine is 140 | // started to send notifications. 141 | go publish() 142 | return nil 143 | }, 144 | func(channel string, message []byte) error { 145 | fmt.Printf("channel: %s, message: %s\n", channel, message) 146 | 147 | // For the purpose of this example, cancel the listener's context 148 | // after receiving last message sent by publish(). 149 | if string(message) == "goodbye" { 150 | cancel() 151 | } 152 | return nil 153 | }, 154 | "c1", "c2") 155 | 156 | if err != nil { 157 | fmt.Println(err) 158 | return 159 | } 160 | 161 | // Output: 162 | // channel: c1, message: hello 163 | // channel: c2, message: world 164 | // channel: c1, message: goodbye 165 | } 166 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/pubsub_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "reflect" 19 | "testing" 20 | "time" 21 | 22 | "github.com/garyburd/redigo/redis" 23 | ) 24 | 25 | func expectPushed(t *testing.T, c redis.PubSubConn, message string, expected interface{}) { 26 | actual := c.Receive() 27 | if !reflect.DeepEqual(actual, expected) { 28 | t.Errorf("%s = %v, want %v", message, actual, expected) 29 | } 30 | } 31 | 32 | func TestPushed(t *testing.T) { 33 | pc, err := redis.DialDefaultServer() 34 | if err != nil { 35 | t.Fatalf("error connection to database, %v", err) 36 | } 37 | defer pc.Close() 38 | 39 | sc, err := redis.DialDefaultServer() 40 | if err != nil { 41 | t.Fatalf("error connection to database, %v", err) 42 | } 43 | defer sc.Close() 44 | 45 | c := redis.PubSubConn{Conn: sc} 46 | 47 | c.Subscribe("c1") 48 | expectPushed(t, c, "Subscribe(c1)", redis.Subscription{Kind: "subscribe", Channel: "c1", Count: 1}) 49 | c.Subscribe("c2") 50 | expectPushed(t, c, "Subscribe(c2)", redis.Subscription{Kind: "subscribe", Channel: "c2", Count: 2}) 51 | c.PSubscribe("p1") 52 | expectPushed(t, c, "PSubscribe(p1)", redis.Subscription{Kind: "psubscribe", Channel: "p1", Count: 3}) 53 | c.PSubscribe("p2") 54 | expectPushed(t, c, "PSubscribe(p2)", redis.Subscription{Kind: "psubscribe", Channel: "p2", Count: 4}) 55 | c.PUnsubscribe() 56 | expectPushed(t, c, "Punsubscribe(p1)", redis.Subscription{Kind: "punsubscribe", Channel: "p1", Count: 3}) 57 | expectPushed(t, c, "Punsubscribe()", redis.Subscription{Kind: "punsubscribe", Channel: "p2", Count: 2}) 58 | 59 | pc.Do("PUBLISH", "c1", "hello") 60 | expectPushed(t, c, "PUBLISH c1 hello", redis.Message{Channel: "c1", Data: []byte("hello")}) 61 | 62 | c.Ping("hello") 63 | expectPushed(t, c, `Ping("hello")`, redis.Pong{Data: "hello"}) 64 | 65 | c.Conn.Send("PING") 66 | c.Conn.Flush() 67 | expectPushed(t, c, `Send("PING")`, redis.Pong{}) 68 | 69 | c.Ping("timeout") 70 | got := c.ReceiveWithTimeout(time.Minute) 71 | if want := (redis.Pong{Data: "timeout"}); want != got { 72 | t.Errorf("recv /w timeout got %v, want %v", got, want) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/redis.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "errors" 19 | "time" 20 | ) 21 | 22 | // Error represents an error returned in a command reply. 23 | type Error string 24 | 25 | func (err Error) Error() string { return string(err) } 26 | 27 | // Conn represents a connection to a Redis server. 28 | type Conn interface { 29 | // Close closes the connection. 30 | Close() error 31 | 32 | // Err returns a non-nil value when the connection is not usable. 33 | Err() error 34 | 35 | // Do sends a command to the server and returns the received reply. 36 | Do(commandName string, args ...interface{}) (reply interface{}, err error) 37 | 38 | // Send writes the command to the client's output buffer. 39 | Send(commandName string, args ...interface{}) error 40 | 41 | // Flush flushes the output buffer to the Redis server. 42 | Flush() error 43 | 44 | // Receive receives a single reply from the Redis server 45 | Receive() (reply interface{}, err error) 46 | } 47 | 48 | // Argument is the interface implemented by an object which wants to control how 49 | // the object is converted to Redis bulk strings. 50 | type Argument interface { 51 | // RedisArg returns a value to be encoded as a bulk string per the 52 | // conversions listed in the section 'Executing Commands'. 53 | // Implementations should typically return a []byte or string. 54 | RedisArg() interface{} 55 | } 56 | 57 | // Scanner is implemented by an object which wants to control its value is 58 | // interpreted when read from Redis. 59 | type Scanner interface { 60 | // RedisScan assigns a value from a Redis value. The argument src is one of 61 | // the reply types listed in the section `Executing Commands`. 62 | // 63 | // An error should be returned if the value cannot be stored without 64 | // loss of information. 65 | RedisScan(src interface{}) error 66 | } 67 | 68 | // ConnWithTimeout is an optional interface that allows the caller to override 69 | // a connection's default read timeout. This interface is useful for executing 70 | // the BLPOP, BRPOP, BRPOPLPUSH, XREAD and other commands that block at the 71 | // server. 72 | // 73 | // A connection's default read timeout is set with the DialReadTimeout dial 74 | // option. Applications should rely on the default timeout for commands that do 75 | // not block at the server. 76 | // 77 | // All of the Conn implementations in this package satisfy the ConnWithTimeout 78 | // interface. 79 | // 80 | // Use the DoWithTimeout and ReceiveWithTimeout helper functions to simplify 81 | // use of this interface. 82 | type ConnWithTimeout interface { 83 | Conn 84 | 85 | // Do sends a command to the server and returns the received reply. 86 | // The timeout overrides the read timeout set when dialing the 87 | // connection. 88 | DoWithTimeout(timeout time.Duration, commandName string, args ...interface{}) (reply interface{}, err error) 89 | 90 | // Receive receives a single reply from the Redis server. The timeout 91 | // overrides the read timeout set when dialing the connection. 92 | ReceiveWithTimeout(timeout time.Duration) (reply interface{}, err error) 93 | } 94 | 95 | var errTimeoutNotSupported = errors.New("redis: connection does not support ConnWithTimeout") 96 | 97 | // DoWithTimeout executes a Redis command with the specified read timeout. If 98 | // the connection does not satisfy the ConnWithTimeout interface, then an error 99 | // is returned. 100 | func DoWithTimeout(c Conn, timeout time.Duration, cmd string, args ...interface{}) (interface{}, error) { 101 | cwt, ok := c.(ConnWithTimeout) 102 | if !ok { 103 | return nil, errTimeoutNotSupported 104 | } 105 | return cwt.DoWithTimeout(timeout, cmd, args...) 106 | } 107 | 108 | // ReceiveWithTimeout receives a reply with the specified read timeout. If the 109 | // connection does not satisfy the ConnWithTimeout interface, then an error is 110 | // returned. 111 | func ReceiveWithTimeout(c Conn, timeout time.Duration) (interface{}, error) { 112 | cwt, ok := c.(ConnWithTimeout) 113 | if !ok { 114 | return nil, errTimeoutNotSupported 115 | } 116 | return cwt.ReceiveWithTimeout(timeout) 117 | } 118 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/redis_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | 21 | "github.com/garyburd/redigo/redis" 22 | ) 23 | 24 | type timeoutTestConn int 25 | 26 | func (tc timeoutTestConn) Do(string, ...interface{}) (interface{}, error) { 27 | return time.Duration(-1), nil 28 | } 29 | func (tc timeoutTestConn) DoWithTimeout(timeout time.Duration, cmd string, args ...interface{}) (interface{}, error) { 30 | return timeout, nil 31 | } 32 | 33 | func (tc timeoutTestConn) Receive() (interface{}, error) { 34 | return time.Duration(-1), nil 35 | } 36 | func (tc timeoutTestConn) ReceiveWithTimeout(timeout time.Duration) (interface{}, error) { 37 | return timeout, nil 38 | } 39 | 40 | func (tc timeoutTestConn) Send(string, ...interface{}) error { return nil } 41 | func (tc timeoutTestConn) Err() error { return nil } 42 | func (tc timeoutTestConn) Close() error { return nil } 43 | func (tc timeoutTestConn) Flush() error { return nil } 44 | 45 | func testTimeout(t *testing.T, c redis.Conn) { 46 | r, err := c.Do("PING") 47 | if r != time.Duration(-1) || err != nil { 48 | t.Errorf("Do() = %v, %v, want %v, %v", r, err, time.Duration(-1), nil) 49 | } 50 | r, err = redis.DoWithTimeout(c, time.Minute, "PING") 51 | if r != time.Minute || err != nil { 52 | t.Errorf("DoWithTimeout() = %v, %v, want %v, %v", r, err, time.Minute, nil) 53 | } 54 | r, err = c.Receive() 55 | if r != time.Duration(-1) || err != nil { 56 | t.Errorf("Receive() = %v, %v, want %v, %v", r, err, time.Duration(-1), nil) 57 | } 58 | r, err = redis.ReceiveWithTimeout(c, time.Minute) 59 | if r != time.Minute || err != nil { 60 | t.Errorf("ReceiveWithTimeout() = %v, %v, want %v, %v", r, err, time.Minute, nil) 61 | } 62 | } 63 | 64 | func TestConnTimeout(t *testing.T) { 65 | testTimeout(t, timeoutTestConn(0)) 66 | } 67 | 68 | func TestPoolConnTimeout(t *testing.T) { 69 | p := &redis.Pool{Dial: func() (redis.Conn, error) { return timeoutTestConn(0), nil }} 70 | testTimeout(t, p.Get()) 71 | } 72 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/reply_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "testing" 21 | 22 | "github.com/garyburd/redigo/redis" 23 | ) 24 | 25 | type valueError struct { 26 | v interface{} 27 | err error 28 | } 29 | 30 | func ve(v interface{}, err error) valueError { 31 | return valueError{v, err} 32 | } 33 | 34 | var replyTests = []struct { 35 | name interface{} 36 | actual valueError 37 | expected valueError 38 | }{ 39 | { 40 | "ints([[]byte, []byte])", 41 | ve(redis.Ints([]interface{}{[]byte("4"), []byte("5")}, nil)), 42 | ve([]int{4, 5}, nil), 43 | }, 44 | { 45 | "ints([nt64, int64])", 46 | ve(redis.Ints([]interface{}{int64(4), int64(5)}, nil)), 47 | ve([]int{4, 5}, nil), 48 | }, 49 | { 50 | "ints([[]byte, nil, []byte])", 51 | ve(redis.Ints([]interface{}{[]byte("4"), nil, []byte("5")}, nil)), 52 | ve([]int{4, 0, 5}, nil), 53 | }, 54 | { 55 | "ints(nil)", 56 | ve(redis.Ints(nil, nil)), 57 | ve([]int(nil), redis.ErrNil), 58 | }, 59 | { 60 | "int64s([[]byte, []byte])", 61 | ve(redis.Int64s([]interface{}{[]byte("4"), []byte("5")}, nil)), 62 | ve([]int64{4, 5}, nil), 63 | }, 64 | { 65 | "int64s([int64, int64])", 66 | ve(redis.Int64s([]interface{}{int64(4), int64(5)}, nil)), 67 | ve([]int64{4, 5}, nil), 68 | }, 69 | { 70 | "strings([[]byte, []bytev2])", 71 | ve(redis.Strings([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 72 | ve([]string{"v1", "v2"}, nil), 73 | }, 74 | { 75 | "strings([string, string])", 76 | ve(redis.Strings([]interface{}{"v1", "v2"}, nil)), 77 | ve([]string{"v1", "v2"}, nil), 78 | }, 79 | { 80 | "byteslices([v1, v2])", 81 | ve(redis.ByteSlices([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 82 | ve([][]byte{[]byte("v1"), []byte("v2")}, nil), 83 | }, 84 | { 85 | "float64s([v1, v2])", 86 | ve(redis.Float64s([]interface{}{[]byte("1.234"), []byte("5.678")}, nil)), 87 | ve([]float64{1.234, 5.678}, nil), 88 | }, 89 | { 90 | "values([v1, v2])", 91 | ve(redis.Values([]interface{}{[]byte("v1"), []byte("v2")}, nil)), 92 | ve([]interface{}{[]byte("v1"), []byte("v2")}, nil), 93 | }, 94 | { 95 | "values(nil)", 96 | ve(redis.Values(nil, nil)), 97 | ve([]interface{}(nil), redis.ErrNil), 98 | }, 99 | { 100 | "float64(1.0)", 101 | ve(redis.Float64([]byte("1.0"), nil)), 102 | ve(float64(1.0), nil), 103 | }, 104 | { 105 | "float64(nil)", 106 | ve(redis.Float64(nil, nil)), 107 | ve(float64(0.0), redis.ErrNil), 108 | }, 109 | { 110 | "uint64(1)", 111 | ve(redis.Uint64(int64(1), nil)), 112 | ve(uint64(1), nil), 113 | }, 114 | { 115 | "uint64(-1)", 116 | ve(redis.Uint64(int64(-1), nil)), 117 | ve(uint64(0), redis.ErrNegativeInt), 118 | }, 119 | { 120 | "positions([[1, 2], nil, [3, 4]])", 121 | ve(redis.Positions([]interface{}{[]interface{}{[]byte("1"), []byte("2")}, nil, []interface{}{[]byte("3"), []byte("4")}}, nil)), 122 | ve([]*[2]float64{{1.0, 2.0}, nil, {3.0, 4.0}}, nil), 123 | }, 124 | } 125 | 126 | func TestReply(t *testing.T) { 127 | for _, rt := range replyTests { 128 | if rt.actual.err != rt.expected.err { 129 | t.Errorf("%s returned err %v, want %v", rt.name, rt.actual.err, rt.expected.err) 130 | continue 131 | } 132 | if !reflect.DeepEqual(rt.actual.v, rt.expected.v) { 133 | t.Errorf("%s=%+v, want %+v", rt.name, rt.actual.v, rt.expected.v) 134 | } 135 | } 136 | } 137 | 138 | // dial wraps DialDefaultServer() with a more suitable function name for examples. 139 | func dial() (redis.Conn, error) { 140 | return redis.DialDefaultServer() 141 | } 142 | 143 | // serverAddr wraps DefaultServerAddr() with a more suitable function name for examples. 144 | func serverAddr() (string, error) { 145 | return redis.DefaultServerAddr() 146 | } 147 | 148 | func ExampleBool() { 149 | c, err := dial() 150 | if err != nil { 151 | fmt.Println(err) 152 | return 153 | } 154 | defer c.Close() 155 | 156 | c.Do("SET", "foo", 1) 157 | exists, _ := redis.Bool(c.Do("EXISTS", "foo")) 158 | fmt.Printf("%#v\n", exists) 159 | // Output: 160 | // true 161 | } 162 | 163 | func ExampleInt() { 164 | c, err := dial() 165 | if err != nil { 166 | fmt.Println(err) 167 | return 168 | } 169 | defer c.Close() 170 | 171 | c.Do("SET", "k1", 1) 172 | n, _ := redis.Int(c.Do("GET", "k1")) 173 | fmt.Printf("%#v\n", n) 174 | n, _ = redis.Int(c.Do("INCR", "k1")) 175 | fmt.Printf("%#v\n", n) 176 | // Output: 177 | // 1 178 | // 2 179 | } 180 | 181 | func ExampleInts() { 182 | c, err := dial() 183 | if err != nil { 184 | fmt.Println(err) 185 | return 186 | } 187 | defer c.Close() 188 | 189 | c.Do("SADD", "set_with_integers", 4, 5, 6) 190 | ints, _ := redis.Ints(c.Do("SMEMBERS", "set_with_integers")) 191 | fmt.Printf("%#v\n", ints) 192 | // Output: 193 | // []int{4, 5, 6} 194 | } 195 | 196 | func ExampleString() { 197 | c, err := dial() 198 | if err != nil { 199 | fmt.Println(err) 200 | return 201 | } 202 | defer c.Close() 203 | 204 | c.Do("SET", "hello", "world") 205 | s, err := redis.String(c.Do("GET", "hello")) 206 | fmt.Printf("%#v\n", s) 207 | // Output: 208 | // "world" 209 | } 210 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/script.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "crypto/sha1" 19 | "encoding/hex" 20 | "io" 21 | "strings" 22 | ) 23 | 24 | // Script encapsulates the source, hash and key count for a Lua script. See 25 | // http://redis.io/commands/eval for information on scripts in Redis. 26 | type Script struct { 27 | keyCount int 28 | src string 29 | hash string 30 | } 31 | 32 | // NewScript returns a new script object. If keyCount is greater than or equal 33 | // to zero, then the count is automatically inserted in the EVAL command 34 | // argument list. If keyCount is less than zero, then the application supplies 35 | // the count as the first value in the keysAndArgs argument to the Do, Send and 36 | // SendHash methods. 37 | func NewScript(keyCount int, src string) *Script { 38 | h := sha1.New() 39 | io.WriteString(h, src) 40 | return &Script{keyCount, src, hex.EncodeToString(h.Sum(nil))} 41 | } 42 | 43 | func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} { 44 | var args []interface{} 45 | if s.keyCount < 0 { 46 | args = make([]interface{}, 1+len(keysAndArgs)) 47 | args[0] = spec 48 | copy(args[1:], keysAndArgs) 49 | } else { 50 | args = make([]interface{}, 2+len(keysAndArgs)) 51 | args[0] = spec 52 | args[1] = s.keyCount 53 | copy(args[2:], keysAndArgs) 54 | } 55 | return args 56 | } 57 | 58 | // Hash returns the script hash. 59 | func (s *Script) Hash() string { 60 | return s.hash 61 | } 62 | 63 | // Do evaluates the script. Under the covers, Do optimistically evaluates the 64 | // script using the EVALSHA command. If the command fails because the script is 65 | // not loaded, then Do evaluates the script using the EVAL command (thus 66 | // causing the script to load). 67 | func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, error) { 68 | v, err := c.Do("EVALSHA", s.args(s.hash, keysAndArgs)...) 69 | if e, ok := err.(Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") { 70 | v, err = c.Do("EVAL", s.args(s.src, keysAndArgs)...) 71 | } 72 | return v, err 73 | } 74 | 75 | // SendHash evaluates the script without waiting for the reply. The script is 76 | // evaluated with the EVALSHA command. The application must ensure that the 77 | // script is loaded by a previous call to Send, Do or Load methods. 78 | func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error { 79 | return c.Send("EVALSHA", s.args(s.hash, keysAndArgs)...) 80 | } 81 | 82 | // Send evaluates the script without waiting for the reply. 83 | func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error { 84 | return c.Send("EVAL", s.args(s.src, keysAndArgs)...) 85 | } 86 | 87 | // Load loads the script without evaluating it. 88 | func (s *Script) Load(c Conn) error { 89 | _, err := c.Do("SCRIPT", "LOAD", s.src) 90 | return err 91 | } 92 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/script_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | "reflect" 20 | "testing" 21 | "time" 22 | 23 | "github.com/garyburd/redigo/redis" 24 | ) 25 | 26 | var ( 27 | // These variables are declared at package level to remove distracting 28 | // details from the examples. 29 | c redis.Conn 30 | reply interface{} 31 | err error 32 | ) 33 | 34 | func ExampleScript() { 35 | // Initialize a package-level variable with a script. 36 | var getScript = redis.NewScript(1, `return redis.call('get', KEYS[1])`) 37 | 38 | // In a function, use the script Do method to evaluate the script. The Do 39 | // method optimistically uses the EVALSHA command. If the script is not 40 | // loaded, then the Do method falls back to the EVAL command. 41 | reply, err = getScript.Do(c, "foo") 42 | } 43 | 44 | func TestScript(t *testing.T) { 45 | c, err := redis.DialDefaultServer() 46 | if err != nil { 47 | t.Fatalf("error connection to database, %v", err) 48 | } 49 | defer c.Close() 50 | 51 | // To test fall back in Do, we make script unique by adding comment with current time. 52 | script := fmt.Sprintf("--%d\nreturn {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", time.Now().UnixNano()) 53 | s := redis.NewScript(2, script) 54 | reply := []interface{}{[]byte("key1"), []byte("key2"), []byte("arg1"), []byte("arg2")} 55 | 56 | v, err := s.Do(c, "key1", "key2", "arg1", "arg2") 57 | if err != nil { 58 | t.Errorf("s.Do(c, ...) returned %v", err) 59 | } 60 | 61 | if !reflect.DeepEqual(v, reply) { 62 | t.Errorf("s.Do(c, ..); = %v, want %v", v, reply) 63 | } 64 | 65 | err = s.Load(c) 66 | if err != nil { 67 | t.Errorf("s.Load(c) returned %v", err) 68 | } 69 | 70 | err = s.SendHash(c, "key1", "key2", "arg1", "arg2") 71 | if err != nil { 72 | t.Errorf("s.SendHash(c, ...) returned %v", err) 73 | } 74 | 75 | err = c.Flush() 76 | if err != nil { 77 | t.Errorf("c.Flush() returned %v", err) 78 | } 79 | 80 | v, err = c.Receive() 81 | if !reflect.DeepEqual(v, reply) { 82 | t.Errorf("s.SendHash(c, ..); c.Receive() = %v, want %v", v, reply) 83 | } 84 | 85 | err = s.Send(c, "key1", "key2", "arg1", "arg2") 86 | if err != nil { 87 | t.Errorf("s.Send(c, ...) returned %v", err) 88 | } 89 | 90 | err = c.Flush() 91 | if err != nil { 92 | t.Errorf("c.Flush() returned %v", err) 93 | } 94 | 95 | v, err = c.Receive() 96 | if !reflect.DeepEqual(v, reply) { 97 | t.Errorf("s.Send(c, ..); c.Receive() = %v, want %v", v, reply) 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/test_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis 16 | 17 | import ( 18 | "bufio" 19 | "errors" 20 | "flag" 21 | "fmt" 22 | "io" 23 | "io/ioutil" 24 | "os" 25 | "os/exec" 26 | "strconv" 27 | "strings" 28 | "sync" 29 | "testing" 30 | "time" 31 | ) 32 | 33 | func SetNowFunc(f func() time.Time) { 34 | nowFunc = f 35 | } 36 | 37 | var ( 38 | ErrNegativeInt = errNegativeInt 39 | 40 | serverPath = flag.String("redis-server", "redis-server", "Path to redis server binary") 41 | serverAddress = flag.String("redis-address", "127.0.0.1", "The address of the server") 42 | serverBasePort = flag.Int("redis-port", 16379, "Beginning of port range for test servers") 43 | serverLogName = flag.String("redis-log", "", "Write Redis server logs to `filename`") 44 | serverLog = ioutil.Discard 45 | 46 | defaultServerMu sync.Mutex 47 | defaultServer *Server 48 | defaultServerErr error 49 | ) 50 | 51 | type Server struct { 52 | name string 53 | cmd *exec.Cmd 54 | done chan struct{} 55 | } 56 | 57 | func NewServer(name string, args ...string) (*Server, error) { 58 | s := &Server{ 59 | name: name, 60 | cmd: exec.Command(*serverPath, args...), 61 | done: make(chan struct{}), 62 | } 63 | 64 | r, err := s.cmd.StdoutPipe() 65 | if err != nil { 66 | return nil, err 67 | } 68 | 69 | err = s.cmd.Start() 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | ready := make(chan error, 1) 75 | go s.watch(r, ready) 76 | 77 | select { 78 | case err = <-ready: 79 | case <-time.After(time.Second * 10): 80 | err = errors.New("timeout waiting for server to start") 81 | } 82 | 83 | if err != nil { 84 | s.Stop() 85 | return nil, err 86 | } 87 | 88 | return s, nil 89 | } 90 | 91 | func (s *Server) watch(r io.Reader, ready chan error) { 92 | fmt.Fprintf(serverLog, "%d START %s \n", s.cmd.Process.Pid, s.name) 93 | var listening bool 94 | var text string 95 | scn := bufio.NewScanner(r) 96 | for scn.Scan() { 97 | text = scn.Text() 98 | fmt.Fprintf(serverLog, "%s\n", text) 99 | if !listening { 100 | if strings.Contains(text, " * Ready to accept connections") || 101 | strings.Contains(text, " * The server is now ready to accept connections on port") { 102 | listening = true 103 | ready <- nil 104 | } 105 | } 106 | } 107 | if !listening { 108 | ready <- fmt.Errorf("server exited: %s", text) 109 | } 110 | s.cmd.Wait() 111 | fmt.Fprintf(serverLog, "%d STOP %s \n", s.cmd.Process.Pid, s.name) 112 | close(s.done) 113 | } 114 | 115 | func (s *Server) Stop() { 116 | s.cmd.Process.Signal(os.Interrupt) 117 | <-s.done 118 | } 119 | 120 | // stopDefaultServer stops the server created by DialDefaultServer. 121 | func stopDefaultServer() { 122 | defaultServerMu.Lock() 123 | defer defaultServerMu.Unlock() 124 | if defaultServer != nil { 125 | defaultServer.Stop() 126 | defaultServer = nil 127 | } 128 | } 129 | 130 | // DefaultServerAddr starts the test server if not already started and returns 131 | // the address of that server. 132 | func DefaultServerAddr() (string, error) { 133 | defaultServerMu.Lock() 134 | defer defaultServerMu.Unlock() 135 | addr := fmt.Sprintf("%v:%d", *serverAddress, *serverBasePort) 136 | if defaultServer != nil || defaultServerErr != nil { 137 | return addr, defaultServerErr 138 | } 139 | defaultServer, defaultServerErr = NewServer( 140 | "default", 141 | "--port", strconv.Itoa(*serverBasePort), 142 | "--bind", *serverAddress, 143 | "--save", "", 144 | "--appendonly", "no") 145 | return addr, defaultServerErr 146 | } 147 | 148 | // DialDefaultServer starts the test server if not already started and dials a 149 | // connection to the server. 150 | func DialDefaultServer() (Conn, error) { 151 | addr, err := DefaultServerAddr() 152 | if err != nil { 153 | return nil, err 154 | } 155 | c, err := Dial("tcp", addr, DialReadTimeout(1*time.Second), DialWriteTimeout(1*time.Second)) 156 | if err != nil { 157 | return nil, err 158 | } 159 | c.Do("FLUSHDB") 160 | return c, nil 161 | } 162 | 163 | func TestMain(m *testing.M) { 164 | os.Exit(func() int { 165 | flag.Parse() 166 | 167 | var f *os.File 168 | if *serverLogName != "" { 169 | var err error 170 | f, err = os.OpenFile(*serverLogName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600) 171 | if err != nil { 172 | fmt.Fprintf(os.Stderr, "Error opening redis-log: %v\n", err) 173 | return 1 174 | } 175 | defer f.Close() 176 | serverLog = f 177 | } 178 | 179 | defer stopDefaultServer() 180 | 181 | return m.Run() 182 | }()) 183 | } 184 | -------------------------------------------------------------------------------- /vendor/github.com/garyburd/redigo/redis/zpop_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Gary Burd 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 | // not use this file except in compliance with the License. You may obtain 5 | // 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, WITHOUT 11 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | // License for the specific language governing permissions and limitations 13 | // under the License. 14 | 15 | package redis_test 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/garyburd/redigo/redis" 21 | ) 22 | 23 | // zpop pops a value from the ZSET key using WATCH/MULTI/EXEC commands. 24 | func zpop(c redis.Conn, key string) (result string, err error) { 25 | 26 | defer func() { 27 | // Return connection to normal state on error. 28 | if err != nil { 29 | c.Do("DISCARD") 30 | } 31 | }() 32 | 33 | // Loop until transaction is successful. 34 | for { 35 | if _, err := c.Do("WATCH", key); err != nil { 36 | return "", err 37 | } 38 | 39 | members, err := redis.Strings(c.Do("ZRANGE", key, 0, 0)) 40 | if err != nil { 41 | return "", err 42 | } 43 | if len(members) != 1 { 44 | return "", redis.ErrNil 45 | } 46 | 47 | c.Send("MULTI") 48 | c.Send("ZREM", key, members[0]) 49 | queued, err := c.Do("EXEC") 50 | if err != nil { 51 | return "", err 52 | } 53 | 54 | if queued != nil { 55 | result = members[0] 56 | break 57 | } 58 | } 59 | 60 | return result, nil 61 | } 62 | 63 | // zpopScript pops a value from a ZSET. 64 | var zpopScript = redis.NewScript(1, ` 65 | local r = redis.call('ZRANGE', KEYS[1], 0, 0) 66 | if r ~= nil then 67 | r = r[1] 68 | redis.call('ZREM', KEYS[1], r) 69 | end 70 | return r 71 | `) 72 | 73 | // This example implements ZPOP as described at 74 | // http://redis.io/topics/transactions using WATCH/MULTI/EXEC and scripting. 75 | func Example_zpop() { 76 | c, err := dial() 77 | if err != nil { 78 | fmt.Println(err) 79 | return 80 | } 81 | defer c.Close() 82 | 83 | // Add test data using a pipeline. 84 | 85 | for i, member := range []string{"red", "blue", "green"} { 86 | c.Send("ZADD", "zset", i, member) 87 | } 88 | if _, err := c.Do(""); err != nil { 89 | fmt.Println(err) 90 | return 91 | } 92 | 93 | // Pop using WATCH/MULTI/EXEC 94 | 95 | v, err := zpop(c, "zset") 96 | if err != nil { 97 | fmt.Println(err) 98 | return 99 | } 100 | fmt.Println(v) 101 | 102 | // Pop using a script. 103 | 104 | v, err = redis.String(zpopScript.Do(c, "zset")) 105 | if err != nil { 106 | fmt.Println(err) 107 | return 108 | } 109 | fmt.Println(v) 110 | 111 | // Output: 112 | // red 113 | // blue 114 | } 115 | --------------------------------------------------------------------------------