Say a machine waiting for its push as waiting, not uncomposable (hq issue 275)
Between assign and push a module's own secrets are not made yet; D1 composed without making them and raised an urgent 'nothing can be sent' that the next push resolved silently. D1 now composes as the push would (Foreseeing): a secret the push makes gets a stand-in and is named, one the push is refused on is refused with the push's words. Waiting is said only past 30 minutes, as a warning. D3 and D13 expect a holder only once its machine was sent it and reported or had ten minutes to.
This commit is contained in:
@@ -976,6 +976,25 @@ func (i *Inventory) RecordSentUnder(ctx context.Context, node, digest string, bu
|
||||
return err
|
||||
}
|
||||
|
||||
// AwaitingSince is since when a machine has had something waiting for its next send (novox/hq issue
|
||||
// 275): the oldest assignment on it made after it was last sent, or — when nothing was assigned since —
|
||||
// when it was last sent, or when it joined if it never was. The moment a bound on "not pushed yet" is
|
||||
// read from: every change a push carries happened after the last push, and an assignment is the one
|
||||
// that says when.
|
||||
func (i *Inventory) AwaitingSince(ctx context.Context, name string) (time.Time, error) {
|
||||
var since time.Time
|
||||
err := i.store.Pool().QueryRow(ctx,
|
||||
`select coalesce(
|
||||
(select min(a.assigned) from assignment a
|
||||
where a.node = n.id and (n.sent_at is null or a.assigned > n.sent_at)),
|
||||
n.sent_at, n.created)
|
||||
from node n where n.name = $1`, name).Scan(&since)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
return time.Time{}, fmt.Errorf("%w: %s", ErrNoSuchNode, name)
|
||||
}
|
||||
return since, err
|
||||
}
|
||||
|
||||
// SentBuilds is the build of each module a machine was last sent, by its name: module to the commit
|
||||
// its build was made from (novox/hq issue 259). Known is false when that was not kept — a machine
|
||||
// last sent before it was, sent a declaration by hand, or one the mesh does not know.
|
||||
|
||||
@@ -327,9 +327,7 @@ func (i *Inventory) SecretForModule(ctx context.Context, node, module, name stri
|
||||
return "", err
|
||||
}
|
||||
if key == "" {
|
||||
return "", fmt.Errorf(
|
||||
"%s needs a secret and %s has no sealing key, so nothing can be sealed to it",
|
||||
module, node)
|
||||
return "", noSealingKey(module, node)
|
||||
}
|
||||
record, err := i.NodeByName(ctx, node)
|
||||
if err != nil {
|
||||
@@ -349,10 +347,7 @@ func (i *Inventory) SecretForModule(ctx context.Context, node, module, name stri
|
||||
// would put 32 random bytes where a working credential was: the machine would apply it,
|
||||
// report success, and whatever reads it would fail to authenticate somewhere else
|
||||
// entirely — with the mesh insisting the secret was delivered, which it was.
|
||||
return "", fmt.Errorf(
|
||||
"%s on %s holds %q, which was given to the mesh rather than made by it, and %s has "+
|
||||
"since generated a new sealing key. The mesh cannot make another; issue it again",
|
||||
module, node, name, node)
|
||||
return "", acceptedUnderAnOldKey(module, node, name)
|
||||
}
|
||||
|
||||
operator, err := i.OperatorKey(ctx)
|
||||
@@ -381,6 +376,51 @@ func (i *Inventory) SecretForModule(ctx context.Context, node, module, name stri
|
||||
return made.ForConsumer, nil
|
||||
}
|
||||
|
||||
// WouldMakeSecretForModule says what SecretForModule would do about an own secret, without doing it:
|
||||
// nil when it would make one (or one is held), and otherwise the very refusal it would meet. The read
|
||||
// a question asked ahead of a send uses (novox/hq issue 275), so that "the next push makes this" is
|
||||
// told apart from "the next push fails on this" by the same rules the push applies.
|
||||
func (i *Inventory) WouldMakeSecretForModule(ctx context.Context, node, module, name string) error {
|
||||
key, err := i.SealingKeyOf(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if key == "" {
|
||||
return noSealingKey(module, node)
|
||||
}
|
||||
record, err := i.NodeByName(ctx, node)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var against, origin string
|
||||
err = i.store.Pool().QueryRow(ctx,
|
||||
`select node_key, origin from module_secret where node = $1 and module = $2 and name = $3`,
|
||||
record.ID, module, name).Scan(&against, &origin)
|
||||
switch {
|
||||
case errors.Is(err, pgx.ErrNoRows):
|
||||
return nil
|
||||
case err != nil:
|
||||
return err
|
||||
case against != key && origin == "accepted":
|
||||
return acceptedUnderAnOldKey(module, node, name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// noSealingKey is the refusal for a secret on a machine that has no key to seal it to.
|
||||
func noSealingKey(module, node string) error {
|
||||
return fmt.Errorf("%s needs a secret and %s has no sealing key, so nothing can be sealed to it",
|
||||
module, node)
|
||||
}
|
||||
|
||||
// acceptedUnderAnOldKey is the refusal for a given secret sealed to a key the machine no longer has.
|
||||
func acceptedUnderAnOldKey(module, node, name string) error {
|
||||
return fmt.Errorf(
|
||||
"%s on %s holds %q, which was given to the mesh rather than made by it, and %s has "+
|
||||
"since generated a new sealing key. The mesh cannot make another; issue it again",
|
||||
module, node, name, node)
|
||||
}
|
||||
|
||||
// AcceptSecretForModule keeps a value somebody supplied as a module's own secret.
|
||||
//
|
||||
// The counterpart to SecretForModule, which generates one. Some of what a module needs the mesh
|
||||
|
||||
Reference in New Issue
Block a user