Satellite bw usage v3 121 (#547)
* protobuf for sending bandwidth agreements to satellite from storage nodes * Setup process for sending agreements * Add payer_id to db with bandwidth agreements for better sorting * Linter errors * Read agreements from PSDB * Try writing message to server * Cleanup * Basic functionality * Better error handelling * Fix test * setup config and server structure for receiving bandwidth agreements * Resolve linter issues * Optional commit for if we want to handle deletes all at once * add identity to Server, add logic for receiving bandwidth messsages * Bandwidth agreement DBX creation and integration with bw agreement endpoint Co-authored-by: Kishore <kishore@storj.io> Co-authored-by: Cam <cameron@storj.io> * protobuf for sending bandwidth agreements to satellite from storage nodes * Setup process for sending agreements * Add payer_id to db with bandwidth agreements for better sorting * Linter errors * Read agreements from PSDB * Try writing message to server * Cleanup * Basic functionality * Better error handelling * Fix test * setup config and server structure for receiving bandwidth agreements * Resolve linter issues * Optional commit for if we want to handle deletes all at once * add identity to Server, add logic for receiving bandwidth messsages * Bandwidth agreement DBX creation and integration with bw agreement endpoint Co-authored-by: Kishore <kishore@storj.io> Co-authored-by: Cam <cameron@storj.io> * added postgres create/read/delete test function Co-authored-by: kishore <kishore@storj.io Co-authored-by: cam <cameron@storj.io> * edit comment * removed sqlite3 driver from dbx * remove generated sqlite code, add dbx read limitoffset * remove getServerAndDB function, rename getDBPath to getPSQLInfo * WIP writing server endpoint test * code review changes
This commit is contained in:
parent
1129ead6ef
commit
a760e48d4d
@ -96,15 +96,15 @@ func main() {
|
||||
id := client.NewPieceID()
|
||||
|
||||
allocationData := &pb.PayerBandwidthAllocation_Data{
|
||||
SatelliteId: []byte("OhHeyThisIsAnUnrealFakeSatellite"),
|
||||
Action: pb.PayerBandwidthAllocation_PUT,
|
||||
SatelliteId: []byte("OhHeyThisIsAnUnrealFakeSatellite"),
|
||||
Action: pb.PayerBandwidthAllocation_PUT,
|
||||
}
|
||||
|
||||
|
||||
serializedAllocation, err := proto.Marshal(allocationData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
pba := &pb.PayerBandwidthAllocation{
|
||||
Data: serializedAllocation,
|
||||
}
|
||||
@ -159,15 +159,15 @@ func main() {
|
||||
}
|
||||
|
||||
allocationData := &pb.PayerBandwidthAllocation_Data{
|
||||
SatelliteId: []byte("OhHeyThisIsAnUnrealFakeSatellite"),
|
||||
Action: pb.PayerBandwidthAllocation_GET,
|
||||
SatelliteId: []byte("OhHeyThisIsAnUnrealFakeSatellite"),
|
||||
Action: pb.PayerBandwidthAllocation_GET,
|
||||
}
|
||||
|
||||
|
||||
serializedAllocation, err := proto.Marshal(allocationData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
pba := &pb.PayerBandwidthAllocation{
|
||||
Data: serializedAllocation,
|
||||
}
|
||||
|
1
go.sum
1
go.sum
@ -171,6 +171,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kshvakov/clickhouse v1.3.4/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE=
|
||||
github.com/lib/pq v0.0.0-20180523175426-90697d60dd84/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/loov/hrtime v0.0.0-20180911122900-a9e82bc6c180 h1:kLwg5eA/kaWQ/RwANTH7Gg+VdxmdjbcSWyaS/1VQGkA=
|
||||
|
@ -1,66 +0,0 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package agreementreceiver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"storj.io/storj/pkg/pb"
|
||||
"storj.io/storj/pkg/provider"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Server is an implementation of the pb.BandwidthServer interface
|
||||
type Server struct {
|
||||
// DB *dbx.DB
|
||||
identity *provider.FullIdentity
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewServer initializes a Server struct
|
||||
func NewServer(source string, fi *provider.FullIdentity, logger *zap.Logger) (*Server, error) {
|
||||
//TODO: open dbx postgres database and pass to Server
|
||||
return &Server{
|
||||
identity: fi,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BandwidthAgreements receives and stores bandwidth agreements from storage nodes
|
||||
func (s *Server) BandwidthAgreements(stream pb.Bandwidth_BandwidthAgreementsServer) (err error) {
|
||||
ctx := stream.Context()
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
ch := make(chan *pb.RenterBandwidthAllocation, 1)
|
||||
errch := make(chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
msg, err := stream.Recv()
|
||||
if err != nil {
|
||||
errch <- err
|
||||
return
|
||||
}
|
||||
ch <- msg
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-errch:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case agreement := <-ch:
|
||||
go func() {
|
||||
fmt.Println(agreement)
|
||||
//TODO: write to DB
|
||||
//err = s.DB.WriteAgreement(agreement)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
11
pkg/bwagreement/common.go
Normal file
11
pkg/bwagreement/common.go
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package bwagreement
|
||||
|
||||
import (
|
||||
"github.com/zeebo/errs"
|
||||
)
|
||||
|
||||
// Error is the default bwagreement errs class
|
||||
var Error = errs.Class("bwagreement error")
|
@ -1,7 +1,7 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package agreementreceiver
|
||||
package bwagreement
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -20,13 +20,14 @@ var (
|
||||
// Config is a configuration struct that is everything you need to start an
|
||||
// agreement receiver responsibility
|
||||
type Config struct {
|
||||
DatabaseURL string `help:"the database connection string to use" default:"$CONFDIR/agreements.db"`
|
||||
DatabaseURL string `help:"the database connection string to use" default:"$CONFDIR/agreements.db"`
|
||||
DatabaseDriver string `help:"the database driver to use" default:"postgres"`
|
||||
}
|
||||
|
||||
// Run implements the provider.Responsibility interface
|
||||
func (c Config) Run(ctx context.Context, server *provider.Provider) (err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
ns, err := NewServer(c.DatabaseURL, server.Identity(), zap.L())
|
||||
ns, err := NewServer(c.DatabaseDriver, c.DatabaseURL, zap.L())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
21
pkg/bwagreement/dbx/bwagreement.dbx
Normal file
21
pkg/bwagreement/dbx/bwagreement.dbx
Normal file
@ -0,0 +1,21 @@
|
||||
// dbx.v1 golang bwagreement.dbx .
|
||||
model bwagreement (
|
||||
key signature
|
||||
|
||||
field signature blob
|
||||
|
||||
field data blob
|
||||
|
||||
field created_at timestamp ( autoinsert )
|
||||
)
|
||||
|
||||
create bwagreement ( )
|
||||
delete bwagreement ( where bwagreement.signature = ? )
|
||||
read one (
|
||||
select bwagreement
|
||||
where bwagreement.signature = ?
|
||||
)
|
||||
read limitoffset (
|
||||
select bwagreement
|
||||
)
|
||||
|
838
pkg/bwagreement/dbx/bwagreement.dbx.go
Normal file
838
pkg/bwagreement/dbx/bwagreement.dbx.go
Normal file
@ -0,0 +1,838 @@
|
||||
// AUTOGENERATED BY gopkg.in/spacemonkeygo/dbx.v1
|
||||
// DO NOT EDIT.
|
||||
|
||||
package bwagreement
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
// Prevent conditional imports from causing build failures
|
||||
var _ = strconv.Itoa
|
||||
var _ = strings.LastIndex
|
||||
var _ = fmt.Sprint
|
||||
var _ sync.Mutex
|
||||
|
||||
var (
|
||||
WrapErr = func(err *Error) error { return err }
|
||||
Logger func(format string, args ...interface{})
|
||||
|
||||
errTooManyRows = errors.New("too many rows")
|
||||
errUnsupportedDriver = errors.New("unsupported driver")
|
||||
errEmptyUpdate = errors.New("empty update")
|
||||
)
|
||||
|
||||
func logError(format string, args ...interface{}) {
|
||||
if Logger != nil {
|
||||
Logger(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
type ErrorCode int
|
||||
|
||||
const (
|
||||
ErrorCode_Unknown ErrorCode = iota
|
||||
ErrorCode_UnsupportedDriver
|
||||
ErrorCode_NoRows
|
||||
ErrorCode_TxDone
|
||||
ErrorCode_TooManyRows
|
||||
ErrorCode_ConstraintViolation
|
||||
ErrorCode_EmptyUpdate
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
Err error
|
||||
Code ErrorCode
|
||||
Driver string
|
||||
Constraint string
|
||||
QuerySuffix string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
func wrapErr(e *Error) error {
|
||||
if WrapErr == nil {
|
||||
return e
|
||||
}
|
||||
return WrapErr(e)
|
||||
}
|
||||
|
||||
func makeErr(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
e := &Error{Err: err}
|
||||
switch err {
|
||||
case sql.ErrNoRows:
|
||||
e.Code = ErrorCode_NoRows
|
||||
case sql.ErrTxDone:
|
||||
e.Code = ErrorCode_TxDone
|
||||
}
|
||||
return wrapErr(e)
|
||||
}
|
||||
|
||||
func unsupportedDriver(driver string) error {
|
||||
return wrapErr(&Error{
|
||||
Err: errUnsupportedDriver,
|
||||
Code: ErrorCode_UnsupportedDriver,
|
||||
Driver: driver,
|
||||
})
|
||||
}
|
||||
|
||||
func emptyUpdate() error {
|
||||
return wrapErr(&Error{
|
||||
Err: errEmptyUpdate,
|
||||
Code: ErrorCode_EmptyUpdate,
|
||||
})
|
||||
}
|
||||
|
||||
func tooManyRows(query_suffix string) error {
|
||||
return wrapErr(&Error{
|
||||
Err: errTooManyRows,
|
||||
Code: ErrorCode_TooManyRows,
|
||||
QuerySuffix: query_suffix,
|
||||
})
|
||||
}
|
||||
|
||||
func constraintViolation(err error, constraint string) error {
|
||||
return wrapErr(&Error{
|
||||
Err: err,
|
||||
Code: ErrorCode_ConstraintViolation,
|
||||
Constraint: constraint,
|
||||
})
|
||||
}
|
||||
|
||||
type driver interface {
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
QueryRow(query string, args ...interface{}) *sql.Row
|
||||
}
|
||||
|
||||
var (
|
||||
notAPointer = errors.New("destination not a pointer")
|
||||
lossyConversion = errors.New("lossy conversion")
|
||||
)
|
||||
|
||||
type DB struct {
|
||||
*sql.DB
|
||||
dbMethods
|
||||
|
||||
Hooks struct {
|
||||
Now func() time.Time
|
||||
}
|
||||
}
|
||||
|
||||
func Open(driver, source string) (db *DB, err error) {
|
||||
var sql_db *sql.DB
|
||||
switch driver {
|
||||
case "postgres":
|
||||
sql_db, err = openpostgres(source)
|
||||
default:
|
||||
return nil, unsupportedDriver(driver)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, makeErr(err)
|
||||
}
|
||||
defer func(sql_db *sql.DB) {
|
||||
if err != nil {
|
||||
sql_db.Close()
|
||||
}
|
||||
}(sql_db)
|
||||
|
||||
if err := sql_db.Ping(); err != nil {
|
||||
return nil, makeErr(err)
|
||||
}
|
||||
|
||||
db = &DB{
|
||||
DB: sql_db,
|
||||
}
|
||||
db.Hooks.Now = time.Now
|
||||
|
||||
switch driver {
|
||||
case "postgres":
|
||||
db.dbMethods = newpostgres(db)
|
||||
default:
|
||||
return nil, unsupportedDriver(driver)
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func (obj *DB) Close() (err error) {
|
||||
return obj.makeErr(obj.DB.Close())
|
||||
}
|
||||
|
||||
func (obj *DB) Open(ctx context.Context) (*Tx, error) {
|
||||
tx, err := obj.DB.Begin()
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
|
||||
return &Tx{
|
||||
Tx: tx,
|
||||
txMethods: obj.wrapTx(tx),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (obj *DB) NewRx() *Rx {
|
||||
return &Rx{db: obj}
|
||||
}
|
||||
|
||||
func DeleteAll(ctx context.Context, db *DB) (int64, error) {
|
||||
tx, err := db.Open(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer func() {
|
||||
if err == nil {
|
||||
err = db.makeErr(tx.Commit())
|
||||
return
|
||||
}
|
||||
|
||||
if err_rollback := tx.Rollback(); err_rollback != nil {
|
||||
logError("delete-all: rollback failed: %v", db.makeErr(err_rollback))
|
||||
}
|
||||
}()
|
||||
return tx.deleteAll(ctx)
|
||||
}
|
||||
|
||||
type Tx struct {
|
||||
Tx *sql.Tx
|
||||
txMethods
|
||||
}
|
||||
|
||||
type dialectTx struct {
|
||||
tx *sql.Tx
|
||||
}
|
||||
|
||||
func (tx *dialectTx) Commit() (err error) {
|
||||
return makeErr(tx.tx.Commit())
|
||||
}
|
||||
|
||||
func (tx *dialectTx) Rollback() (err error) {
|
||||
return makeErr(tx.tx.Rollback())
|
||||
}
|
||||
|
||||
type postgresImpl struct {
|
||||
db *DB
|
||||
dialect __sqlbundle_postgres
|
||||
driver driver
|
||||
}
|
||||
|
||||
func (obj *postgresImpl) Rebind(s string) string {
|
||||
return obj.dialect.Rebind(s)
|
||||
}
|
||||
|
||||
func (obj *postgresImpl) logStmt(stmt string, args ...interface{}) {
|
||||
postgresLogStmt(stmt, args...)
|
||||
}
|
||||
|
||||
func (obj *postgresImpl) makeErr(err error) error {
|
||||
constraint, ok := obj.isConstraintError(err)
|
||||
if ok {
|
||||
return constraintViolation(err, constraint)
|
||||
}
|
||||
return makeErr(err)
|
||||
}
|
||||
|
||||
type postgresDB struct {
|
||||
db *DB
|
||||
*postgresImpl
|
||||
}
|
||||
|
||||
func newpostgres(db *DB) *postgresDB {
|
||||
return &postgresDB{
|
||||
db: db,
|
||||
postgresImpl: &postgresImpl{
|
||||
db: db,
|
||||
driver: db.DB,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (obj *postgresDB) Schema() string {
|
||||
return `CREATE TABLE bwagreements (
|
||||
signature bytea NOT NULL,
|
||||
data bytea NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
PRIMARY KEY ( signature )
|
||||
);`
|
||||
}
|
||||
|
||||
func (obj *postgresDB) wrapTx(tx *sql.Tx) txMethods {
|
||||
return &postgresTx{
|
||||
dialectTx: dialectTx{tx: tx},
|
||||
postgresImpl: &postgresImpl{
|
||||
db: obj.db,
|
||||
driver: tx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type postgresTx struct {
|
||||
dialectTx
|
||||
*postgresImpl
|
||||
}
|
||||
|
||||
func postgresLogStmt(stmt string, args ...interface{}) {
|
||||
// TODO: render placeholders
|
||||
if Logger != nil {
|
||||
out := fmt.Sprintf("stmt: %s\nargs: %v\n", stmt, pretty(args))
|
||||
Logger(out)
|
||||
}
|
||||
}
|
||||
|
||||
type pretty []interface{}
|
||||
|
||||
func (p pretty) Format(f fmt.State, c rune) {
|
||||
fmt.Fprint(f, "[")
|
||||
nextval:
|
||||
for i, val := range p {
|
||||
if i > 0 {
|
||||
fmt.Fprint(f, ", ")
|
||||
}
|
||||
rv := reflect.ValueOf(val)
|
||||
if rv.Kind() == reflect.Ptr {
|
||||
if rv.IsNil() {
|
||||
fmt.Fprint(f, "NULL")
|
||||
continue
|
||||
}
|
||||
val = rv.Elem().Interface()
|
||||
}
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
fmt.Fprintf(f, "%q", v)
|
||||
case time.Time:
|
||||
fmt.Fprintf(f, "%s", v.Format(time.RFC3339Nano))
|
||||
case []byte:
|
||||
for _, b := range v {
|
||||
if !unicode.IsPrint(rune(b)) {
|
||||
fmt.Fprintf(f, "%#x", v)
|
||||
continue nextval
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(f, "%q", v)
|
||||
default:
|
||||
fmt.Fprintf(f, "%v", v)
|
||||
}
|
||||
}
|
||||
fmt.Fprint(f, "]")
|
||||
}
|
||||
|
||||
type Bwagreement struct {
|
||||
Signature []byte
|
||||
Data []byte
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func (Bwagreement) _Table() string { return "bwagreements" }
|
||||
|
||||
type Bwagreement_Update_Fields struct {
|
||||
}
|
||||
|
||||
type Bwagreement_Signature_Field struct {
|
||||
_set bool
|
||||
_value []byte
|
||||
}
|
||||
|
||||
func Bwagreement_Signature(v []byte) Bwagreement_Signature_Field {
|
||||
return Bwagreement_Signature_Field{_set: true, _value: v}
|
||||
}
|
||||
|
||||
func (f Bwagreement_Signature_Field) value() interface{} {
|
||||
if !f._set {
|
||||
return nil
|
||||
}
|
||||
return f._value
|
||||
}
|
||||
|
||||
func (Bwagreement_Signature_Field) _Column() string { return "signature" }
|
||||
|
||||
type Bwagreement_Data_Field struct {
|
||||
_set bool
|
||||
_value []byte
|
||||
}
|
||||
|
||||
func Bwagreement_Data(v []byte) Bwagreement_Data_Field {
|
||||
return Bwagreement_Data_Field{_set: true, _value: v}
|
||||
}
|
||||
|
||||
func (f Bwagreement_Data_Field) value() interface{} {
|
||||
if !f._set {
|
||||
return nil
|
||||
}
|
||||
return f._value
|
||||
}
|
||||
|
||||
func (Bwagreement_Data_Field) _Column() string { return "data" }
|
||||
|
||||
type Bwagreement_CreatedAt_Field struct {
|
||||
_set bool
|
||||
_value time.Time
|
||||
}
|
||||
|
||||
func Bwagreement_CreatedAt(v time.Time) Bwagreement_CreatedAt_Field {
|
||||
return Bwagreement_CreatedAt_Field{_set: true, _value: v}
|
||||
}
|
||||
|
||||
func (f Bwagreement_CreatedAt_Field) value() interface{} {
|
||||
if !f._set {
|
||||
return nil
|
||||
}
|
||||
return f._value
|
||||
}
|
||||
|
||||
func (Bwagreement_CreatedAt_Field) _Column() string { return "created_at" }
|
||||
|
||||
func toUTC(t time.Time) time.Time {
|
||||
return t.UTC()
|
||||
}
|
||||
|
||||
func toDate(t time.Time) time.Time {
|
||||
// keep up the minute portion so that translations between timezones will
|
||||
// continue to reflect properly.
|
||||
return t.Truncate(time.Minute)
|
||||
}
|
||||
|
||||
//
|
||||
// runtime support for building sql statements
|
||||
//
|
||||
|
||||
type __sqlbundle_SQL interface {
|
||||
Render() string
|
||||
|
||||
private()
|
||||
}
|
||||
|
||||
type __sqlbundle_Dialect interface {
|
||||
Rebind(sql string) string
|
||||
}
|
||||
|
||||
type __sqlbundle_RenderOp int
|
||||
|
||||
const (
|
||||
__sqlbundle_NoFlatten __sqlbundle_RenderOp = iota
|
||||
__sqlbundle_NoTerminate
|
||||
)
|
||||
|
||||
func __sqlbundle_Render(dialect __sqlbundle_Dialect, sql __sqlbundle_SQL, ops ...__sqlbundle_RenderOp) string {
|
||||
out := sql.Render()
|
||||
|
||||
flatten := true
|
||||
terminate := true
|
||||
for _, op := range ops {
|
||||
switch op {
|
||||
case __sqlbundle_NoFlatten:
|
||||
flatten = false
|
||||
case __sqlbundle_NoTerminate:
|
||||
terminate = false
|
||||
}
|
||||
}
|
||||
|
||||
if flatten {
|
||||
out = __sqlbundle_flattenSQL(out)
|
||||
}
|
||||
if terminate {
|
||||
out += ";"
|
||||
}
|
||||
|
||||
return dialect.Rebind(out)
|
||||
}
|
||||
|
||||
var __sqlbundle_reSpace = regexp.MustCompile(`\s+`)
|
||||
|
||||
func __sqlbundle_flattenSQL(s string) string {
|
||||
return strings.TrimSpace(__sqlbundle_reSpace.ReplaceAllString(s, " "))
|
||||
}
|
||||
|
||||
// this type is specially named to match up with the name returned by the
|
||||
// dialect impl in the sql package.
|
||||
type __sqlbundle_postgres struct{}
|
||||
|
||||
func (p __sqlbundle_postgres) Rebind(sql string) string {
|
||||
out := make([]byte, 0, len(sql)+10)
|
||||
|
||||
j := 1
|
||||
for i := 0; i < len(sql); i++ {
|
||||
ch := sql[i]
|
||||
if ch != '?' {
|
||||
out = append(out, ch)
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, '$')
|
||||
out = append(out, strconv.Itoa(j)...)
|
||||
j++
|
||||
}
|
||||
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// this type is specially named to match up with the name returned by the
|
||||
// dialect impl in the sql package.
|
||||
type __sqlbundle_sqlite3 struct{}
|
||||
|
||||
func (s __sqlbundle_sqlite3) Rebind(sql string) string {
|
||||
return sql
|
||||
}
|
||||
|
||||
type __sqlbundle_Literal string
|
||||
|
||||
func (__sqlbundle_Literal) private() {}
|
||||
|
||||
func (l __sqlbundle_Literal) Render() string { return string(l) }
|
||||
|
||||
type __sqlbundle_Literals struct {
|
||||
Join string
|
||||
SQLs []__sqlbundle_SQL
|
||||
}
|
||||
|
||||
func (__sqlbundle_Literals) private() {}
|
||||
|
||||
func (l __sqlbundle_Literals) Render() string {
|
||||
var out bytes.Buffer
|
||||
|
||||
first := true
|
||||
for _, sql := range l.SQLs {
|
||||
if sql == nil {
|
||||
continue
|
||||
}
|
||||
if !first {
|
||||
out.WriteString(l.Join)
|
||||
}
|
||||
first = false
|
||||
out.WriteString(sql.Render())
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
type __sqlbundle_Condition struct {
|
||||
// set at compile/embed time
|
||||
Name string
|
||||
Left string
|
||||
Equal bool
|
||||
Right string
|
||||
|
||||
// set at runtime
|
||||
Null bool
|
||||
}
|
||||
|
||||
func (*__sqlbundle_Condition) private() {}
|
||||
|
||||
func (c *__sqlbundle_Condition) Render() string {
|
||||
|
||||
switch {
|
||||
case c.Equal && c.Null:
|
||||
return c.Left + " is null"
|
||||
case c.Equal && !c.Null:
|
||||
return c.Left + " = " + c.Right
|
||||
case !c.Equal && c.Null:
|
||||
return c.Left + " is not null"
|
||||
case !c.Equal && !c.Null:
|
||||
return c.Left + " != " + c.Right
|
||||
default:
|
||||
panic("unhandled case")
|
||||
}
|
||||
}
|
||||
|
||||
type __sqlbundle_Hole struct {
|
||||
// set at compiile/embed time
|
||||
Name string
|
||||
|
||||
// set at runtime
|
||||
SQL __sqlbundle_SQL
|
||||
}
|
||||
|
||||
func (*__sqlbundle_Hole) private() {}
|
||||
|
||||
func (h *__sqlbundle_Hole) Render() string { return h.SQL.Render() }
|
||||
|
||||
//
|
||||
// end runtime support for building sql statements
|
||||
//
|
||||
|
||||
func (obj *postgresImpl) Create_Bwagreement(ctx context.Context,
|
||||
bwagreement_signature Bwagreement_Signature_Field,
|
||||
bwagreement_data Bwagreement_Data_Field) (
|
||||
bwagreement *Bwagreement, err error) {
|
||||
|
||||
__now := obj.db.Hooks.Now().UTC()
|
||||
__signature_val := bwagreement_signature.value()
|
||||
__data_val := bwagreement_data.value()
|
||||
__created_at_val := __now
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("INSERT INTO bwagreements ( signature, data, created_at ) VALUES ( ?, ?, ? ) RETURNING bwagreements.signature, bwagreements.data, bwagreements.created_at")
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __signature_val, __data_val, __created_at_val)
|
||||
|
||||
bwagreement = &Bwagreement{}
|
||||
err = obj.driver.QueryRow(__stmt, __signature_val, __data_val, __created_at_val).Scan(&bwagreement.Signature, &bwagreement.Data, &bwagreement.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
return bwagreement, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *postgresImpl) Get_Bwagreement_By_Signature(ctx context.Context,
|
||||
bwagreement_signature Bwagreement_Signature_Field) (
|
||||
bwagreement *Bwagreement, err error) {
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT bwagreements.signature, bwagreements.data, bwagreements.created_at FROM bwagreements WHERE bwagreements.signature = ?")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, bwagreement_signature.value())
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
bwagreement = &Bwagreement{}
|
||||
err = obj.driver.QueryRow(__stmt, __values...).Scan(&bwagreement.Signature, &bwagreement.Data, &bwagreement.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
return bwagreement, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *postgresImpl) Limited_Bwagreement(ctx context.Context,
|
||||
limit int, offset int64) (
|
||||
rows []*Bwagreement, err error) {
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("SELECT bwagreements.signature, bwagreements.data, bwagreements.created_at FROM bwagreements LIMIT ? OFFSET ?")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values)
|
||||
|
||||
__values = append(__values, limit, offset)
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
__rows, err := obj.driver.Query(__stmt, __values...)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
defer __rows.Close()
|
||||
|
||||
for __rows.Next() {
|
||||
bwagreement := &Bwagreement{}
|
||||
err = __rows.Scan(&bwagreement.Signature, &bwagreement.Data, &bwagreement.CreatedAt)
|
||||
if err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
rows = append(rows, bwagreement)
|
||||
}
|
||||
if err := __rows.Err(); err != nil {
|
||||
return nil, obj.makeErr(err)
|
||||
}
|
||||
return rows, nil
|
||||
|
||||
}
|
||||
|
||||
func (obj *postgresImpl) Delete_Bwagreement_By_Signature(ctx context.Context,
|
||||
bwagreement_signature Bwagreement_Signature_Field) (
|
||||
deleted bool, err error) {
|
||||
|
||||
var __embed_stmt = __sqlbundle_Literal("DELETE FROM bwagreements WHERE bwagreements.signature = ?")
|
||||
|
||||
var __values []interface{}
|
||||
__values = append(__values, bwagreement_signature.value())
|
||||
|
||||
var __stmt = __sqlbundle_Render(obj.dialect, __embed_stmt)
|
||||
obj.logStmt(__stmt, __values...)
|
||||
|
||||
__res, err := obj.driver.Exec(__stmt, __values...)
|
||||
if err != nil {
|
||||
return false, obj.makeErr(err)
|
||||
}
|
||||
|
||||
__count, err := __res.RowsAffected()
|
||||
if err != nil {
|
||||
return false, obj.makeErr(err)
|
||||
}
|
||||
|
||||
return __count > 0, nil
|
||||
|
||||
}
|
||||
|
||||
func (impl postgresImpl) isConstraintError(err error) (
|
||||
constraint string, ok bool) {
|
||||
if e, ok := err.(*pq.Error); ok {
|
||||
if e.Code.Class() == "23" {
|
||||
return e.Constraint, true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
func (obj *postgresImpl) deleteAll(ctx context.Context) (count int64, err error) {
|
||||
var __res sql.Result
|
||||
var __count int64
|
||||
__res, err = obj.driver.Exec("DELETE FROM bwagreements;")
|
||||
if err != nil {
|
||||
return 0, obj.makeErr(err)
|
||||
}
|
||||
|
||||
__count, err = __res.RowsAffected()
|
||||
if err != nil {
|
||||
return 0, obj.makeErr(err)
|
||||
}
|
||||
count += __count
|
||||
|
||||
return count, nil
|
||||
|
||||
}
|
||||
|
||||
type Rx struct {
|
||||
db *DB
|
||||
tx *Tx
|
||||
}
|
||||
|
||||
func (rx *Rx) UnsafeTx(ctx context.Context) (unsafe_tx *sql.Tx, err error) {
|
||||
tx, err := rx.getTx(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx.Tx, nil
|
||||
}
|
||||
|
||||
func (rx *Rx) getTx(ctx context.Context) (tx *Tx, err error) {
|
||||
if rx.tx == nil {
|
||||
if rx.tx, err = rx.db.Open(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return rx.tx, nil
|
||||
}
|
||||
|
||||
func (rx *Rx) Rebind(s string) string {
|
||||
return rx.db.Rebind(s)
|
||||
}
|
||||
|
||||
func (rx *Rx) Commit() (err error) {
|
||||
if rx.tx != nil {
|
||||
err = rx.tx.Commit()
|
||||
rx.tx = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (rx *Rx) Rollback() (err error) {
|
||||
if rx.tx != nil {
|
||||
err = rx.tx.Rollback()
|
||||
rx.tx = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (rx *Rx) Create_Bwagreement(ctx context.Context,
|
||||
bwagreement_signature Bwagreement_Signature_Field,
|
||||
bwagreement_data Bwagreement_Data_Field) (
|
||||
bwagreement *Bwagreement, err error) {
|
||||
var tx *Tx
|
||||
if tx, err = rx.getTx(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
return tx.Create_Bwagreement(ctx, bwagreement_signature, bwagreement_data)
|
||||
|
||||
}
|
||||
|
||||
func (rx *Rx) Delete_Bwagreement_By_Signature(ctx context.Context,
|
||||
bwagreement_signature Bwagreement_Signature_Field) (
|
||||
deleted bool, err error) {
|
||||
var tx *Tx
|
||||
if tx, err = rx.getTx(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
return tx.Delete_Bwagreement_By_Signature(ctx, bwagreement_signature)
|
||||
}
|
||||
|
||||
func (rx *Rx) Get_Bwagreement_By_Signature(ctx context.Context,
|
||||
bwagreement_signature Bwagreement_Signature_Field) (
|
||||
bwagreement *Bwagreement, err error) {
|
||||
var tx *Tx
|
||||
if tx, err = rx.getTx(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
return tx.Get_Bwagreement_By_Signature(ctx, bwagreement_signature)
|
||||
}
|
||||
|
||||
func (rx *Rx) Limited_Bwagreement(ctx context.Context,
|
||||
limit int, offset int64) (
|
||||
rows []*Bwagreement, err error) {
|
||||
var tx *Tx
|
||||
if tx, err = rx.getTx(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
return tx.Limited_Bwagreement(ctx, limit, offset)
|
||||
}
|
||||
|
||||
type Methods interface {
|
||||
Create_Bwagreement(ctx context.Context,
|
||||
bwagreement_signature Bwagreement_Signature_Field,
|
||||
bwagreement_data Bwagreement_Data_Field) (
|
||||
bwagreement *Bwagreement, err error)
|
||||
|
||||
Delete_Bwagreement_By_Signature(ctx context.Context,
|
||||
bwagreement_signature Bwagreement_Signature_Field) (
|
||||
deleted bool, err error)
|
||||
|
||||
Get_Bwagreement_By_Signature(ctx context.Context,
|
||||
bwagreement_signature Bwagreement_Signature_Field) (
|
||||
bwagreement *Bwagreement, err error)
|
||||
|
||||
Limited_Bwagreement(ctx context.Context,
|
||||
limit int, offset int64) (
|
||||
rows []*Bwagreement, err error)
|
||||
}
|
||||
|
||||
type TxMethods interface {
|
||||
Methods
|
||||
|
||||
Rebind(s string) string
|
||||
Commit() error
|
||||
Rollback() error
|
||||
}
|
||||
|
||||
type txMethods interface {
|
||||
TxMethods
|
||||
|
||||
deleteAll(ctx context.Context) (int64, error)
|
||||
makeErr(err error) error
|
||||
}
|
||||
|
||||
type DBMethods interface {
|
||||
Methods
|
||||
|
||||
Schema() string
|
||||
Rebind(sql string) string
|
||||
}
|
||||
|
||||
type dbMethods interface {
|
||||
DBMethods
|
||||
|
||||
wrapTx(tx *sql.Tx) txMethods
|
||||
makeErr(err error) error
|
||||
}
|
||||
|
||||
func openpostgres(source string) (*sql.DB, error) {
|
||||
return sql.Open("postgres", source)
|
||||
}
|
8
pkg/bwagreement/dbx/bwagreement.dbx.postgres.sql
Normal file
8
pkg/bwagreement/dbx/bwagreement.dbx.postgres.sql
Normal file
@ -0,0 +1,8 @@
|
||||
-- AUTOGENERATED BY gopkg.in/spacemonkeygo/dbx.v1
|
||||
-- DO NOT EDIT
|
||||
CREATE TABLE bwagreements (
|
||||
signature bytea NOT NULL,
|
||||
data bytea NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
PRIMARY KEY ( signature )
|
||||
);
|
7
pkg/bwagreement/dbx/gen.go
Normal file
7
pkg/bwagreement/dbx/gen.go
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package bwagreement
|
||||
|
||||
//go:generate dbx.v1 golang -d postgres bwagreement.dbx .
|
||||
//go:generate dbx.v1 schema -d postgres bwagreement.dbx .
|
99
pkg/bwagreement/server.go
Normal file
99
pkg/bwagreement/server.go
Normal file
@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package bwagreement
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
dbx "storj.io/storj/pkg/bwagreement/dbx"
|
||||
"storj.io/storj/pkg/pb"
|
||||
)
|
||||
|
||||
// Server is an implementation of the pb.BandwidthServer interface
|
||||
type Server struct {
|
||||
DB *dbx.DB
|
||||
//identity *provider.FullIdentity
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewServer creates instance of Server
|
||||
func NewServer(driver, source string, logger *zap.Logger) (*Server, error) {
|
||||
db, err := dbx.Open(driver, source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = db.Exec(db.Schema())
|
||||
if err != nil && !strings.Contains(err.Error(), "already exists") {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Server{
|
||||
DB: db,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create a db entry for the provided storagenode
|
||||
func (s *Server) Create(ctx context.Context, createBwAgreement *pb.RenterBandwidthAllocation) (bwagreement *dbx.Bwagreement, err error) {
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
s.logger.Debug("entering statdb Create")
|
||||
|
||||
signature := createBwAgreement.GetSignature()
|
||||
data := createBwAgreement.GetData()
|
||||
|
||||
bwagreement, err = s.DB.Create_Bwagreement(
|
||||
ctx,
|
||||
dbx.Bwagreement_Signature(signature),
|
||||
dbx.Bwagreement_Data(data),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return bwagreement, nil
|
||||
}
|
||||
|
||||
// BandwidthAgreements receives and stores bandwidth agreements from storage nodes
|
||||
func (s *Server) BandwidthAgreements(stream pb.Bandwidth_BandwidthAgreementsServer) (err error) {
|
||||
ctx := stream.Context()
|
||||
defer mon.Task()(&ctx)(&err)
|
||||
|
||||
ch := make(chan *pb.RenterBandwidthAllocation, 1)
|
||||
errch := make(chan error, 1)
|
||||
go func() {
|
||||
for {
|
||||
msg, err := stream.Recv()
|
||||
if err != nil {
|
||||
s.logger.Error("Grpc Receive Error", zap.Error(err))
|
||||
errch <- err
|
||||
return
|
||||
}
|
||||
ch <- msg
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-errch:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case agreement := <-ch:
|
||||
go func() {
|
||||
_, err = s.Create(ctx, agreement)
|
||||
if err != nil {
|
||||
s.logger.Error("DB entry creation Error", zap.Error(err))
|
||||
errch <- err
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
154
pkg/bwagreement/server_test.go
Normal file
154
pkg/bwagreement/server_test.go
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright (C) 2018 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package bwagreement
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
dbx "storj.io/storj/pkg/bwagreement/dbx"
|
||||
"storj.io/storj/pkg/pb"
|
||||
"storj.io/storj/pkg/provider"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
const (
|
||||
host = "localhost"
|
||||
port = 5432
|
||||
user = "postgres"
|
||||
password = "your-password"
|
||||
dbname = "pointerdb"
|
||||
)
|
||||
|
||||
func getPSQLInfo() string {
|
||||
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
|
||||
"password=%s dbname=%s sslmode=disable",
|
||||
host, port, user, password, dbname)
|
||||
return psqlInfo
|
||||
}
|
||||
|
||||
func TestBandwidthAgreements(t *testing.T) {
|
||||
TS := NewTestServer(t)
|
||||
defer TS.Stop()
|
||||
|
||||
signature := []byte("iamthedummysignatureoftypebyteslice")
|
||||
data := []byte("iamthedummydataoftypebyteslice")
|
||||
|
||||
msg := &pb.RenterBandwidthAllocation{
|
||||
Signature: signature,
|
||||
Data: data,
|
||||
}
|
||||
|
||||
/* emulate sending the bwagreement stream from piecestore node */
|
||||
stream, err := TS.c.BandwidthAgreements(ctx)
|
||||
assert.NoError(t, err)
|
||||
err = stream.Send(msg)
|
||||
assert.NoError(t, err)
|
||||
|
||||
_, _ = stream.CloseAndRecv()
|
||||
|
||||
/* read back from the postgres db in bwagreement table */
|
||||
retData, err := TS.s.DB.Get_Bwagreement_By_Signature(ctx, dbx.Bwagreement_Signature(signature))
|
||||
assert.EqualValues(t, retData.Data, data)
|
||||
assert.NoError(t, err)
|
||||
|
||||
/* delete the entry what you just wrote */
|
||||
delBool, err := TS.s.DB.Delete_Bwagreement_By_Signature(ctx, dbx.Bwagreement_Signature(signature))
|
||||
assert.True(t, delBool)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
type TestServer struct {
|
||||
s *Server
|
||||
grpcs *grpc.Server
|
||||
conn *grpc.ClientConn
|
||||
c pb.BandwidthClient
|
||||
k crypto.PrivateKey
|
||||
}
|
||||
|
||||
func NewTestServer(t *testing.T) *TestServer {
|
||||
check := func(e error) {
|
||||
if !assert.NoError(t, e) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
caS, err := provider.NewTestCA(context.Background())
|
||||
check(err)
|
||||
fiS, err := caS.NewIdentity()
|
||||
check(err)
|
||||
so, err := fiS.ServerOption()
|
||||
check(err)
|
||||
|
||||
caC, err := provider.NewTestCA(context.Background())
|
||||
check(err)
|
||||
fiC, err := caC.NewIdentity()
|
||||
check(err)
|
||||
co, err := fiC.DialOption()
|
||||
check(err)
|
||||
|
||||
s := newTestServerStruct(t)
|
||||
grpcs := grpc.NewServer(so)
|
||||
|
||||
k, ok := fiC.Key.(*ecdsa.PrivateKey)
|
||||
assert.True(t, ok)
|
||||
ts := &TestServer{s: s, grpcs: grpcs, k: k}
|
||||
addr := ts.start()
|
||||
ts.c, ts.conn = connect(addr, co)
|
||||
|
||||
return ts
|
||||
}
|
||||
|
||||
func newTestServerStruct(t *testing.T) *Server {
|
||||
psqlInfo := getPSQLInfo()
|
||||
s, err := NewServer("postgres", psqlInfo, zap.NewNop())
|
||||
assert.NoError(t, err)
|
||||
return s
|
||||
}
|
||||
|
||||
func (TS *TestServer) start() (addr string) {
|
||||
lis, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
pb.RegisterBandwidthServer(TS.grpcs, TS.s)
|
||||
|
||||
go func() {
|
||||
if err := TS.grpcs.Serve(lis); err != nil {
|
||||
log.Fatalf("failed to serve: %v", err)
|
||||
}
|
||||
}()
|
||||
return lis.Addr().String()
|
||||
}
|
||||
|
||||
func connect(addr string, o ...grpc.DialOption) (pb.BandwidthClient, *grpc.ClientConn) {
|
||||
conn, err := grpc.Dial(addr, o...)
|
||||
if err != nil {
|
||||
log.Fatalf("did not connect: %v", err)
|
||||
}
|
||||
|
||||
c := pb.NewBandwidthClient(conn)
|
||||
|
||||
return c, conn
|
||||
}
|
||||
|
||||
func (TS *TestServer) Stop() {
|
||||
if err := TS.conn.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
TS.grpcs.Stop()
|
||||
}
|
Loading…
Reference in New Issue
Block a user