Record whether a node is adopted and which modules were taken on it (hq ADR 0100)
This commit is contained in:
+46
-20
@@ -49,6 +49,12 @@ type Node struct {
|
||||
// month's assignments, and until this existed those looked the same as a node that is
|
||||
// current (novox/hq 09-the-node-lifecycle).
|
||||
LastSeen time.Time
|
||||
|
||||
// Adopted is whether this node is adopted rather than converged (novox/hq ADR 0100): what is
|
||||
// found on it is kept until its module is taken, and the firewall found on it stays in force.
|
||||
// AdoptedSince is when it last became so; zero for a converged node.
|
||||
Adopted bool
|
||||
AdoptedSince time.Time
|
||||
}
|
||||
|
||||
// Silent is how long since this node was last heard from, and whether it ever was.
|
||||
@@ -74,28 +80,59 @@ var ErrNameTaken = errors.New("a node of that name already exists")
|
||||
// (novox/hq 09-the-node-lifecycle), so the record is what a token binds to and must exist before
|
||||
// there is anything to join.
|
||||
func (i *Inventory) AddNode(ctx context.Context, name string) (Node, error) {
|
||||
return i.AddNodeAs(ctx, name, false)
|
||||
}
|
||||
|
||||
// AddNodeAs creates a node record, adopted or converged (novox/hq ADR 0100). The operator says
|
||||
// which; a node added without saying is converged, as every node was before adoption existed.
|
||||
func (i *Inventory) AddNodeAs(ctx context.Context, name string, adopted bool) (Node, error) {
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return Node{}, errors.New("a node needs a name: it is how a token is issued for it")
|
||||
}
|
||||
|
||||
var n Node
|
||||
var since *time.Time
|
||||
err := i.store.Pool().QueryRow(ctx,
|
||||
`insert into node (name) values ($1) returning id, name, created`,
|
||||
name).Scan(&n.ID, &n.Name, &n.Created)
|
||||
`insert into node (name, adopted, adopted_since)
|
||||
values ($1, $2, case when $2 then now() end)
|
||||
returning id, name, created, adopted, adopted_since`,
|
||||
name, adopted).Scan(&n.ID, &n.Name, &n.Created, &n.Adopted, &since)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "node_name_key") {
|
||||
return Node{}, fmt.Errorf("%w: %s", ErrNameTaken, name)
|
||||
}
|
||||
return Node{}, err
|
||||
}
|
||||
if since != nil {
|
||||
n.AdoptedSince = *since
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// nodeColumns and scanNode are the one reading of a node row, so every way of finding a node
|
||||
// says whether it is adopted.
|
||||
const nodeColumns = `id, name, created, last_seen, adopted, adopted_since`
|
||||
|
||||
func scanNode(row pgx.Row) (Node, error) {
|
||||
var n Node
|
||||
var seen, since *time.Time
|
||||
if err := row.Scan(&n.ID, &n.Name, &n.Created, &seen, &n.Adopted, &since); err != nil {
|
||||
return Node{}, err
|
||||
}
|
||||
if seen != nil {
|
||||
n.LastSeen = *seen
|
||||
}
|
||||
if since != nil {
|
||||
n.AdoptedSince = *since
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Nodes are every node record, oldest first.
|
||||
func (i *Inventory) Nodes(ctx context.Context) ([]Node, error) {
|
||||
rows, err := i.store.Pool().Query(ctx,
|
||||
`select id, name, created, last_seen from node order by created, name`)
|
||||
`select `+nodeColumns+` from node order by created, name`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -103,14 +140,10 @@ func (i *Inventory) Nodes(ctx context.Context) ([]Node, error) {
|
||||
|
||||
var nodes []Node
|
||||
for rows.Next() {
|
||||
var n Node
|
||||
var seen *time.Time
|
||||
if err := rows.Scan(&n.ID, &n.Name, &n.Created, &seen); err != nil {
|
||||
n, err := scanNode(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if seen != nil {
|
||||
n.LastSeen = *seen
|
||||
}
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
return nodes, rows.Err()
|
||||
@@ -118,9 +151,8 @@ func (i *Inventory) Nodes(ctx context.Context) ([]Node, error) {
|
||||
|
||||
// NodeByName finds one node record.
|
||||
func (i *Inventory) NodeByName(ctx context.Context, name string) (Node, error) {
|
||||
var n Node
|
||||
err := i.store.Pool().QueryRow(ctx,
|
||||
`select id, name, created from node where name = $1`, name).Scan(&n.ID, &n.Name, &n.Created)
|
||||
n, err := scanNode(i.store.Pool().QueryRow(ctx,
|
||||
`select `+nodeColumns+` from node where name = $1`, name))
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return Node{}, fmt.Errorf("%w: %s", ErrNoSuchNode, name)
|
||||
}
|
||||
@@ -248,10 +280,7 @@ func (i *Inventory) Claim(ctx context.Context, secret, by string, again bool) (N
|
||||
if err != nil {
|
||||
return Node{}, err
|
||||
}
|
||||
var n Node
|
||||
err = i.store.Pool().QueryRow(ctx,
|
||||
`select id, name, created from node where id = $1`, id).Scan(&n.ID, &n.Name, &n.Created)
|
||||
return n, err
|
||||
return scanNode(i.store.Pool().QueryRow(ctx, `select `+nodeColumns+` from node where id = $1`, id))
|
||||
}
|
||||
|
||||
// Spend makes a claimed token used, only for the presenter holding the claim. The last write to the
|
||||
@@ -293,10 +322,7 @@ func (i *Inventory) Redeem(ctx context.Context, secret string) (Node, error) {
|
||||
return Node{}, err
|
||||
}
|
||||
|
||||
var n Node
|
||||
err = i.store.Pool().QueryRow(ctx,
|
||||
`select id, name, created from node where id = $1`, id).Scan(&n.ID, &n.Name, &n.Created)
|
||||
return n, err
|
||||
return scanNode(i.store.Pool().QueryRow(ctx, `select `+nodeColumns+` from node where id = $1`, id))
|
||||
}
|
||||
|
||||
// RecordProfile keeps the last thing a node said about what it can do.
|
||||
|
||||
Reference in New Issue
Block a user