An app is told where its database is
Knowing that a machine needs the anchor's database is useless to the
program that needs it unless the program is told. It knew; nothing was
written anywhere it could read.
Two fields, mirroring contributes/receives in the other direction:
serves: {database: {port: 5432, driver: postgres}} on the provider
binds: {database: /etc/app/database.json} on the consumer
The provider says what a consumer needs to know; the mesh adds the half
only it has — which machine, and what that machine is called on the
private network. The file says, in itself, that it carries no credential
and why. A missing field looks like a bug; a stated absence looks like a
boundary.
Binding something answered on this machine writes nothing. A file saying
"it is on this node" is a fact nobody needs and one more thing to keep
true.
And two machines that share no private network are refused rather than
wired together. An app here and a database there with no path between
them is a mesh that reports itself configured and does not work — the
failure surfaces as a connection timing out, which is the slowest place
to find it. This is checkable now only because the network became
something a machine is given rather than something it has by having an
address.
One fault, found by running it: working out who is on the private network
resolved the mesh, and resolving the mesh asks who is on the private
network. It hung for two minutes. The comment above the function said not
to do that and the function did it anyway; it now resolves each node
locally, which is the right answer to the question regardless — whether a
machine is on the network depends on what it was assigned, not on what it
takes from others.
This commit is contained in:
@@ -162,8 +162,27 @@ type Manifest struct {
|
||||
// delivers the facts, and the module turns them into whatever it runs. That boundary is why
|
||||
// swapping the proxy does not touch a single module that publishes through it.
|
||||
Receives map[string]string `json:"receives,omitempty"`
|
||||
|
||||
// Serves is what a consumer needs to know in order to use something this module provides — a
|
||||
// port, a path, a realm. The module's half of the answer; the mesh adds the other half, which
|
||||
// is *which machine* and *where it is on the private network*.
|
||||
//
|
||||
// It does not carry a credential and cannot: a manifest is the same on every mesh, and a
|
||||
// secret is the one thing that must not be.
|
||||
Serves map[string]map[string]any `json:"serves,omitempty"`
|
||||
|
||||
// Binds is where this module wants to be told about something it requires, per requirement.
|
||||
//
|
||||
// Because "this machine needs a database from the anchor" is useless to the program that
|
||||
// needs it unless the program is told. A file, like everything else — the host writes files
|
||||
// and knows nothing about provisions, which is what keeps this from needing anything new
|
||||
// down there.
|
||||
Binds map[string]string `json:"binds,omitempty"`
|
||||
}
|
||||
|
||||
// BoundID is the resource identity of the file a module is told about a provision in.
|
||||
func BoundID(requirement string) string { return "bound-" + requirement }
|
||||
|
||||
// Wants is everything that must be provided on the same node: what this module requires, and what
|
||||
// it contributes to.
|
||||
func (m Manifest) Wants() []string {
|
||||
@@ -263,6 +282,36 @@ func ParseManifest(raw []byte) (Manifest, error) {
|
||||
"%s contributes nothing to %q; if it only needs one, require it", m.Module, to))
|
||||
}
|
||||
}
|
||||
for to := range m.Serves {
|
||||
var offered bool
|
||||
for _, o := range m.Offers() {
|
||||
if o == to {
|
||||
offered = true
|
||||
}
|
||||
}
|
||||
if !offered {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s serves %q to whoever requires it, and does not provide it", m.Module, to))
|
||||
}
|
||||
}
|
||||
for to, where := range m.Binds {
|
||||
if !strings.HasPrefix(where, "/") {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s binds %q at %q, which is not an absolute path", m.Module, to, where))
|
||||
}
|
||||
var wanted bool
|
||||
for _, w := range m.Wants() {
|
||||
if w == to {
|
||||
wanted = true
|
||||
}
|
||||
}
|
||||
if !wanted {
|
||||
// Being told about something you never asked for would write a file describing a
|
||||
// machine this one has no business talking to.
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s binds %q and does not require it", m.Module, to))
|
||||
}
|
||||
}
|
||||
for to, where := range m.Receives {
|
||||
if !name.MatchString(to) {
|
||||
problems = append(problems, fmt.Sprintf("%q is not a usable name to receive", to))
|
||||
|
||||
Reference in New Issue
Block a user