Refactor testing into docker file

This means a decent scale refactor but ends with our testing being much simpler

	Key changes:
		* single Dockerfile for all services
		* tests moved into docker up so don't need to be run locally
		* configurations moved to environment
This commit is contained in:
Marc Di Luzio 2020-06-11 18:16:11 +01:00
parent 99da6c5d67
commit 14424c16ca
13 changed files with 171 additions and 107 deletions

View file

@ -6,9 +6,11 @@ COPY . .
RUN go mod download RUN go mod download
# For /usr/share/dict/words # For /usr/share/dict/words
RUN apt-get update && apt-get install -y wamerican RUN apt-get -q update && apt-get -qy install wamerican
# Build both executables
RUN go build -o rove-server -ldflags="-X 'github.com/mdiluz/rove/pkg/version.Version=$(git describe --always --long --dirty --tags)'" cmd/rove-server/main.go RUN go build -o rove-server -ldflags="-X 'github.com/mdiluz/rove/pkg/version.Version=$(git describe --always --long --dirty --tags)'" cmd/rove-server/main.go
RUN go build -o rove-accountant -ldflags="-X 'github.com/mdiluz/rove/pkg/version.Version=$(git describe --always --long --dirty --tags)'" cmd/rove-accountant/main.go
CMD [ "./rove-server" ] CMD [ "./rove-server" ]

View file

@ -12,22 +12,9 @@ gen:
protoc --proto_path pkg/accounts --go_out=plugins=grpc:pkg/accounts/ --go_opt=paths=source_relative pkg/accounts/accounts.proto protoc --proto_path pkg/accounts --go_out=plugins=grpc:pkg/accounts/ --go_opt=paths=source_relative pkg/accounts/accounts.proto
test: test:
go mod download docker-compose up --build --exit-code-from=rove-tests --abort-on-container-exit
go build ./...
# Run the server and shut it down again to ensure our docker-compose works # TODO: Collect and convert the coverage data to html
ROVE_ARGS="--quit 1" docker-compose up --build --exit-code-from=rove-server --abort-on-container-exit # go tool cover -html=/tmp/c.out -o /tmp/coverage.html
# Run the server and shut it down again to ensure our docker-compose works .PHONY: install test
docker-compose up -d
# Run tests with coverage and integration tags
go test -v ./... --tags=integration -cover -coverprofile=/tmp/c.out -count 1
#
docker-compose down
# Convert the coverage data to html
go tool cover -html=/tmp/c.out -o /tmp/coverage.html
.PHONY: install test

View file

@ -1,11 +0,0 @@
FROM golang:latest
LABEL maintainer="Marc Di Luzio <marc.diluzio@gmail.com>"
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o rove-accountant -ldflags="-X 'github.com/mdiluz/rove/pkg/version.Version=$(git describe --always --long --dirty --tags)'" cmd/rove-accountant/main.go
CMD [ "./rove-accountant" ]

View file

