bgpwtf/cccampix: draw the rest of the fucking owl

Change-Id: I49fd5906e69512e8f2d414f406edc0179522f225
diff --git a/bgpwtf/cccampix/verifier/model/model.go b/bgpwtf/cccampix/verifier/model/model.go
new file mode 100644
index 0000000..b9b81c9
--- /dev/null
+++ b/bgpwtf/cccampix/verifier/model/model.go
@@ -0,0 +1,141 @@
+package model
+
+import (
+	"context"
+	"fmt"
+	"net"
+	"strings"
+	"time"
+
+	pb "code.hackerspace.pl/hscloud/bgpwtf/cccampix/proto"
+	"code.hackerspace.pl/hscloud/bgpwtf/cccampix/verifier/model/migrations"
+	migrate "github.com/golang-migrate/migrate/v4"
+	_ "github.com/golang-migrate/migrate/v4/database/cockroachdb"
+	"github.com/jmoiron/sqlx"
+	_ "github.com/lib/pq"
+)
+
+type Model interface {
+	MigrateUp() error
+
+	RecordPeeringDBPeers(ctx context.Context, members []*pb.PeeringDBMember) error
+	RecordPeeringDBPeerRouters(ctx context.Context, members []*pb.PeeringDBMember) error
+	GetPeeringDBPeer(ctx context.Context, asn int64) (*pb.PeeringDBMember, error)
+
+	GetCheckablePeers(ctx context.Context) ([]*Peer, error)
+	SubmitPeerCheckResults(ctx context.Context, res []*PeerCheckResult) error
+	GetPeerCheckResults(ctx context.Context, asn []int64) ([]*PeerCheckResult, error)
+
+	UpdatePGPKey(ctx context.Context, key *PeerPGPKey) error
+
+	ConfigureMissingSessions(ctx context.Context, gen func() SessionConfig) error
+
+	UpdateAllowedPrefixes(ctx context.Context, asn int64, prefixes []*AllowedPrefix) error
+	GetAllowedPrefixes(ctx context.Context, asn int64) ([]*AllowedPrefix, error)
+}
+
+type stringer struct {
+}
+
+func (s *stringer) String() string {
+	if s == nil {
+		return "<nil>"
+	}
+	return fmt.Sprintf("%+v", *s)
+}
+
+type Router struct {
+	stringer
+	V6 net.IP
+	V4 net.IP
+}
+
+type Peer struct {
+	stringer
+	ASN     int64
+	Name    string
+	Routers []*Router
+}
+
+type PeerCheckStatus int
+
+const (
+	PeerCheckStatus_Invalid PeerCheckStatus = iota
+	PeerCheckStatus_Okay
+	PeerCheckStatus_Failed
+	PeerCheckStatus_SoftFailed
+)
+
+type PeerCheckResult struct {
+	PeerASN   int64
+	CheckName string
+	Time      time.Time
+	Status    PeerCheckStatus
+	Message   string
+}
+
+func (p *PeerCheckResult) String() string {
+	if p == nil {
+		return "<nil>"
+	}
+	return fmt.Sprintf("%+v", *p)
+}
+
+type PeerPGPKey struct {
+	stringer
+	PeerASN     int64
+	Fingerprint string
+}
+
+type SessionConfig struct {
+	BGPSecret string
+}
+
+type AllowedPrefix struct {
+	Prefix    net.IPNet
+	MaxLength int64
+	TA        string
+}
+
+func (p *AllowedPrefix) String() string {
+	if p == nil {
+		return "<nil>"
+	}
+	return fmt.Sprintf("%+v", *p)
+}
+
+type sqlModel struct {
+	db  *sqlx.DB
+	dsn string
+}
+
+func Connect(ctx context.Context, driver, dsn string) (Model, error) {
+	if dsn == "" {
+		return nil, fmt.Errorf("dsn cannot be empty")
+	}
+
+	db, err := sqlx.ConnectContext(ctx, driver, dsn)
+	if err != nil {
+		return nil, fmt.Errorf("could not connect to database: %v", err)
+	}
+
+	return &sqlModel{
+		db:  db,
+		dsn: dsn,
+	}, nil
+}
+
+func (m *sqlModel) MigrateUp() error {
+	dsn := "cockroach://" + strings.TrimPrefix(m.dsn, "postgres://")
+	mig, err := migrations.New(dsn)
+	if err != nil {
+		return err
+	}
+	err = mig.Up()
+	switch err {
+	case migrate.ErrNoChange:
+		return nil
+	default:
+		return err
+	}
+}