Skip to content

Commit

Permalink
Merge branch 'release/1.0.0' into production
Browse files Browse the repository at this point in the history
  • Loading branch information
koiuo committed Dec 13, 2017
2 parents de64d30 + 23170b0 commit a4972e2
Show file tree
Hide file tree
Showing 7 changed files with 530 additions and 5 deletions.
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# IDE and build system artifacts
*.dll
*.dylib
*.exe
*.iml
*.so
*.test
.idea/
.protoc
gprobe
release/

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog

## 1.0.0 - 2017-12-13

### Added

- Probing servers and services via gRPC health-checking protocol
52 changes: 52 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
BINARY=gprobe
VERSION=$(shell git describe 2>/dev/null || echo "0.0.0")
RELEASE=release
RELEASE_FORMAT=${BINARY}-${GOOS}-${GOARCH}-${VERSION}.tar.gz

default: bin
.PHONY: bin
bin: deps ${BINARY}

.PHONY: release
release: release/gprobe-linux-amd64-${VERSION}.tar.gz
release: release/gprobe-linux-386-${VERSION}.tar.gz
release: release/gprobe-darwin-amd64-${VERSION}.tar.gz

.PHONY: release-dir
release-dir:
mkdir -p ${RELEASE}

${RELEASE}/%-${VERSION}.tar.gz: | release-dir
GOOS=$(shell echo $* | cut -d '-' -f 2) GOARCH=$(shell echo $* | cut -d '-' -f 3) \
go build -ldflags="-s -w -X main.version=${VERSION} -v" -o ${RELEASE}/${BINARY}
tar -C ${RELEASE} -czf $@ ${BINARY}
rm ${RELEASE}/${BINARY}

${BINARY}:
go build -ldflags="-s -w -X main.version=${VERSION} -v" -o $@

.PHONY: lint
lint:
go get -u github.com/golang/lint/golint
golint -set_exit_status ./...

.PHONY: test
test:
go test -v $(go list ./... | grep -v /acctest/)

.PHONY: acctest
acctest: ${BINARY}
go test -v ./acctest/... -args -gprobe `pwd`/${BINARY}

.PHONY: deps
deps:
go get -u ./...

.PHONY: test-deps
test-deps:
go get -t -u ./...

.PHONY: clean
clean:
rm -f ${BINARY}
rm -rf ${RELEASE}
88 changes: 83 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,89 @@
```
____ ____
/ \/ \ |
| | ,--. ,--. .-- .--. |,--. ,---.
\::::::::::; | | | | | | | | | |---'
`:::::::;' '--| |--' | '--' '---' `---
`:::;' / |
`'
```

_gprobe_ is a CLI client for the
[gRPC health-checking protocol](https://github.com/grpc/grpc/blob/master/doc/health-checking.md).

## Usage

Assuming server is listening on `localhost:1234`

Check server health (it is considered healthy if it has `grpc.health.v1.Health` service and is able to serve requests)

```bash
gprobe localhost:1234
```

Check specific service health

```bash
gprobe localhost:1234 my.package.MyService
```

Get help

|
,--. ,--. .-- .--. |,--. ,---.
| | | | | | | | | |---'
'--| |--' | '--' '---' `---
/ |
```bash
gprobe -h
```

## Building

Valid _go_ environment is required to build `gprobe` (`go` is in `PATH`, `GOPATH` is set, etc.).

Build distributable tarballs for all OSes

```bash
make release
```

Build binary for current OS

```bash
make bin
```

## Development

This project follows git-flow branching model. All development is done off of the `develop` branch. `HEAD` in
`production` branch should always point to a tagged release. There's no `master` branch to avoid possible confusion.

To contribute:

