From 4215b4054737d495fb424621b66b5163133e3a40 Mon Sep 17 00:00:00 2001 From: lead-57 Date: Fri, 16 Oct 2026 21:32:13 +0200 Subject: [PATCH] Read the whole plan the controller writes: at, from, as, names and credential-tool (issue 954) The client decodes a plan strictly, and since the controller carries these fields (issues 923 and 928) it refused the plans of gitea, keycloak and claude-code. --- .../cmd/mesh-provisioner-client/plan.go | 10 +++++ .../whole_plan_test.go | 39 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 modules/mesh-provisioner-client/cmd/mesh-provisioner-client/whole_plan_test.go diff --git a/modules/mesh-provisioner-client/cmd/mesh-provisioner-client/plan.go b/modules/mesh-provisioner-client/cmd/mesh-provisioner-client/plan.go index 4e56e034..8caf8968 100644 --- a/modules/mesh-provisioner-client/cmd/mesh-provisioner-client/plan.go +++ b/modules/mesh-provisioner-client/cmd/mesh-provisioner-client/plan.go @@ -97,6 +97,16 @@ type PlannedProvision struct { // Contributes is what this module tells the provider about itself for this provision (a database's name, its // extensions): the consumer manifest's `contributes`, which the provider's helper reads and the client keeps. Contributes map[string]any `json:"contributes,omitempty"` + // At, From and As are where the provider is on the private network, its machine, and who the consumer is at + // its end (issue 788); Names are what the mesh calls the consumer through this provision (issue 792). The + // provider's helper reads them from the plan; the client keeps them, so the plan reads whole (issue 954). + At string `json:"at,omitempty"` + From string `json:"from,omitempty"` + As string `json:"as,omitempty"` + Names map[string]string `json:"names,omitempty"` + // CredentialTool is, for a software-issued offer, the provider's tool the vault asks for the value (issue 928); + // the vault acts on it and this client only reads it. + CredentialTool string `json:"credential-tool,omitempty"` } // issuedForHolder is whether the entry names a value its provider's software issued for this holder alone (issue diff --git a/modules/mesh-provisioner-client/cmd/mesh-provisioner-client/whole_plan_test.go b/modules/mesh-provisioner-client/cmd/mesh-provisioner-client/whole_plan_test.go new file mode 100644 index 00000000..1c2efa79 --- /dev/null +++ b/modules/mesh-provisioner-client/cmd/mesh-provisioner-client/whole_plan_test.go @@ -0,0 +1,39 @@ +package main + +import ( + "encoding/json" + "fmt" + "strings" + "testing" +) + +// A plan provision carries everything the controller's PlanProvision writes (issue 954): what the consumer +// contributes, where the provider is and who the consumer is at its end (issue 788), the names the mesh calls it +// (issue 792) and, for a software-issued offer, the tool the vault asks (issue 928). The client still refuses a field +// nobody writes. +func TestAPlanProvisionWithEveryFieldTheControllerWritesIsRead(t *testing.T) { + p, err := json.Marshal(contributor(t, "shell:zsh:normal", "alias nox=mesh-cli")) + if err != nil { + t.Fatal(err) + } + provision := `"provisions":[{"provision":"route","local":"web","provider":"novox","provider-module":"route-proxy",` + + `"credential-form":"pair","contributes":{"label":"git"},"at":"10.10.0.1","from":"novox","as":"gitea",` + + `"names":{"name-web":"git.example.org"}},{"name":"gitea-read","provision":"gitea-read","provider":"novox",` + + `"provider-module":"gitea","credential-form":"issued","credential-tool":"gitea_agent_token",` + + `"path":"/var/lib/claude-code/gitea-read.secret"}],"items":` + value := fmt.Sprintf(`{"node":"g14","module":"mesh-cli","provisioning":%s}`, + strings.Replace(string(p), `"items":`, provision, 1)) + _, got, err := planOf("g14", "g14.mesh-cli", []byte(value)) + if err != nil { + t.Fatalf("a plan with the controller's fields is refused: %v", err) + } + if len(got.Provisions) != 2 || got.Provisions[0].At != "10.10.0.1" || got.Provisions[0].From != "novox" || + got.Provisions[0].As != "gitea" || got.Provisions[0].Names["name-web"] != "git.example.org" || + got.Provisions[1].CredentialTool != "gitea_agent_token" { + t.Fatalf("the plan's provisions read as %+v", got.Provisions) + } + spoilt := strings.Replace(value, `"at":`, `"surprise":1,"at":`, 1) + if _, _, err := planOf("g14", "g14.mesh-cli", []byte(spoilt)); err == nil { + t.Fatalf("a provision with a field nobody writes is read: %s", spoilt) + } +}