Something answered on this machine is still bound
A binding was skipped when the provider turned out to be on the same node, reasoning that a file saying "it is on this node" is a fact nobody needs. That is right about the location and wrong about everything beside it: a binding also carries what the provider said a consumer must know, which is the port, and a consumer cannot invent that. A build machine sharing a node with the registry it pushes to sat in a loop saying it could not read its own binding. Nothing was wrong with the machine, the module, the credential or the provision — the file was never written, and the absence looked exactly like a mistake in the module. The original intent is kept where it was right: a provision whose provider said nothing a consumer must know is still not written. A shell is answered here and there is nothing to say about it. A registry is answered here and the port is still unguessable. The address is this machine's name on the private network, or loopback when it has none — a machine off the network still reaches itself, and a name nothing resolves is worse than an address that always works.
This commit is contained in:
@@ -193,10 +193,22 @@ func (r Resolution) Declaration(with Rendering) ([]map[string]any, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if found == nil {
|
if found == nil {
|
||||||
// Bound to something answered on this machine rather than from the mesh. Nothing
|
// Answered on this same machine rather than from elsewhere in the mesh.
|
||||||
// to write: the answer is here, and a file saying "it is on this node" would be
|
//
|
||||||
// a fact nobody needs and one more thing to keep true.
|
// **Still written.** It used to be skipped, reasoning that "it is on this node"
|
||||||
continue
|
// is a fact nobody needs — and that is true of the *location* and false of
|
||||||
|
// everything beside it. A binding also carries what the provider said a consumer
|
||||||
|
// must know, which is the port; a consumer cannot invent that, and got an absent
|
||||||
|
// file with no explanation. A build machine sharing a node with the registry it
|
||||||
|
// pushes to sat in a loop saying it could not read its own binding.
|
||||||
|
here := here(r, to)
|
||||||
|
if here == nil {
|
||||||
|
// Nothing in this node's set offers it either, so there is genuinely nothing
|
||||||
|
// to say. Resolution has already refused anything unanswerable, so this is a
|
||||||
|
// requirement met by the module itself.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
found = here
|
||||||
}
|
}
|
||||||
file, err := boundFile(*found, m.Binds[to])
|
file, err := boundFile(*found, m.Binds[to])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -429,3 +441,28 @@ func (r Resolution) ContributionsTo(requirement string, settings SettingsBy) (
|
|||||||
}
|
}
|
||||||
return given[0].From, given[0].Values, nil
|
return given[0].From, given[0].Values, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// here is the module on this same machine that answers a requirement, as a binding.
|
||||||
|
//
|
||||||
|
// **Only when the provider said something a consumer must know.** That is the line: the original
|
||||||
|
// reasoning — a file saying "it is on this node" is a fact nobody needs — is right about the
|
||||||
|
// location and wrong about everything beside it. A shell is answered here and there is nothing to
|
||||||
|
// say about it. A registry is answered here and the consumer still cannot guess the port.
|
||||||
|
//
|
||||||
|
// The address is this machine's own name on the private network when it has one, and loopback
|
||||||
|
// when it does not — a machine not on the network still reaches itself, and naming it by a name
|
||||||
|
// nothing resolves would be worse than naming it by an address that always works.
|
||||||
|
func here(r Resolution, requirement string) *Needed {
|
||||||
|
for _, m := range r.Modules {
|
||||||
|
serves, said := m.Serves[requirement]
|
||||||
|
if !said || len(serves) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
at := r.At
|
||||||
|
if at == "" {
|
||||||
|
at = "127.0.0.1"
|
||||||
|
}
|
||||||
|
return &Needed{Name: requirement, From: r.Node, At: at, Serves: serves}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -299,3 +299,68 @@ func (computedOnce) Resources(string) ([]map[string]any, bool, error) {
|
|||||||
return []map[string]any{{"id": "wg", "type": "file", "path": "/etc/wireguard/wg0.conf",
|
return []map[string]any{{"id": "wg", "type": "file", "path": "/etc/wireguard/wg0.conf",
|
||||||
"content": "x", "mode": "0600"}}, true, nil
|
"content": "x", "mode": "0600"}}, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A consumer bound to something answered on its own machine still has to be told where it is.
|
||||||
|
//
|
||||||
|
// The location is a fact nobody needs — "it is here" — and everything beside it is not: the
|
||||||
|
// binding carries what the provider said a consumer must know, which is the port. A build machine
|
||||||
|
// sharing a node with the registry it pushes to sat in a loop saying it could not read its own
|
||||||
|
// binding, because none was written.
|
||||||
|
func TestSomethingAnsweredOnThisMachineIsStillBound(t *testing.T) {
|
||||||
|
r := Resolution{
|
||||||
|
Node: "anchor", At: "anchor.internal",
|
||||||
|
Modules: []Manifest{
|
||||||
|
{Module: "registry", Provides: []Offer{{Name: "artifact-store", Scope: ScopeMesh}},
|
||||||
|
Serves: map[string]map[string]any{"artifact-store": {"port": 5000}}},
|
||||||
|
{Module: "builder", Requires: []string{"artifact-store"},
|
||||||
|
Binds: map[string]string{"artifact-store": "/var/lib/builder/store.json"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
out, err := r.Declaration(Rendering{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("declaration: %v", err)
|
||||||
|
}
|
||||||
|
var content string
|
||||||
|
for _, res := range out {
|
||||||
|
if res["path"] == "/var/lib/builder/store.json" {
|
||||||
|
content, _ = res["content"].(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if content == "" {
|
||||||
|
t.Fatal("nothing was written, so the consumer cannot find a provider on its own machine")
|
||||||
|
}
|
||||||
|
if !strings.Contains(content, "5000") {
|
||||||
|
t.Fatalf("the binding does not carry the port, which is the part nobody can guess:\n%s", content)
|
||||||
|
}
|
||||||
|
if !strings.Contains(content, "anchor.internal") {
|
||||||
|
t.Fatalf("the binding does not say where to reach it:\n%s", content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And a machine not on the private network still reaches itself.
|
||||||
|
func TestAMachineOffTheNetworkIsBoundToItselfByAnAddressThatWorks(t *testing.T) {
|
||||||
|
r := Resolution{
|
||||||
|
Node: "alone",
|
||||||
|
Modules: []Manifest{
|
||||||
|
{Module: "registry", Provides: []Offer{{Name: "artifact-store", Scope: ScopeMesh}},
|
||||||
|
Serves: map[string]map[string]any{"artifact-store": {"port": 5000}}},
|
||||||
|
{Module: "builder", Requires: []string{"artifact-store"},
|
||||||
|
Binds: map[string]string{"artifact-store": "/var/lib/builder/store.json"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
out, err := r.Declaration(Rendering{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("declaration: %v", err)
|
||||||
|
}
|
||||||
|
for _, res := range out {
|
||||||
|
if res["path"] != "/var/lib/builder/store.json" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
content, _ := res["content"].(string)
|
||||||
|
if !strings.Contains(content, "127.0.0.1") {
|
||||||
|
t.Fatalf("bound by a name nothing on this machine resolves:\n%s", content)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Fatal("nothing was written")
|
||||||
|
}
|
||||||
|
|||||||
@@ -74,6 +74,10 @@ type Held struct {
|
|||||||
type Resolution struct {
|
type Resolution struct {
|
||||||
// Node is which machine this was resolved for, so a generator can be asked about it.
|
// Node is which machine this was resolved for, so a generator can be asked about it.
|
||||||
Node string
|
Node string
|
||||||
|
// At is this machine's own name on the private network, empty if it is not on one. Kept
|
||||||
|
// because a consumer bound to something answered on this same machine still has to be told
|
||||||
|
// where it is — the answer being local does not make the port guessable.
|
||||||
|
At string
|
||||||
|
|
||||||
// Modules in the order they were resolved: assigned first, then what they pulled in.
|
// Modules in the order they were resolved: assigned first, then what they pulled in.
|
||||||
Modules []Manifest
|
Modules []Manifest
|
||||||
@@ -320,7 +324,7 @@ func Resolve(catalogue map[string]Manifest, assigned []string, node Node, world
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resolution := Resolution{Node: node.Name, Because: because, Needs: needs}
|
resolution := Resolution{Node: node.Name, At: node.At, Because: because, Needs: needs}
|
||||||
for _, n := range order {
|
for _, n := range order {
|
||||||
resolution.Modules = append(resolution.Modules, catalogue[n])
|
resolution.Modules = append(resolution.Modules, catalogue[n])
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user