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:
parent
99da6c5d67
commit
14424c16ca
13 changed files with 171 additions and 107 deletions
|
@ -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" ]
|
||||||
|
|
21
Makefile
21
Makefile
|
@ -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
|
|
||||||
|
|
|
@ -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" ]
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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(), ®); err != nil {
|
if acc, err := s.accountant.Register(ctx, ®, 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 {
|
||||||
|
|
|
@ -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(), ®)
|
acc, err := s.accountant.Register(context.Background(), ®)
|
||||||
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(), ®)
|
acc, err := s.accountant.Register(context.Background(), ®)
|
||||||
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(), ®)
|
acc, err := s.accountant.Register(context.Background(), ®)
|
||||||
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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()))
|
||||||
|
|
||||||
|
|
|
@ -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
1
go.mod
|
@ -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
36
go.sum
|
@ -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=
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue