package main import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "os" "sync" "testing" "git.novox.be/novox/mesh-sdk/go/provider" ) // bucketBus is the helper's bus over in-memory buckets. type bucketBus struct { mu sync.Mutex buckets map[string]map[string][]byte } func (b *bucketBus) Watch(_ context.Context, bucket string, handle func(string, []byte)) error { b.mu.Lock() defer b.mu.Unlock() for k, v := range b.buckets[bucket] { handle(k, v) } return nil } func (b *bucketBus) Put(_ context.Context, bucket, key string, value []byte) error { b.mu.Lock() defer b.mu.Unlock() if b.buckets[bucket] == nil { b.buckets[bucket] = map[string][]byte{} } b.buckets[bucket][key] = append([]byte(nil), value...) return nil } func (b *bucketBus) Delete(_ context.Context, bucket, key string) error { b.mu.Lock() defer b.mu.Unlock() delete(b.buckets[bucket], key) return nil } // A consumer of a route is answered with the names its plan's entry carries, and nothing of a credential (issue 792): // the entry's values are the controller's composition, copied unchanged, and no vault is asked. func TestAConsumerOfARouteIsToldItsNamesFromItsPlanEntry(t *testing.T) { bus := &bucketBus{buckets: map[string]map[string][]byte{}} plan, _ := json.Marshal(map[string]any{"node": "g14", "module": "gitea", "provisioning": map[string]any{ "provisions": []any{map[string]any{"provision": ProvisionName, "provider": "novox", "provider-module": ModuleName, "at": "10.7.0.1", "from": "novox", "as": "mesh_g14_gitea", "names": map[string]any{"name": "git.example.org", "internal-name": "git.novox.internal", "name-web": "git.example.org"}}}}}) if err := bus.Put(context.Background(), provider.AssignmentsBucket, "g14.gitea", plan); err != nil { t.Fatal(err) } p := newProvider("novox", bus, t.Logf) out, err := p.Pass(context.Background()) if err != nil { t.Fatal(err) } if len(out) != 1 || out[0].Status != provider.Answered { t.Fatalf("the pass did %+v", out) } var e provider.Entry if err := json.Unmarshal(bus.buckets[provider.BucketOf(ModuleName)]["g14.gitea.route"], &e); err != nil { t.Fatal(err) } if e.Values["name-web"] != "git.example.org" || e.Values["name"] != "git.example.org" || e.Values["internal-name"] != "git.novox.internal" || e.VaultName != "" || e.User != "" || e.Refused != "" { t.Fatalf("the entry is %+v", e) } } // route-proxy's own files and resources stay as they were (issue 792): the module answers its route through the // helper but is not marked moved, so its acme.env, its broker secret and its containers are composed as today and // the public ingress does not change. Everything of the manifest but its build, what it provides and the state it // keeps is pinned by this digest; changing the ingress means changing it here, on purpose. func TestRouteProxyIsNotMovedAndKeepsItsOwnFiles(t *testing.T) { raw, err := os.ReadFile("../../module.json") if err != nil { t.Fatal(err) } var m map[string]any if err := json.Unmarshal(raw, &m); err != nil { t.Fatal(err) } if _, moved := m["moved"]; moved { t.Fatal("route-proxy is marked moved: its own files would be handed") } provides, _ := m["provides"].([]any) if len(provides) != 1 || provides[0].(map[string]any)["answered-by"] != "helper" { t.Fatalf("route is not said to be answered by the helper: %v", provides) } for _, k := range []string{"build", "provides", "state", "reads"} { delete(m, k) } pinned, err := json.Marshal(m) // sorted keys, compact if err != nil { t.Fatal(err) } sum := sha256.Sum256(pinned) if got := hex.EncodeToString(sum[:]); got != "57a39de0943d8b33b9db6cb998ce97f8793602cca6cdfa5849c1c392390454a3" { t.Fatalf("route-proxy's own manifest changed: digest %s", got) } // The two acme templates are pinned the same way (issue 849): they are what the proxy's certificates are asked // with, so a change to either is a change to the public ingress. for file, want := range map[string]string{ "acme.env": "d66eba145c893080e30e013329fc00e51395f0c31fc8621a20c7b26abc11b52f", "internal-acme.env": "baf4da75379183758c8ae505be4dd8a79d5b089ae1e7c8ded4d232df40251df5", } { text, err := os.ReadFile("../../files/" + file) if err != nil { t.Fatal(err) } sum := sha256.Sum256(text) if got := hex.EncodeToString(sum[:]); got != want { t.Fatalf("route-proxy's files/%s changed: digest %s, pinned %s; if the change is meant, check that the "+ "public and internal ingress still ask for the certificates they should, then pin the new digest here", file, got, want) } } }