├── .gitignore ├── tests ├── help.test ├── peek.test ├── put.test └── pop.test ├── go.mod ├── Makefile ├── command ├── peek.go ├── command.go ├── put.go ├── pop.go ├── flush.go └── monitor.go ├── LICENSE.md ├── .travis.yml ├── beanstalkd-cli.man ├── README.md ├── go.sum └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /vendor 3 | -------------------------------------------------------------------------------- /tests/help.test: -------------------------------------------------------------------------------- 1 | --help 2 | >>> /A simple cli interface for managing beanstalkd queues\./ 3 | >>>= 0 4 | -------------------------------------------------------------------------------- /tests/peek.test: -------------------------------------------------------------------------------- 1 | beanstalkd-cli put --data="peek-test" 2 | >>> /Succesfully inserted job/ 3 | >>>= 0 4 | 5 | beanstalkd-cli peek 6 | >>> 7 | peek-test 8 | >>>= 0 9 | 10 | beanstalkd-cli peek 11 | >>> 12 | peek-test 13 | >>>= 0 14 | 15 | beanstalkd-cli flush 16 | >>>= 0 17 | -------------------------------------------------------------------------------- /tests/put.test: -------------------------------------------------------------------------------- 1 | beanstalkd-cli put --data="test" 2 | >>> /Succesfully inserted job/ 3 | >>>= 0 4 | 5 | beanstalkd-cli put 6 | <<< 7 | test2 8 | >>> /Succesfully inserted job/ 9 | >>>= 0 10 | 11 | beanstalkd-cli put --data="put tube" --tube=put-test 12 | >>> /tube=put-test/ 13 | >>>= 0 14 | 15 | beanstalkd-cli flush 16 | >>>= 0 17 | -------------------------------------------------------------------------------- /tests/pop.test: -------------------------------------------------------------------------------- 1 | beanstalkd-cli put --data="pop test" 2 | >>> /Succesfully inserted job/ 3 | >>>= 0 4 | 5 | beanstalkd-cli pop 6 | >>> 7 | pop test 8 | >>>= 0 9 | 10 | 11 | beanstalkd-cli put --data="pop test2" --tube=pop-test 12 | >>> /Succesfully inserted job/ 13 | >>>= 0 14 | 15 | beanstalkd-cli pop --tube=pop-test 16 | >>> 17 | pop test2 18 | >>>= 0 19 | 20 | beanstalkd-cli flush 21 | >>>= 0 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module beanstalkd-cli 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect 7 | github.com/edwinhoksberg/beanstalkd-cli v0.0.0-20200117123345-4921228864cc // indirect 8 | github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect 9 | github.com/mpdroog/beanstalkd v0.0.0-20151231090228-bf387addf002 10 | github.com/sirupsen/logrus v1.4.2 11 | github.com/urfave/cli v1.22.2 12 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 // indirect 13 | ) 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUILDPATH=$(CURDIR) 2 | MANPATH="/usr/local/share/man/man1" 3 | GO=$(shell which go) 4 | 5 | # Compile time values 6 | PROGRAM=beanstalkd-cli 7 | VERSION=$(shell printf "%s [%s]" `git describe --abbrev=0 --tags` `git rev-parse --verify HEAD`) 8 | 9 | # Interpolate the variable values using go link flags 10 | LDFLAGS=-ldflags "-X 'main.Name=${PROGRAM}' -X 'main.Version=${VERSION}'" 11 | 12 | build: 13 | @if [ ! -d $(BUILDPATH)/bin ] ; then mkdir -p $(BUILDPATH)/bin ; fi 14 | $(GO) build ${LDFLAGS} -o $(BUILDPATH)/bin/$(PROGRAM) 15 | 16 | install: 17 | @if [ ! -d $(MANPATH) ] ; then mkdir -p $(MANPATH) ; fi 18 | 19 | cp $(BUILDPATH)/bin/$(PROGRAM) /usr/bin/$(PROGRAM) 20 | gzip -c $(PROGRAM).man | tee $(MANPATH)/$(PROGRAM).1.gz > /dev/null 21 | 22 | ci-test: 23 | ifeq ($(GIMME_OS),linux) 24 | shelltest --with ./bin/beanstalkd-cli_$(GIMME_OS)_$(GIMME_ARCH)$(EXT) --diff --color --all tests 25 | endif 26 | 27 | clean: 28 | rm -f $(BUILDPATH)/bin/$(PROGRAM) 29 | 30 | all: build 31 | -------------------------------------------------------------------------------- /command/peek.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/urfave/cli" 7 | ) 8 | 9 | // Peek displays the next job in the tube without removing it. 10 | func (c *Command) Peek(cli *cli.Context) error { 11 | log := c.GetLogger(cli) 12 | 13 | // Build and connect to beanstalkd 14 | client, err := c.GetBeanstalkdClient(cli) 15 | if err != nil { 16 | log.WithError(err).Error("Could not connect to beanstalkd server") 17 | return err 18 | } 19 | 20 | // Select a tube to peek from 21 | log.Debug("Selecting correct tube...") 22 | client.Use(cli.String("tube")) 23 | if err != nil { 24 | log.WithError(err).WithField("tube", cli.String("tube")).Error("Failed to select tube") 25 | return err 26 | } 27 | 28 | // Here we peek a job from the watched tube 29 | log.Debug("Peeking job...") 30 | job, err := client.PeekReady() 31 | if err != nil { 32 | log.WithError(err).WithField("tube", cli.String("tube")).Error("Failed to peek job") 33 | return err 34 | } 35 | 36 | fmt.Println(string(job.Data[:])) 37 | 38 | client.Quit() 39 | 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Edwin Hoksberg 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 | -------------------------------------------------------------------------------- /command/command.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/mpdroog/beanstalkd" 8 | log "github.com/sirupsen/logrus" 9 | "github.com/sirupsen/logrus/hooks/test" 10 | "github.com/urfave/cli" 11 | ) 12 | 13 | // Command holds data for the cli commands. 14 | type Command struct{} 15 | 16 | // GetLogger returns a logrus object according to any flags set 17 | // in the application parameters. 18 | func (c Command) GetLogger(cli *cli.Context) *log.Logger { 19 | if cli.GlobalBool("quiet") { 20 | logger, _ := test.NewNullLogger() 21 | return logger 22 | } 23 | 24 | // Set the default output formatter 25 | format := &log.TextFormatter{ 26 | TimestampFormat: "2006-01-02 15:04:05.000", 27 | FullTimestamp: true, 28 | } 29 | 30 | log.SetFormatter(format) 31 | 32 | // If the verbose flag was enabled, enable debug logging 33 | if cli.GlobalBool("verbose") { 34 | log.SetLevel(log.DebugLevel) 35 | } 36 | 37 | logger := log.StandardLogger() 38 | logger.SetOutput(os.Stdout) 39 | 40 | return logger 41 | } 42 | 43 | // GetBeanstalkdClient returns a client object for interacting with a beanstalkd server. 44 | func (c Command) GetBeanstalkdClient(cli *cli.Context) (*beanstalkd.BeanstalkdClient, error) { 45 | // Build a connection string. 46 | addr := fmt.Sprintf("%s:%d", cli.GlobalString("server"), cli.GlobalInt("port")) 47 | 48 | // Connect to the beanstalkd server. 49 | log.Debugf("Connecting to beanstalkd server: %s", addr) 50 | client, err := beanstalkd.Dial(addr) 51 | 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | log.Debug("Successfully connected") 57 | 58 | return client, nil 59 | } 60 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | 4 | language: go 5 | 6 | services: 7 | - docker 8 | 9 | env: 10 | global: 11 | - PROGRAM=beanstalkd-cli 12 | - GO111MODULE=on 13 | jobs: 14 | - GIMME_OS=linux GIMME_ARCH=amd64 EXT= 15 | - GIMME_OS=linux GIMME_ARCH=386 EXT= 16 | - GIMME_OS=windows GIMME_ARCH=amd64 EXT=.exe 17 | - GIMME_OS=windows GIMME_ARCH=386 EXT=.exe 18 | - GIMME_OS=darwin GIMME_ARCH=amd64 EXT= 19 | 20 | before_install: 21 | - docker run -d -p 11300:11300 schickling/beanstalkd 22 | 23 | install: 24 | - sudo apt-get -y install shelltestrunner 25 | - go get -v 26 | 27 | script: 28 | - go build -v -ldflags "-X main.CommitHash=${TRAVIS_COMMIT} -X main.Name=${PROGRAM} -X main.Version=${TRAVIS_TAG}" -o ./bin/beanstalkd-cli_${GIMME_OS}_${GIMME_ARCH}${EXT} 29 | - make ci-test 30 | 31 | deploy: 32 | provider: releases 33 | token: 34 | secure: XK7hoV3VH4ya/I+dw4JPNYf9u1AZkP02T7ZkfMAHDK5pYWyS9S8MeRj/kCrgjPi5tLMgsPcxrN19FXa+FVkkqc1ngGd4ALrJMDx4/RVMyH6JKhr/pmTt6gxTumrCclQrPiubhVaM5ihfY9NRxymZnipKHWtZTuLNzy88m4Xn2yRKYk1FSXr/ssE6qY7aW3StjqoDNZi6TeUd7Dooz2WTwCdquH23EsUiclJ1WMKSHJz7ROeiBU7B2Vvp1uFIAijr5L4Vw96XVfEDkL1G0zB6IX1/IJ0dQk4SiL+Qv26GcI3zsVfH9UZCa9NfcFEMwvjb5a2bcPRlxIww+ScN01OLD6IY8TcKjGWkhmCx9bozN+m402C273ELypBptgjw2F0tOgMqdT4Shr+s8RqnSLru5kZ/kyJXMN+rgN/c3pA1YpZmbjnEt90muSwevkNEUG47mwaTwqHoEvb+tS3LuLKt7iFitlu7P9i15UlOTgFDSxSoimGHoKJwkLukSKqyFnlIXTprzj2quENxekM1OhmpGC2YW+81JZWfzbITWtY5D8j6JXBv//1rF5QgTscovd8xUTGN7FzezLBsSAio+DzJ1ZiDqfM2czSRTf/3ZO2sENVdOnXztleyDx91eZWVSb/BFUXCW1fJ74xChdQjZk4Uau21OC3L9wiWdhQu55YfyRw= 35 | file: ./bin/beanstalkd-cli_${GIMME_OS}_${GIMME_ARCH}${EXT} 36 | skip_cleanup: true 37 | on: 38 | tags: true 39 | -------------------------------------------------------------------------------- /beanstalkd-cli.man: -------------------------------------------------------------------------------- 1 | .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.12. 2 | .TH BEANSTALKD-CLI "1" "February 2020" "beanstalkd-cli version 1.3.2 [7f1ad96f65246471eae3c07454ad8c77e6a0792f]" "User Commands" 3 | .SH NAME 4 | beanstalkd-cli \- manual page for beanstalkd-cli version 1.3.2 [7f1ad96f65246471eae3c07454ad8c77e6a0792f] 5 | .SH DESCRIPTION 6 | .SS "NAME:" 7 | .IP 8 | beanstalkd\-cli 9 | .SS "USAGE:" 10 | .IP 11 | A simple cli interface for managing beanstalkd queues. 12 | .IP 13 | Homepage: https://github.com/edwinhoksberg/beanstalkd\-cli 14 | .SS "VERSION:" 15 | .IP 16 | 1.3.2 [7f1ad96f65246471eae3c07454ad8c77e6a0792f] 17 | .SS "AUTHOR:" 18 | .IP 19 | Edwin Hoksberg 20 | .SS "COMMANDS:" 21 | .TP 22 | monitor 23 | Monitor the beanstalkd queues 24 | .TP 25 | flush 26 | Completely remove all jobs from a tube 27 | .TP 28 | pop 29 | Remove a job from a queue and display it 30 | .TP 31 | put 32 | Write a job to a queue 33 | .TP 34 | peek 35 | Display a job from the queue without removing it 36 | .TP 37 | help, h 38 | Shows a list of commands or help for one command 39 | .SS "GLOBAL OPTIONS:" 40 | .TP 41 | \fB\-\-verbose\fR 42 | set this to enable debug logging 43 | .TP 44 | \fB\-\-quiet\fR 45 | set this to disable logging 46 | .TP 47 | \fB\-\-server\fR value 48 | The server name where beanstalkd is running (default: "127.0.0.1") 49 | .TP 50 | \fB\-\-port\fR value 51 | The port on which beanstalkd is listening (default: 11300) 52 | .TP 53 | \fB\-\-help\fR, \fB\-h\fR 54 | show help 55 | .TP 56 | \fB\-\-version\fR, \fB\-v\fR 57 | print the version 58 | .SH "SEE ALSO" 59 | The full documentation for 60 | .B beanstalkd-cli 61 | is maintained as a Texinfo manual. If the 62 | .B info 63 | and 64 | .B beanstalkd-cli 65 | programs are properly installed at your site, the command 66 | .IP 67 | .B info beanstalkd-cli 68 | .PP 69 | should give you access to the complete manual. 70 | -------------------------------------------------------------------------------- /command/put.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | 7 | "github.com/sirupsen/logrus" 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | // Put adds a new job into a queue, from a string or reading from stdin. 12 | func (c *Command) Put(cli *cli.Context) error { 13 | log := c.GetLogger(cli) 14 | 15 | // Check if the job data is not empty 16 | if len(cli.String("data")) < 1 { 17 | log.Error("No data passed, use the --data parameter to insert data into a tube.") 18 | return nil 19 | } 20 | 21 | data := []byte(cli.String("data")) 22 | 23 | // Read data from stdin 24 | var err error 25 | if cli.String("data") == "-" { 26 | // Read data from stdin until EOF 27 | data, err = ioutil.ReadAll(os.Stdin) 28 | 29 | // Exit with an error if we couldnt read from stdin. 30 | if err != nil { 31 | log.WithError(err).Error("Could not read from stdin") 32 | } 33 | } 34 | 35 | // Build and connect to beanstalkd 36 | client, err := c.GetBeanstalkdClient(cli) 37 | if err != nil { 38 | log.WithError(err).Error("Could not connect to beanstalkd server") 39 | return err 40 | } 41 | 42 | log.Debug("Selecting correct tube...") 43 | 44 | // Select a tube to insert 45 | client.Use(cli.String("tube")) 46 | if err != nil { 47 | log.WithError(err).WithField("tube", cli.String("tube")).Error("Failed to select tube") 48 | return err 49 | } 50 | 51 | log.Debug("Inserting job...") 52 | 53 | // Insert the new job 54 | id, err := client.Put( 55 | uint32(cli.Int("priority")), 56 | cli.Duration("delay"), 57 | cli.Duration("ttr"), 58 | data, 59 | ) 60 | 61 | if err != nil { 62 | log.WithError(err).WithField("tube", cli.String("tube")).Error("Failed to insert job in queue") 63 | return err 64 | } 65 | 66 | log.WithFields(logrus.Fields{ 67 | "tube": cli.String("tube"), 68 | "id": id, 69 | }).Info("Succesfully inserted job") 70 | 71 | client.Quit() 72 | 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /command/pop.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/sirupsen/logrus" 7 | "github.com/urfave/cli" 8 | ) 9 | 10 | // Pop removes and displays a job from the selected tube. 11 | func (c *Command) Pop(cli *cli.Context) error { 12 | log := c.GetLogger(cli) 13 | 14 | // Build and connect to beanstalkd 15 | client, err := c.GetBeanstalkdClient(cli) 16 | if err != nil { 17 | log.WithError(err).Error("Could not connect to beanstalkd server") 18 | return err 19 | } 20 | 21 | log.Debug("Selecting correct tube...") 22 | 23 | if cli.String("tube") != "default" { 24 | // Watch a specified tube. 25 | if _, err := client.Watch(cli.String("tube")); err != nil { 26 | log.WithError(err).WithField("tube", cli.String("tube")).Error("Failed to select tube") 27 | return err 28 | } 29 | 30 | // By default the default tube is always in the watch list. 31 | // To prevent flushing any jobs from the default tube we can ignore it 32 | // after watching a different tube. 33 | if _, err := client.Ignore("default"); err != nil { 34 | log.WithError(err).Error("Failed to ignore default tube") 35 | return err 36 | } 37 | } 38 | 39 | // Here we reserve a job to retrieve its contents 40 | log.Debug("Reserving a job...") 41 | job, err := client.Reserve(3) 42 | if err != nil { 43 | if err.Error() == "timed out" { 44 | log.Info("No jobs found in tube, exiting...") 45 | return err 46 | } 47 | 48 | log.WithError(err).Error() 49 | return err 50 | } 51 | 52 | // After reserving a job, we can delete it 53 | log.WithField("id", job.Id).Debug("Deleting the reserved job") 54 | if err := client.Delete(job.Id); err != nil { 55 | log.WithError(err).WithFields(logrus.Fields{ 56 | "tube": cli.String("tube"), 57 | "job": job.Id, 58 | }).Error("Failed to delete job") 59 | return err 60 | } 61 | 62 | fmt.Println(string(job.Data[:])) 63 | 64 | client.Quit() 65 | 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # beanstalkd-cli [![Build Status](https://travis-ci.org/EdwinHoksberg/beanstalkd-cli.svg?branch=master)](https://travis-ci.org/EdwinHoksberg/beanstalkd-cli) 2 | A simple cli interface for managing beanstalkd queues. 3 | 4 | ## Download 5 | You can find the latest release including binaries [here](https://github.com/EdwinHoksberg/beanstalkd-cli/releases/latest). 6 | 7 | ## Usage 8 | ``` 9 | NAME: 10 | beanstalkd-cli 11 | 12 | USAGE: 13 | A simple cli interface for managing beanstalkd queues. 14 | 15 | Homepage: https://github.com/edwinhoksberg/beanstalkd-cli 16 | 17 | VERSION: 18 | 1.3.2 [7f1ad96f65246471eae3c07454ad8c77e6a0792f] 19 | 20 | AUTHOR: 21 | Edwin Hoksberg 22 | 23 | COMMANDS: 24 | monitor 25 | Monitor the beanstalkd queues 26 | 27 | flush Completely remove all jobs from a tube 28 | 29 | pop Remove a job from a queue and display it 30 | 31 | put Write a job to a queue 32 | 33 | peek Display a job from the queue without removing it 34 | 35 | help, h 36 | Shows a list of commands or help for one command 37 | 38 | GLOBAL OPTIONS: 39 | --verbose 40 | set this to enable debug logging 41 | 42 | --quiet 43 | set this to disable logging 44 | 45 | --server value 46 | The server name where beanstalkd is running (default: "127.0.0.1") 47 | 48 | --port value 49 | The port on which beanstalkd is listening (default: 11300) 50 | 51 | --help, -h 52 | show help 53 | 54 | --version, -v 55 | print the version 56 | ``` 57 | 58 | ## Development 59 | This program is written in [Go](https://golang.org/), using these dependencies: 60 | - [sirupsen/logrus](https://github.com/sirupsen/logrus) - Logrus is a structured logger for Go (golang), completely API compatible with the standard library logger. 61 | - [urfave/cli](https://github.com/urfave/cli) - cli is a simple, fast, and fun package for building command line apps in Go. 62 | - [mpdroog/beanstalkd](https://github.com/mpdroog/beanstalkd) - A golang beanstalkd client. 63 | 64 | ## Compile from source 65 | This program uses Go modules which makes it easy to compile it anywhere on your system without the need for GOPATH. Just run `make build` in the project directory and it should compile without any problems. 66 | 67 | ## License 68 | [MIT](LICENSE.md) 69 | -------------------------------------------------------------------------------- /command/flush.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "github.com/sirupsen/logrus" 5 | "github.com/urfave/cli" 6 | ) 7 | 8 | // Flush empties an entire tube of jobs. 9 | func (c *Command) Flush(cli *cli.Context) error { 10 | log := c.GetLogger(cli) 11 | 12 | // Build and connect to beanstalkd 13 | client, err := c.GetBeanstalkdClient(cli) 14 | if err != nil { 15 | log.WithError(err).Error("Could not connect to beanstalkd server") 16 | return err 17 | } 18 | 19 | log.Debug("Selecting correct tube...") 20 | 21 | if cli.String("tube") != "default" { 22 | // Watch a specified tube. 23 | if _, err := client.Watch(cli.String("tube")); err != nil { 24 | log.WithError(err).WithField("tube", cli.String("tube")).Error("Failed to select tube") 25 | return err 26 | } 27 | 28 | // By default the default tube is always in the watch list. 29 | // To prevent flushing any jobs from the default tube we can ignore it 30 | // after watching a different tube. 31 | if _, err := client.Ignore("default"); err != nil { 32 | log.WithError(err).Error("Failed to ignore default tube") 33 | return err 34 | } 35 | } 36 | 37 | log.Debug("Starting flush loop...") 38 | 39 | counter := 0 40 | for { 41 | log.Debug("[Loop] Reserving job...") 42 | 43 | job, err := client.Reserve(1) 44 | if err != nil { 45 | if err.Error() == "timed out" { 46 | // If the job deleted counter is still at zero, it means the tube 47 | // was empty before we even started flushing jobs, so we don't show the error. 48 | if counter == 0 { 49 | log.WithError(err).WithField("tube", cli.String("tube")).Info("No jobs found in tube, exiting...") 50 | } 51 | 52 | break 53 | } 54 | 55 | // The DEADLINE_SOON message just indicates that a job was about to expire. 56 | // This has no effect on our actions here so we just ignore it. 57 | if err.Error() != "deadline soon" { 58 | log.WithError(err).WithField("tube", cli.String("tube")).Error("Failed reserving a job") 59 | break 60 | } 61 | } 62 | 63 | log.Debug("[Loop] Deleting job...") 64 | 65 | // Delete the job from the tube 66 | if err := client.Delete(job.Id); err != nil { 67 | log.WithError(err).WithFields(logrus.Fields{ 68 | "tube": cli.String("tube"), 69 | "job": job.Id, 70 | }).Error("Failed to delete job") 71 | break 72 | } 73 | 74 | log.Debug("[Loop] Done") 75 | 76 | counter++ 77 | } 78 | 79 | log.Infof("%d jobs removed", counter) 80 | 81 | client.Quit() 82 | 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /command/monitor.go: -------------------------------------------------------------------------------- 1 | package command 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/urfave/cli" 9 | ) 10 | 11 | var ( 12 | keys = []string{ 13 | "uptime", 14 | "current-jobs-ready", 15 | "current-workers", 16 | "current-producers", 17 | "current-connections", 18 | } 19 | 20 | tubeKeys = []string{ 21 | "total-jobs", 22 | "current-jobs-ready", 23 | "current-using", 24 | "current-watching", 25 | } 26 | ) 27 | 28 | // Monitor outputs a overview of server and tube statistics, repeated every second. 29 | func (c *Command) Monitor(cli *cli.Context) error { 30 | log := c.GetLogger(cli) 31 | 32 | // Build and connect to beanstalkd 33 | client, err := c.GetBeanstalkdClient(cli) 34 | if err != nil { 35 | log.WithError(err).Error("Could not connect to beanstalkd server") 36 | return err 37 | } 38 | 39 | // Assign a few interesting keys to display. 40 | // If no keys are passed on the cli we assign a we default keys ourself. 41 | if len(cli.StringSlice("keys")) > 0 { 42 | keys = cli.StringSlice("keys") 43 | } 44 | 45 | if len(cli.StringSlice("tubekeys")) > 0 { 46 | tubeKeys = cli.StringSlice("tubekeys") 47 | } 48 | 49 | // Infinite loop, show beanstalkd stats until the users exists the application. 50 | for { 51 | // Retrieve stats, break loop if an error occured. 52 | log.Debug("Retrieving stats...") 53 | stats, err := client.Stats() 54 | 55 | if err != nil { 56 | log.WithError(err).Error("Failed to retrieve server stats") 57 | break 58 | } 59 | 60 | // Build a buffer for our output. 61 | var buffer bytes.Buffer 62 | 63 | // Clear the screen. 64 | buffer.WriteString("\033[H\033[2J") 65 | 66 | // Write global stats. 67 | for i, value := range keys { 68 | buffer.WriteString(fmt.Sprintf("%s: %s", value, stats[value])) 69 | 70 | if i < (len(keys) - 1) { 71 | buffer.WriteString(", ") 72 | } 73 | } 74 | 75 | buffer.WriteByte('\n') 76 | 77 | // Get a slice of all existing tubes. 78 | tubes, err := client.ListTubes() 79 | if err != nil { 80 | log.WithError(err).Error("Error listing tubes") 81 | break 82 | } 83 | 84 | // Write stats for each tube. 85 | for _, tube := range tubes { 86 | log.WithField("tube", tube).Debug("Retrieving tube stats...") 87 | 88 | stats, err := client.StatsTube(tube) 89 | if err != nil { 90 | log.WithError(err).WithField("tube", tube).Error("Error reading tube stats") 91 | break 92 | } 93 | 94 | buffer.WriteString(fmt.Sprintf("\nName: %s\n", tube)) 95 | 96 | for _, value := range tubeKeys { 97 | buffer.WriteString(fmt.Sprintf("%s: %s\n", value, stats[value])) 98 | } 99 | } 100 | 101 | // Output the buffer. 102 | fmt.Print(buffer.String()) 103 | 104 | // Sleep for 1 second. 105 | time.Sleep(time.Second) 106 | } 107 | 108 | return nil 109 | } 110 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 4 | github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= 5 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/edwinhoksberg/beanstalkd-cli v0.0.0-20200117123345-4921228864cc h1:f6ga3ko8DEd8mNJUONKSSbCmm2N0PT9v2UIjb04khaA= 8 | github.com/edwinhoksberg/beanstalkd-cli v0.0.0-20200117123345-4921228864cc/go.mod h1:sGJNsH6qB0/Xtu5esTGWuQR5y66Nuyw5eEGLX67FshU= 9 | github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= 10 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 11 | github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= 12 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 13 | github.com/mpdroog/beanstalkd v0.0.0-20151231090228-bf387addf002 h1:QNXRrUAScGch2v2koxvFMpobq88V7cEGvbLfHX8D+Ak= 14 | github.com/mpdroog/beanstalkd v0.0.0-20151231090228-bf387addf002/go.mod h1:Qnm/zq76O/pqutkxyyE4WGu30l4S/6iPUnESIDLNFFQ= 15 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 16 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= 17 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 18 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= 19 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 20 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= 21 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 22 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 23 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 24 | github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo= 25 | github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 26 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= 27 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 28 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= 29 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 30 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 31 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 32 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "beanstalkd-cli/command" 9 | "github.com/urfave/cli" 10 | ) 11 | 12 | var ( 13 | Name string 14 | Version string 15 | ) 16 | 17 | func main() { 18 | command := new(command.Command) 19 | 20 | app := cli.NewApp() 21 | 22 | app.Name = Name 23 | app.Usage = "" 24 | app.HelpName = Name 25 | app.Version = Version 26 | app.Authors = []cli.Author{ 27 | { 28 | Name: "Edwin Hoksberg", 29 | Email: "mail@edwinhoksberg.nl", 30 | }, 31 | } 32 | app.UsageText = fmt.Sprintf(`A simple cli interface for managing beanstalkd queues. 33 | 34 | Homepage: https://github.com/edwinhoksberg/beanstalkd-cli`) 35 | 36 | app.Commands = []cli.Command{ 37 | { 38 | Name: "monitor", 39 | Usage: "Monitor the beanstalkd queues", 40 | Action: command.Monitor, 41 | Flags: []cli.Flag{ 42 | &cli.StringSliceFlag{ 43 | Name: "keys", 44 | Usage: "Different keys to monitor", 45 | }, 46 | &cli.StringSliceFlag{ 47 | Name: "tubekeys", 48 | Usage: "Different tube keys to monitor", 49 | }, 50 | }, 51 | }, 52 | { 53 | Name: "flush", 54 | Usage: "Completely remove all jobs from a tube", 55 | Action: command.Flush, 56 | Flags: []cli.Flag{ 57 | &cli.StringFlag{ 58 | Name: "tube", 59 | Usage: "The name of the tube to flush", 60 | Value: "default", 61 | }, 62 | }, 63 | }, 64 | { 65 | Name: "pop", 66 | Usage: "Remove a job from a queue and display it", 67 | Action: command.Pop, 68 | Flags: []cli.Flag{ 69 | &cli.StringFlag{ 70 | Name: "tube", 71 | Usage: "The name of the tube to pop an item from", 72 | Value: "default", 73 | }, 74 | }, 75 | }, 76 | { 77 | Name: "put", 78 | Usage: "Write a job to a queue", 79 | Action: command.Put, 80 | Flags: []cli.Flag{ 81 | &cli.StringFlag{ 82 | Name: "tube", 83 | Usage: "The name of the tube where a new job should be inserted", 84 | Value: "default", 85 | }, 86 | &cli.StringFlag{ 87 | Name: "data", 88 | Usage: "The message to write to the queue (default reads from stdin)", 89 | Value: "-", 90 | }, 91 | &cli.IntFlag{ 92 | Name: "priority", 93 | Usage: "Job priority, a lower value will be scheduled before jobs with a larger priority", 94 | Value: 1024, 95 | }, 96 | &cli.DurationFlag{ 97 | Name: "delay", 98 | Usage: "How many seconds to wait before putting the job in the queue, e.g. 300s or 2h15m", 99 | Value: 0, 100 | }, 101 | &cli.DurationFlag{ 102 | Name: "ttr", 103 | Usage: "The number of seconds to allow a worker to run this job, e.g. 300s or 2h15m", 104 | Value: time.Hour, 105 | }, 106 | }, 107 | }, 108 | { 109 | Name: "peek", 110 | Usage: "Display a job from the queue without removing it", 111 | Action: command.Peek, 112 | Flags: []cli.Flag{ 113 | &cli.StringFlag{ 114 | Name: "tube", 115 | Usage: "The name of the tube to peek from", 116 | Value: "default", 117 | }, 118 | }, 119 | }, 120 | } 121 | 122 | app.Flags = []cli.Flag{ 123 | &cli.BoolFlag{ 124 | Name: "verbose", 125 | Usage: "set this to enable debug logging", 126 | }, 127 | &cli.BoolFlag{ 128 | Name: "quiet", 129 | Usage: "set this to disable logging", 130 | }, 131 | &cli.StringFlag{ 132 | Name: "server", 133 | Usage: "The server name where beanstalkd is running", 134 | Value: "127.0.0.1", 135 | }, 136 | &cli.IntFlag{ 137 | Name: "port", 138 | Usage: "The port on which beanstalkd is listening", 139 | Value: 11300, 140 | }, 141 | } 142 | 143 | app.Run(os.Args) 144 | } 145 | --------------------------------------------------------------------------------