1. Create a feature branch from the latest `develop`, commit your work there
```bash
git checkout develop
git pull
git checkout -b feature/<feature_description>
```
2. Run `go fmt` and all the checks before committing any code
```bash
go fmt ./...
make lint test acctest
```
3. When the change is ready in a separate commit update `CHANGELOG.md` describing the change. Follow
[keepachangelog](http://keepachangelog.com/en/1.0.0/) guidelines
4. Create PR to develop

To release:

1. Create a release branch from the latest `develop` and update `CHANGELOG.md` there, setting version and date
```bash
git checkout -b release/1.2.3
```
2. Create PR to `production`
3. Once PR is merged, tag `HEAD` commit using annotated tag
```bash
git tag -a 1.2.3 -m "1.2.3"
```
4. Merge `production` back to `develop`. Do not use `fast-forward` merges
```bash
git checkout develop
git merge --no-ff production
```
170 changes: 170 additions & 0 deletions acctest/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// PUBLIC DOMAIN NOTICE
// National Center for Biotechnology Information
//
// This software/database is a "United States Government Work" under the
// terms of the United States Copyright Act. It was written as part of
// the author's official duties as a United States Government employee and
// thus cannot be copyrighted. This software/database is freely available
// to the public for use. The National Library of Medicine and the U.S.
// Government have not placed any restriction on its use or reproduction.
//
// Although all reasonable efforts have been taken to ensure the accuracy
// and reliability of the software and data, the NLM and the U.S.
// Government do not and cannot warrant the performance or results that
// may be obtained by using this software or data. The NLM and the U.S.
// Government disclaim all warranties, express or implied, including
// warranties of performance, merchantability or fitness for any particular
// purpose.
//
// Please cite the author in any work or product based on this material.

package acctest

import (
"bytes"
"fmt"
"github.com/stretchr/testify/assert"
"google.golang.org/grpc"
"google.golang.org/grpc/health"
hv1 "google.golang.org/grpc/health/grpc_health_v1"
"io"
"log"
"net"
"os"
"os/exec"
"syscall"
"testing"
"flag"
)

var (
port int
listenAddr string
bin string
)

func startServer() net.Listener {
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}

grpcServer := grpc.NewServer()
server := health.NewServer()
hv1.RegisterHealthServer(grpcServer, server)

server.SetServingStatus("foo", hv1.HealthCheckResponse_SERVING)
server.SetServingStatus("bar", hv1.HealthCheckResponse_NOT_SERVING)
go grpcServer.Serve(listener)
return listener
}

func init() {
flag.IntVar(&port, "stub-port", 54321, "port for the stub server")
flag.StringVar(&bin, "gprobe", "", "path to the gprobe binary")
}

func TestMain(m *testing.M) {
flag.Parse()
listenAddr = fmt.Sprintf("%s:%d", "localhost", port)

lis := startServer()
result := 0
defer func() {
lis.Close()
os.Exit(result)
}()

result = m.Run()
}

func TestShouldReturnServingForRunningServer(t *testing.T) {
stdout, stderr, exitcode := runBin(t, listenAddr)

assert.Equal(t, 0, exitcode)
assert.Equal(t, "SERVING\n", stdout)
assert.Empty(t, stderr)
}

func TestShouldFailIfServerIsNotListening(t *testing.T) {
stdout, stderr, exitcode := runBin(t, "nosuchhost:1234")

assert.Equal(t, 127, exitcode)
assert.Empty(t, stdout)
assert.Contains(t, stderr, "error", "should print status to STDOUT")
}

func TestShouldReturnServingForHealthyService(t *testing.T) {
stdout, stderr, exitcode := runBin(t, listenAddr, "foo")

assert.Equal(t, 0, exitcode)
assert.Equal(t, "SERVING\n", stdout)
assert.Empty(t, stderr)
}

func TestShouldReturnNotServingForUnhealthyService(t *testing.T) {
stdout, stderr, exitcode := runBin(t, listenAddr, "bar")

assert.Equal(t, 2, exitcode)
assert.Equal(t, "NOT_SERVING\n", stdout)
assert.Contains(t, stderr, "health-check failed")
}

func TestShouldNotFailForUnhealthyServiceIfNoFailIsSet(t *testing.T) {
stdout, stderr, exitcode := runBin(t, "--no-fail", listenAddr, "bar")

assert.Equal(t, 0, exitcode)
assert.Equal(t, "NOT_SERVING\n", stdout)
assert.Empty(t, stderr)
}

func TestShouldFailIfServiceHealthCheckIsNotRegistered(t *testing.T) {
stdout, stderr, exitcode := runBin(t, listenAddr, "non_registered_service")

assert.Equal(t, 127, exitcode)
assert.Empty(t, stdout)
assert.Contains(t, stderr, "NotFound")
}

func runBin(t *testing.T, args ...string) (stdout string, stderr string, exitcode int) {
gprobe := exec.Command(bin, args...)
stdoutPipe, _ := gprobe.StdoutPipe()
stderrPipe, _ := gprobe.StderrPipe()

err := gprobe.Start()
if err != nil {
t.Error(err)
}

stdout = readPipe(t, stdoutPipe)
stderr = readPipe(t, stderrPipe)
exitcode = waitForExitCode(t, gprobe)

return
}

func readPipe(t *testing.T, reader io.Reader) string {
buf := new(bytes.Buffer)
_, err := io.Copy(buf, reader)
if err != nil {
t.Error(err)
}
return buf.String()
}

func waitForExitCode(t *testing.T, cmd *exec.Cmd) (exitcode int) {
err := cmd.Wait()
if err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
exitcode = status.ExitStatus()
}
} else {
exitcode = -1
t.Error(err)
}
}
return
}


Loading

0 comments on commit a4972e2

Please sign in to comment.
-