Commit f80241b8 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent d4509066
......@@ -22,7 +22,7 @@
// Package lonet provides TCP network simulated on top of localhost TCP loopback.
//
// For testing distributed systems it is sometimes handy to imitate network of
// several TCP hosts. It is also handy that port allocated on Dial/Listen/Accept on
// several TCP hosts. It is also handy that ports allocated on Dial/Listen/Accept on
// that hosts be predictable - that would help tests to verify network events
// against expected sequence. When whole system could be imitated in 1 OS-level
// process, package lab.nexedi.com/kirr/go123/xnet/pipenet serves the task via
......@@ -32,7 +32,7 @@
//
// Similarly to pipenet addresses on lonet are host:port pairs and several
// hosts could be created with different names. A host is xnet.Networker and
// so can be worked with similarly to regular TCP network with
// so can be worked with similarly to regular TCP network access-point with
// Dial/Listen/Accept. Host's ports allocation is predictable: ports of a host
// are contiguous integer sequence starting from 1 that are all initially free,
// and whenever autobind is requested the first free port of the host will be
......
......@@ -23,9 +23,12 @@ package lonet
import (
"context"
"errors"
"fmt"
"crawshaw.io/sqlite"
"crawshaw.io/sqlite/sqliteutil"
"lab.nexedi.com/kirr/go123/xerr"
)
// registry schema
......@@ -41,6 +44,8 @@ import (
// "schemaver" str(int) - version of schema.
// "network" text - name of lonet network this registry serves.
const schemaVer = "lonet.1"
type sqliteRegistry struct {
dbpool *sqlite.Pool
......@@ -48,8 +53,9 @@ type sqliteRegistry struct {
}
// openRegistrySqlite opens SQLite registry located at dburi.
// XXX network name?
func openRegistrySQLite(ctx context.Context, dburi string) (_ *sqliteRegistry, err error) {
//
// the registry is setup/verified to be serving specified lonet network.
func openRegistrySQLite(ctx context.Context, dburi, network string) (_ *sqliteRegistry, err error) {
r := &sqliteRegistry{uri: dburi}
defer r.regerr(&err, "open")
......@@ -60,7 +66,7 @@ func openRegistrySQLite(ctx context.Context, dburi string) (_ *sqliteRegistry, e
r.dbpool = dbpool
err = r.setup(ctx)
err = r.setup(ctx, network)
if err != nil {
r.Close()
return nil, err
......@@ -90,8 +96,8 @@ func (r *sqliteRegistry) withConn(ctx context.Context, f func(*sqlite.Conn) erro
return f(conn)
}
var errNoRows = errors.New("query1: empty result")
var errManyRows = errors.New("query1: multiple results")
var errNoRows = errors.New("query: empty result")
var errManyRows = errors.New("query: multiple results")
// query1 is like sqliteutil.Exec but checks that exactly 1 row is returned.
//
......@@ -116,9 +122,9 @@ func query1(conn *sqlite.Conn, query string, resultf func(stmt *sqlite.Stmt), ar
return nil
}
func (r *sqliteRegistry) setup(ctx context.Context) (err error) {
return r.withConn(ctx, func(conn *sqlite.Conn) error {
err := sqliteutil.ExecScript(conn, `
func (r *sqliteRegistry) setup(ctx context.Context, network string) error {
return r.withConn(ctx, func(conn *sqlite.Conn) (err error) {
err = sqliteutil.ExecScript(conn, `
CREATE TABLE IF NOT EXISTS hosts (
hostname TEXT NON NULL PRIMARY KEY,
osladdr TEXT NON NULL
......@@ -133,13 +139,79 @@ func (r *sqliteRegistry) setup(ctx context.Context) (err error) {
return err
}
// XXX check schemaver
// XXX check network name
// do check/init under transaction
defer sqliteutil.Save(conn)(&err)
// XXX vvv review error handling
// check/init schema version
ver, err := r.config(conn, "schemaver")
if err != nil {
return err
}
if ver == "" {
ver = schemaVer
err = r.setConfig(conn, "schemaver", ver)
if err != nil {
return err
}
}
if ver != schemaVer {
return fmt.Errorf("schema version mismatch: want %q; have %q", schemaVer, ver)
}
// check/init network name
dbnetwork, err := r.config(conn, "network")
if err != nil {
return err
}
if dbnetwork == "" {
dbnetwork = network
err = r.setConfig(conn, "network", dbnetwork)
if err != nil {
return err
}
}
if dbnetwork != network {
return fmt.Errorf("network name mismatch: want %q; have %q", network, dbnetwork)
}
return nil
})
}
// config gets one registry configuration value by name.
//
// if there is no record corresponding to name ("", nil) is returned.
// XXX add ok ret to indicate presence of value?
func (r *sqliteRegistry) config(conn *sqlite.Conn, name string) (value string, err error) {
defer xerr.Contextf(&err, "config: get %q", name)
err = query1(conn, "SELECT value FROM meta WHERE name = ?", func(stmt *sqlite.Stmt) {
value = stmt.ColumnText(0)
}, name)
switch err {
case errNoRows:
return "", nil
case errManyRows:
value = ""
}
return value, err
}
// setConfig sets one registry configuration value by name.
func (r *sqliteRegistry) setConfig(conn *sqlite.Conn, name, value string) (err error) {
defer xerr.Contextf(&err, "config: set %q = %q", name, value)
err = sqliteutil.Exec(conn,
"INSERT OR REPLACE INTO meta (name, value) VALUES (?, ?)", nil,
name, value)
return err
}
func (r *sqliteRegistry) Announce(ctx context.Context, hostname, osladdr string) (err error) {
defer r.regerr(&err, "announce", hostname, osladdr)
......@@ -149,7 +221,7 @@ func (r *sqliteRegistry) Announce(ctx context.Context, hostname, osladdr string)
hostname, osladdr)
switch sqlite.ErrCode(err) {
case sqlite.SQLITE_CONSTRAINT_UNIQUE: // XXX test
case sqlite.SQLITE_CONSTRAINT_UNIQUE:
err = errHostDup
}
......
......@@ -21,6 +21,7 @@ package lonet
import (
"context"
"fmt"
"io/ioutil"
"os"
"testing"
......@@ -39,7 +40,7 @@ func TestRegistrySQLite(t *testing.T) {
ctx := context.Background()
r, err := openRegistrySQLite(ctx, dbpath)
r, err := openRegistrySQLite(ctx, dbpath, "aaa")
X(err)
// query checks that result of Query(hostname) is as expected.
......@@ -112,7 +113,7 @@ func TestRegistrySQLite(t *testing.T) {
query(r, "α", "alpha:1234")
query(r, "β", ø)
r2, err := openRegistrySQLite(ctx, dbpath)
r2, err := openRegistrySQLite(ctx, dbpath, "aaa")
// r2.Network() == ...
query(r2, "α", "alpha:1234")
query(r2, "β", ø)
......@@ -133,4 +134,15 @@ func TestRegistrySQLite(t *testing.T) {
X(r2.Close())
query(r2, "α", errRegistryDown)
// verify network mismatch detection works
r3, err := openRegistrySQLite(ctx, dbpath, "bbb")
if !(r3 == nil && err != nil) {
t.Fatalf("network mismatch: not detected")
}
errWant := fmt.Sprintf(`%s: open []: network name mismatch: want "bbb"; have "aaa"`, dbpath)
if err.Error() != errWant {
t.Fatalf("network mismatch: error:\nhave: %q\nwant: %q", err.Error(), errWant)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment