A gate judges its own send and the build it sent, and never puts the controller back behind its store (hq issue 352)
On 2026-10-09 a release's gate on the control node read the machine's
report against a newer send another plan had just made there, failed
three builds the machine had reported healthy, and put them back on
every machine to a controller older than the store's schema; that
controller then passed the newer plan's gate from its own health.
- A gate keeps what its send carried (digest, sequence) and reads the
report against it; a report on the last send is on it too.
- A gate judges only the build the machine was last sent: another build
there supersedes the judging — no verdict, nothing put back.
- A controller is told its build (MESH_CONTROLLER_VERSION, ${version}
in a process's env) and records how far it reads the store's schema;
a put-back to a build that reaches less, or never said, is refused
and the current build kept, said as urgent.
- A release's open gate holds other sends of its modules there, and a
plan's own first send waits on it.
This commit is contained in:
@@ -173,11 +173,7 @@ func (m Manifest) Resolve(built []Built) (Manifest, error) {
|
||||
// had — so re-composing a declaration moves nothing, where a commit would move the
|
||||
// path of an identical binary and recreate everything that reads it.
|
||||
for key, value := range filled {
|
||||
text, isText := value.(string)
|
||||
if !isText || !strings.Contains(text, versionRef) {
|
||||
continue
|
||||
}
|
||||
filled[key] = strings.ReplaceAll(text, versionRef, versionOf(artifact.Digest))
|
||||
filled[key] = withVersion(value, versionOf(artifact.Digest))
|
||||
}
|
||||
default:
|
||||
return Manifest{}, fmt.Errorf("%s: %q is a %q, and an artifact is %q, %q, %q or %q",
|
||||
@@ -189,6 +185,31 @@ func (m Manifest) Resolve(built []Built) (Manifest, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// withVersion is a resource's value with `${version}` filled: in a string, and in each string of a map —
|
||||
// a process's env, where the controller is told which build it is (novox/hq issue 352). Anything else is
|
||||
// left as it is.
|
||||
func withVersion(value any, version string) any {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
if strings.Contains(v, versionRef) {
|
||||
return strings.ReplaceAll(v, versionRef, version)
|
||||
}
|
||||
case map[string]any:
|
||||
out := make(map[string]any, len(v))
|
||||
for k, x := range v {
|
||||
out[k] = withVersion(x, version)
|
||||
}
|
||||
return out
|
||||
case map[string]string:
|
||||
out := make(map[string]string, len(v))
|
||||
for k, x := range v {
|
||||
out[k] = strings.ReplaceAll(x, versionRef, version)
|
||||
}
|
||||
return out
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// checkBuild is the manifest's own account of what it builds.
|
||||
func (b *Build) problems(module string) []string {
|
||||
if b == nil {
|
||||
|
||||
@@ -95,3 +95,28 @@ func TestAResourceWithoutAVersionReferenceIsUntouched(t *testing.T) {
|
||||
t.Fatalf("a path naming no version became %q", path)
|
||||
}
|
||||
}
|
||||
|
||||
// A process's env can name the build's own version too (novox/hq issue 352): the controller is told which
|
||||
// build it is, and records what that build reads of the store's schema under it.
|
||||
func TestAProcessEnvCanNameTheBuildsOwnVersion(t *testing.T) {
|
||||
m := Manifest{
|
||||
Module: "mesh-controller",
|
||||
Build: &Build{Artifacts: []Artifact{{Name: "controller", Kind: ArtifactBundle, Language: "go", System: "arch"}}},
|
||||
Resources: []map[string]any{{
|
||||
"id": "controller", "type": "process", "artifact": "controller", "run": []any{"./mesh-controller", "serve"},
|
||||
"env": map[string]any{"MESH_CONTROLLER_VERSION": "${version}", "OTHER": "kept"},
|
||||
}},
|
||||
}
|
||||
got, err := m.Resolve([]Built{{Name: "controller", Kind: ArtifactBundle,
|
||||
Reference: "artifact-store://mesh-controller/controller", Digest: aDigest}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
env, _ := got.Resources[0]["env"].(map[string]any)
|
||||
if env["MESH_CONTROLLER_VERSION"] != "ad62528c47c7" || env["OTHER"] != "kept" {
|
||||
t.Fatalf("the env resolved to %v", env)
|
||||
}
|
||||
if run, _ := got.Resources[0]["run"].([]any); len(run) != 2 {
|
||||
t.Fatalf("the run was changed: %v", run)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user