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:
@@ -994,8 +994,13 @@ func planFor(ctx context.Context, inv *inventory.Inventory, nodeName string) (ca
|
||||
return catalogue.Resolution{}, nil, err
|
||||
}
|
||||
|
||||
onNetwork, err := whereEveryoneIs(ctx, inv, shelf)
|
||||
if err != nil {
|
||||
return catalogue.Resolution{}, nil, err
|
||||
}
|
||||
resolved, err := catalogue.Resolve(shelf, assigned,
|
||||
catalogue.Node{Name: nodeName, Site: site, Capabilities: capabilities}, world)
|
||||
catalogue.Node{Name: nodeName, Site: site, Capabilities: capabilities,
|
||||
At: onNetwork[nodeName]}, world)
|
||||
if err != nil {
|
||||
return catalogue.Resolution{}, nil, err
|
||||
}
|
||||
@@ -1053,6 +1058,13 @@ func theRestOfTheMesh(ctx context.Context, inv *inventory.Inventory,
|
||||
for _, p := range places {
|
||||
siteOf[p.Name] = p.Site
|
||||
}
|
||||
// Which machines are actually on the private network, and what they are called there. Not
|
||||
// "has an address" — that was true of every placed machine and told you nothing about whether
|
||||
// anything could reach it. It is what resolved the module.
|
||||
onNetwork, err := whereEveryoneIs(ctx, inv, shelf)
|
||||
if err != nil {
|
||||
return catalogue.World{}, err
|
||||
}
|
||||
|
||||
type candidate struct {
|
||||
node catalogue.Node
|
||||
@@ -1069,10 +1081,11 @@ func theRestOfTheMesh(ctx context.Context, inv *inventory.Inventory,
|
||||
}
|
||||
caps, _ := inv.ProfileOf(ctx, n.Name)
|
||||
others = append(others, candidate{
|
||||
catalogue.Node{Name: n.Name, Site: siteOf[n.Name], Capabilities: caps}, theirs})
|
||||
catalogue.Node{Name: n.Name, Site: siteOf[n.Name], Capabilities: caps,
|
||||
At: onNetwork[n.Name]}, theirs})
|
||||
}
|
||||
|
||||
offered := map[string][]string{}
|
||||
offered := map[string][]catalogue.Provider{}
|
||||
for _, o := range others {
|
||||
got, err := catalogue.Resolve(shelf, o.assigned, o.node, catalogue.World{Unchecked: true})
|
||||
if err != nil {
|
||||
@@ -1082,12 +1095,29 @@ func theRestOfTheMesh(ctx context.Context, inv *inventory.Inventory,
|
||||
}
|
||||
for _, m := range got.Modules {
|
||||
for _, name := range m.OffersAt(catalogue.ScopeMesh) {
|
||||
offered[name] = append(offered[name], o.node.Name)
|
||||
// What that module says a consumer needs to know, with that node's settings on
|
||||
// it: a port somebody moved on the provider is a port its consumers must be told
|
||||
// about, and the two coming from different places is how they come to disagree.
|
||||
serves := m.Serves[name]
|
||||
if len(serves) > 0 {
|
||||
layers, err := inv.SettingsFor(ctx, o.node.Name, m.Module)
|
||||
if err != nil {
|
||||
return catalogue.World{}, err
|
||||
}
|
||||
serves, err = catalogue.Settle(serves, layers)
|
||||
if err != nil {
|
||||
return catalogue.World{}, err
|
||||
}
|
||||
}
|
||||
offered[name] = append(offered[name], catalogue.Provider{
|
||||
Node: o.node.Name, At: o.node.At, Serves: serves})
|
||||
}
|
||||
}
|
||||
}
|
||||
for k := range offered {
|
||||
sort.Strings(offered[k])
|
||||
sort.Slice(offered[k], func(i, j int) bool {
|
||||
return offered[k][i].Node < offered[k][j].Node
|
||||
})
|
||||
}
|
||||
|
||||
world := catalogue.World{Offered: offered}
|
||||
@@ -1101,6 +1131,54 @@ func theRestOfTheMesh(ctx context.Context, inv *inventory.Inventory,
|
||||
return world, nil
|
||||
}
|
||||
|
||||
// whereEveryoneIs is each machine's name on the private network, for the ones on it.
|
||||
//
|
||||
// **Resolved without consulting the rest of the mesh**, and that is not an optimisation. Every
|
||||
// other path here answers a question about one node by resolving the others; this one is called
|
||||
// *from* that path, so doing the same would not terminate — which it did not, for two minutes,
|
||||
// until it was run.
|
||||
//
|
||||
// An unchecked resolution is exactly right for the question anyway. Whether a machine is on the
|
||||
// private network depends on what it was assigned and what that requires, both of which are local
|
||||
// facts. What it takes *from* other machines does not change the answer.
|
||||
//
|
||||
// The distinction that matters is kept: a machine absent from the network module's own view is
|
||||
// absent here, so "has an address" is not mistaken for "is reachable" — which it was, before the
|
||||
// network became something a machine is given.
|
||||
func whereEveryoneIs(ctx context.Context, inv *inventory.Inventory,
|
||||
shelf map[string]catalogue.Manifest) (map[string]string, error) {
|
||||
|
||||
places, err := inv.Overlays(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := map[string]string{}
|
||||
for _, p := range places {
|
||||
if p.Address == "" {
|
||||
continue
|
||||
}
|
||||
assigned, err := inv.Assigned(ctx, p.Name)
|
||||
if err != nil || len(assigned) == 0 {
|
||||
continue
|
||||
}
|
||||
caps, _ := inv.ProfileOf(ctx, p.Name)
|
||||
got, err := catalogue.Resolve(shelf, assigned,
|
||||
catalogue.Node{Name: p.Name, Site: p.Site, Capabilities: caps},
|
||||
catalogue.World{Unchecked: true})
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, m := range got.Modules {
|
||||
for _, offered := range m.Offers() {
|
||||
if offered == overlay.Requirement {
|
||||
out[p.Name] = overlay.InternalName(p.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func planCommand(ctx context.Context, args []string) error {
|
||||
set := flag.NewFlagSet("plan", flag.ContinueOnError)
|
||||
// Because "one resource" does not tell you whether the settings landed. Being able to read
|
||||
|
||||
Reference in New Issue
Block a user