@ -2,7 +2,6 @@ package main
import ( import (
"context" "context"
"flag"
"fmt" "fmt"
"log" "log"
"net" "net"
@ -17,8 +16,8 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
var address = flag.String("address", "", "port to server the accountant on") var address = os.Getenv("HOST_ADDRESS")
var data = flag.String("data", os.TempDir(), "path to persistent data") var data = os.Getenv("DATA_PATH")
// accountantServer is the internal object to manage the requests // accountantServer is the internal object to manage the requests
type accountantServer struct { type accountantServer struct {
@ -83,14 +82,12 @@ func (a *accountantServer) GetValue(_ context.Context, in *accounts.DataKey) (*a
// main // main
func main() { func main() {
flag.Parse()
// Verify the input // Verify the input
if len(*address) == 0 { if len(address) == 0 {
log.Fatal("No address set with --address") log.Fatal("No address set with $HOST_ADDRESS")
} }
persistence.SetPath(*data) persistence.SetPath(data)
// Initialise and load the accountant // Initialise and load the accountant
accountant := &internal.Accountant{} accountant := &internal.Accountant{}
@ -99,7 +96,7 @@ func main() {
} }
// Set up the RPC server and register // Set up the RPC server and register
lis, err := net.Listen("tcp", *address) lis, err := net.Listen("tcp", address)
if err != nil { if err != nil {
log.Fatalf("failed to listen: %v", err) log.Fatalf("failed to listen: %v", err)
} }
@ -118,7 +115,7 @@ func main() {
}() }()
// Serve the RPC server // Serve the RPC server
fmt.Printf("Serving accountant on %s\n", *address) fmt.Printf("Serving accountant on %s\n", address)
if err := grpcServer.Serve(lis); err != nil { if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to server gRPC: %s", err) log.Fatalf("failed to server gRPC: %s", err)
} }

View file

@ -1,7 +1,8 @@
// +build integration
package internal package internal
import ( import (
"fmt"
"os" "os"
"testing" "testing"
@ -11,30 +12,17 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// To be set by the main function const (
var serv rove.Server defaultAddress = "localhost:80"
)
func TestMain(m *testing.M) { var serv = func() rove.Server {
s := NewServer() var address = os.Getenv("ROVE_SERVER_ADDRESS")
if err := s.Initialise(true); err != nil { if len(address) == 0 {
fmt.Println(err) address = defaultAddress
os.Exit(1)
} }
return rove.Server(address)
serv = rove.Server(s.Addr()) }()
go s.Run()
fmt.Printf("Test server hosted on %s", serv)
code := m.Run()
if err := s.StopAndClose(); err != nil {
fmt.Println(err)
os.Exit(1)
}
os.Exit(code)
}
func TestServer_Status(t *testing.T) { func TestServer_Status(t *testing.T) {
status, err := serv.Status() status, err := serv.Status()

View file

@ -6,11 +6,13 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/mdiluz/rove/pkg/accounts" "github.com/mdiluz/rove/pkg/accounts"
"github.com/mdiluz/rove/pkg/rove" "github.com/mdiluz/rove/pkg/rove"
"github.com/mdiluz/rove/pkg/version" "github.com/mdiluz/rove/pkg/version"
"google.golang.org/grpc"
) )
// Handler describes a function that handles any incoming request and can respond // Handler describes a function that handles any incoming request and can respond
@ -87,8 +89,10 @@ func HandleRegister(s *Server, vars map[string]string, b io.ReadCloser, w io.Wri
response.Error = "Cannot register empty name" response.Error = "Cannot register empty name"
} }
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
reg := accounts.RegisterInfo{Name: data.Name} reg := accounts.RegisterInfo{Name: data.Name}
if acc, err := s.accountant.Register(context.Background(), &reg); err != nil { if acc, err := s.accountant.Register(ctx, &reg, grpc.WaitForReady(true)); err != nil {
response.Error = err.Error() response.Error = err.Error()
} else if !acc.Success { } else if !acc.Success {
@ -125,11 +129,13 @@ func HandleCommand(s *Server, vars map[string]string, b io.ReadCloser, w io.Writ
} }
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
key := accounts.DataKey{Account: id, Key: "rover"} key := accounts.DataKey{Account: id, Key: "rover"}
if len(id) == 0 { if len(id) == 0 {
response.Error = "No account ID provided" response.Error = "No account ID provided"
} else if resp, err := s.accountant.GetValue(context.Background(), &key); err != nil { } else if resp, err := s.accountant.GetValue(ctx, &key); err != nil {
response.Error = fmt.Sprintf("Provided account has no rover: %s", err) response.Error = fmt.Sprintf("Provided account has no rover: %s", err)
} else if !resp.Success { } else if !resp.Success {
@ -155,12 +161,14 @@ func HandleRadar(s *Server, vars map[string]string, b io.ReadCloser, w io.Writer
Success: false, Success: false,
} }
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
id := vars["account"] id := vars["account"]
key := accounts.DataKey{Account: id, Key: "rover"} key := accounts.DataKey{Account: id, Key: "rover"}
if len(id) == 0 { if len(id) == 0 {
response.Error = "No account ID provided" response.Error = "No account ID provided"
} else if resp, err := s.accountant.GetValue(context.Background(), &key); err != nil { } else if resp, err := s.accountant.GetValue(ctx, &key); err != nil {
response.Error = fmt.Sprintf("Provided account has no rover: %s", err) response.Error = fmt.Sprintf("Provided account has no rover: %s", err)
} else if !resp.Success { } else if !resp.Success {
@ -191,12 +199,14 @@ func HandleRover(s *Server, vars map[string]string, b io.ReadCloser, w io.Writer
Success: false, Success: false,
} }
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
id := vars["account"] id := vars["account"]
key := accounts.DataKey{Account: id, Key: "rover"} key := accounts.DataKey{Account: id, Key: "rover"}
if len(id) == 0 { if len(id) == 0 {
response.Error = "No account ID provided" response.Error = "No account ID provided"
} else if resp, err := s.accountant.GetValue(context.Background(), &key); err != nil { } else if resp, err := s.accountant.GetValue(ctx, &key); err != nil {
response.Error = fmt.Sprintf("Provided account has no rover: %s", err) response.Error = fmt.Sprintf("Provided account has no rover: %s", err)
} else if !resp.Success { } else if !resp.Success {

View file

@ -1,3 +1,5 @@
// +build integration
package internal package internal
import ( import (
@ -9,6 +11,7 @@ import (
"path" "path"
"testing" "testing"
"github.com/google/uuid"
"github.com/mdiluz/rove/pkg/accounts" "github.com/mdiluz/rove/pkg/accounts"
"github.com/mdiluz/rove/pkg/game" "github.com/mdiluz/rove/pkg/game"
"github.com/mdiluz/rove/pkg/rove" "github.com/mdiluz/rove/pkg/rove"
@ -39,7 +42,7 @@ func TestHandleStatus(t *testing.T) {
} }
func TestHandleRegister(t *testing.T) { func TestHandleRegister(t *testing.T) {
data := rove.RegisterData{Name: "one"} data := rove.RegisterData{Name: uuid.New().String()}
b, err := json.Marshal(data) b, err := json.Marshal(data)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@ -57,22 +60,24 @@ func TestHandleRegister(t *testing.T) {
json.NewDecoder(response.Body).Decode(&status) json.NewDecoder(response.Body).Decode(&status)
if status.Success != true { if status.Success != true {
t.Errorf("got false for /register") t.Errorf("got false for /register: %s", status.Error)
} }
} }
func TestHandleCommand(t *testing.T) { func TestHandleCommand(t *testing.T) {
name := uuid.New().String()
s := NewServer() s := NewServer()
s.Initialise(false) // Leave the world empty with no obstacles s.Initialise(false) // Leave the world empty with no obstacles
reg := accounts.RegisterInfo{Name: "test"} reg := accounts.RegisterInfo{Name: name}
acc, err := s.accountant.Register(context.Background(), &reg) acc, err := s.accountant.Register(context.Background(), &reg)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, acc.Success) assert.NotNil(t, acc)
assert.True(t, acc.Success, acc.Error)
assert.NoError(t, err, "Error registering account") assert.NoError(t, err, "Error registering account")
// Spawn the rover rover for the account // Spawn the rover rover for the account
_, inst, err := s.SpawnRoverForAccount("test") _, inst, err := s.SpawnRoverForAccount(name)
assert.NoError(t, s.world.WarpRover(inst, vector.Vector{})) assert.NoError(t, s.world.WarpRover(inst, vector.Vector{}))
attribs, err := s.world.RoverAttributes(inst) attribs, err := s.world.RoverAttributes(inst)
@ -91,7 +96,7 @@ func TestHandleCommand(t *testing.T) {
b, err := json.Marshal(data) b, err := json.Marshal(data)
assert.NoError(t, err, "Error marshalling data") assert.NoError(t, err, "Error marshalling data")
request, _ := http.NewRequest(http.MethodPost, path.Join("/", "test", "/command"), bytes.NewReader(b)) request, _ := http.NewRequest(http.MethodPost, path.Join("/", name, "/command"), bytes.NewReader(b))
response := httptest.NewRecorder() response := httptest.NewRecorder()
s.router.ServeHTTP(response, request) s.router.ServeHTTP(response, request)
@ -118,16 +123,17 @@ func TestHandleCommand(t *testing.T) {
} }
func TestHandleRadar(t *testing.T) { func TestHandleRadar(t *testing.T) {
name := uuid.New().String()
s := NewServer() s := NewServer()
s.Initialise(false) // Spawn a clean world s.Initialise(false) // Spawn a clean world
reg := accounts.RegisterInfo{Name: "test"} reg := accounts.RegisterInfo{Name: name}
acc, err := s.accountant.Register(context.Background(), &reg) acc, err := s.accountant.Register(context.Background(), &reg)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, acc.Success) assert.True(t, acc.Success, acc.Error)
assert.NoError(t, err, "Error registering account") assert.NoError(t, err, "Error registering account")
// Spawn the rover rover for the account // Spawn the rover rover for the account
attrib, id, err := s.SpawnRoverForAccount("test") attrib, id, err := s.SpawnRoverForAccount(name)
assert.NoError(t, err) assert.NoError(t, err)
// Warp this rover to 0,0 // Warp this rover to 0,0
@ -143,7 +149,7 @@ func TestHandleRadar(t *testing.T) {
assert.NoError(t, s.world.Atlas.SetTile(rockPos, game.TileRock)) assert.NoError(t, s.world.Atlas.SetTile(rockPos, game.TileRock))
assert.NoError(t, s.world.Atlas.SetTile(emptyPos, game.TileEmpty)) assert.NoError(t, s.world.Atlas.SetTile(emptyPos, game.TileEmpty))
request, _ := http.NewRequest(http.MethodGet, path.Join("/", "test", "/radar"), nil) request, _ := http.NewRequest(http.MethodGet, path.Join("/", name, "/radar"), nil)
response := httptest.NewRecorder() response := httptest.NewRecorder()
s.router.ServeHTTP(response, request) s.router.ServeHTTP(response, request)
@ -175,18 +181,19 @@ func TestHandleRadar(t *testing.T) {
} }
func TestHandleRover(t *testing.T) { func TestHandleRover(t *testing.T) {
name := uuid.New().String()
s := NewServer() s := NewServer()
s.Initialise(true) s.Initialise(true)
reg := accounts.RegisterInfo{Name: "test"} reg := accounts.RegisterInfo{Name: name}
acc, err := s.accountant.Register(context.Background(), &reg) acc, err := s.accountant.Register(context.Background(), &reg)
assert.NoError(t, err) assert.NoError(t, err)
assert.True(t, acc.Success) assert.True(t, acc.Success, acc.Error)
// Spawn one rover for the account // Spawn one rover for the account
attribs, _, err := s.SpawnRoverForAccount("test") attribs, _, err := s.SpawnRoverForAccount(name)
assert.NoError(t, err) assert.NoError(t, err)
request, _ := http.NewRequest(http.MethodGet, path.Join("/", "test", "/rover"), nil) request, _ := http.NewRequest(http.MethodGet, path.Join("/", name, "/rover"), nil)
response := httptest.NewRecorder() response := httptest.NewRecorder()
s.router.ServeHTTP(response, request) s.router.ServeHTTP(response, request)

View file

@ -3,11 +3,11 @@ package internal
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"flag"
"fmt" "fmt"
"log" "log"
"net" "net"
"net/http" "net/http"
"os"
"sync" "sync"
"time" "time"
@ -20,7 +20,7 @@ import (
"google.golang.org/grpc" "google.golang.org/grpc"
) )
var accountantAddress = flag.String("accountant", "", "address of the accountant to connect to") var accountantAddress = os.Getenv("ACCOUNTANT_ADDRESS")
const ( const (
// PersistentData will allow the server to load and save it's state // PersistentData will allow the server to load and save it's state
@ -116,12 +116,12 @@ func (s *Server) Initialise(fillWorld bool) (err error) {
s.sync.Add(1) s.sync.Add(1)
// Connect to the accountant // Connect to the accountant
fmt.Printf("Dialing accountant on %s\n", *accountantAddress) fmt.Printf("Dialing accountant on %s\n", accountantAddress)
clientConn, err := grpc.Dial(*accountantAddress, grpc.WithInsecure()) s.clientConn, err = grpc.Dial(accountantAddress, grpc.WithInsecure())
if err != nil { if err != nil {
return err return err
} }
s.accountant = accounts.NewAccountantClient(clientConn) s.accountant = accounts.NewAccountantClient(s.clientConn)
// Spawn a border on the default world // Spawn a border on the default world
if err := s.world.SpawnWorld(fillWorld); err != nil { if err := s.world.SpawnWorld(fillWorld); err != nil {
@ -139,6 +139,7 @@ func (s *Server) Initialise(fillWorld bool) (err error) {
} }
// Start the listen // Start the listen
fmt.Printf("Listening on %s\n", s.server.Addr)
if s.listener, err = net.Listen("tcp", s.server.Addr); err != nil { if s.listener, err = net.Listen("tcp", s.server.Addr); err != nil {
return err return err
} }

View file

@ -3,8 +3,10 @@ package main
import ( import (
"flag" "flag"
"fmt" "fmt"
"log"
"os" "os"
"os/signal" "os/signal"
"strconv"
"syscall" "syscall"
"time" "time"
@ -15,9 +17,15 @@ import (
var ver = flag.Bool("version", false, "Display version number") var ver = flag.Bool("version", false, "Display version number")
var quit = flag.Int("quit", 0, "Quit after n seconds, useful for testing") var quit = flag.Int("quit", 0, "Quit after n seconds, useful for testing")
var address = flag.String("address", "", "The address to host on, automatically selected if empty")
var data = flag.String("data", "", "Directory to store persistant data, no storage if empty") // Address to host the server on, automatically selected if empty
var tick = flag.Int("tick", 5, "Number of minutes per server tick (0 for no tick)") var address = os.Getenv("HOST_ADDRESS")
// Path for persistent storage
var data = os.Getenv("DATA_PATH")
// The tick rate of the server in seconds
var tick = os.Getenv("TICK_RATE")
func InnerMain() { func InnerMain() {
flag.Parse() flag.Parse()
@ -31,13 +39,23 @@ func InnerMain() {
fmt.Printf("Initialising version %s...\n", version.Version) fmt.Printf("Initialising version %s...\n", version.Version)
// Set the persistence path // Set the persistence path
persistence.SetPath(*data) persistence.SetPath(data)
// Convert the tick rate
tickRate := 5
if len(tick) > 0 {
var err error
tickRate, err = strconv.Atoi(tick)
if err != nil {
log.Fatalf("TICK_RATE not set to valid int: %s", err)
}
}
// Create the server data // Create the server data
s := internal.NewServer( s := internal.NewServer(
internal.OptionAddress(*address), internal.OptionAddress(address),
internal.OptionPersistentData(), internal.OptionPersistentData(),
internal.OptionTick(*tick)) internal.OptionTick(tickRate))
// Initialise the server // Initialise the server
if err := s.Initialise(true); err != nil { if err := s.Initialise(true); err != nil {
@ -74,6 +92,5 @@ func InnerMain() {
} }
func main() { func main() {
flag.Parse()
InnerMain() InnerMain()
} }

View file

@ -12,9 +12,17 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
var address = "localhost:80" const (
defaultAddress = "localhost:80"
)
func Test_InnerMain(t *testing.T) { func Test_InnerMain(t *testing.T) {
var address = os.Getenv("ROVE_SERVER_ADDRESS")
if len(address) == 0 {
address = defaultAddress
}
// Set up the flags to act locally and use a temporary file // Set up the flags to act locally and use a temporary file
flag.Set("data", path.Join(os.TempDir(), uuid.New().String())) flag.Set("data", path.Join(os.TempDir(), uuid.New().String()))

View file

@ -4,24 +4,45 @@ volumes:
persistent-data: persistent-data:
services: services:
rove-server:
build:
context: .
dockerfile: cmd/rove-server/Dockerfile
image: rove-server:latest
ports:
- "80:80"
command: ./rove-server --address ":80" --data=/mnt/rove-server ${ROVE_ARGS} --accountant "rove-accountant:8081"
volumes:
- persistent-data:/mnt/rove-server:rw
rove-accountant: rove-accountant:
build: build:
context: . context: .
dockerfile: cmd/rove-accountant/Dockerfile dockerfile: Dockerfile
image: rove-accountant:latest image: rove:latest
ports: ports:
- "8081:8081" - "8081:8081"
command: ./rove-accountant --address ":8081" --data=/mnt/rove-server environment:
- HOST_ADDRESS=:8081
- DATA_PATH=/mnt/rove-server
volumes: volumes:
- persistent-data:/mnt/rove-server:rw - persistent-data:/mnt/rove-server:rw
command: [ ./rove-accountant ]
rove-server:
depends_on: [ rove-accountant ]
build:
context: .
dockerfile: Dockerfile
image: rove:latest
ports:
- "80:80"
environment:
- HOST_ADDRESS=:80
- DATA_PATH=/mnt/rove-server
- ACCOUNTANT_ADDRESS=rove-accountant:8081
volumes:
- persistent-data:/mnt/rove-server:rw
rove-tests:
depends_on: [ rove-server ]
build:
context: .
dockerfile: Dockerfile
image: rove:latest
environment:
- ACCOUNTANT_ADDRESS=rove-accountant:8081
- ROVE_SERVER_ADDRESS=rove-server:80
command: [ "go", "test", "-v", "./...", "--tags=integration", "-cover", "-coverprofile=/tmp/c.out", "-count", "1" ]

1
go.mod
View file

@ -6,6 +6,7 @@ require (
github.com/golang/protobuf v1.4.2 github.com/golang/protobuf v1.4.2
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/gorilla/mux v1.7.4 github.com/gorilla/mux v1.7.4
github.com/onsi/ginkgo v1.12.3 // indirect
github.com/robfig/cron v1.2.0 github.com/robfig/cron v1.2.0
github.com/stretchr/testify v1.6.0 github.com/stretchr/testify v1.6.0
github.com/tjarratt/babble v0.0.0-20191209142150-eecdf8c2339d github.com/tjarratt/babble v0.0.0-20191209142150-eecdf8c2339d

36
go.sum
View file

@ -8,6 +8,9 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -23,11 +26,22 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.12.3 h1:+RYp9QczoWz9zfUyLP/5SLXQVhfr6gZOoKGfQqHuLZQ=
github.com/onsi/ginkgo v1.12.3/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -45,22 +59,37 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -79,7 +108,14 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=