From b1d68f5c80eadba454c779e671fc707fc5acd69d Mon Sep 17 00:00:00 2001 From: unkin-agent Date: Fri, 9 Oct 2026 23:49:42 +1100 Subject: [PATCH] Check backend hosts only when the source fact is enabled, compare them case-insensitively --- config.go | 5 ++++- config_test.go | 21 +++++++++++++++++++++ source.go | 13 +++---------- source_test.go | 5 +++-- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/config.go b/config.go index f62cb6c..ca2ef29 100644 --- a/config.go +++ b/config.go @@ -296,7 +296,7 @@ func backendHost(raw string) string { if err != nil { return "" } - return u.Hostname() + return strings.ToLower(u.Hostname()) } func (c Config) Validate() error { @@ -314,6 +314,9 @@ func (c Config) Validate() error { return fmt.Errorf("duplicate backend name %q", b.Name) } seen[b.Name] = true + if !c.SourceFactEnabled { + continue + } host := backendHost(b.URL) if host == "" { return fmt.Errorf("backend %q url %q has no host", b.Name, b.URL) diff --git a/config_test.go b/config_test.go index d2297a5..aca37a4 100644 --- a/config_test.go +++ b/config_test.go @@ -234,6 +234,15 @@ func TestValidate(t *testing.T) { {"url without host", func(c *Config) { c.Backends[0].URL = "localhost:18080" }, true}, {"shared host on another port", func(c *Config) { c.Backends[1].URL = "http://localhost:18081" }, true}, {"same host differing by scheme", func(c *Config) { c.Backends[1].URL = "https://localhost" }, true}, + {"same host differing by case", func(c *Config) { c.Backends[1].URL = "http://LocalHost:18081" }, true}, + {"shared host while source fact disabled", func(c *Config) { + c.SourceFactEnabled = false + c.Backends[1].URL = "http://localhost:18081" + }, false}, + {"url without host while source fact disabled", func(c *Config) { + c.SourceFactEnabled = false + c.Backends[0].URL = "localhost:18080" + }, false}, {"bad merge", func(c *Config) { c.Merge = "wrong" }, true}, {"zero timeout", func(c *Config) { c.Timeout = 0 }, true}, {"empty source fact while enabled", func(c *Config) { c.SourceFact = "" }, true}, @@ -515,3 +524,15 @@ func clearEnv(t *testing.T) { t.Setenv(envPrefix+k, "") } } + +func TestBackendHost(t *testing.T) { + for raw, want := range map[string]string{ + "https://PuppetDB.Example:8081/x": "puppetdb.example", + "http://[::1]:8080": "::1", + "localhost:8080": "", + } { + if got := backendHost(raw); got != want { + t.Errorf("backendHost(%q) = %q, want %q", raw, got, want) + } + } +} diff --git a/source.go b/source.go index 58cfe01..f60d62f 100644 --- a/source.go +++ b/source.go @@ -13,7 +13,7 @@ import ( // callers need no branch. type sourceInjector struct { name string - // hosts maps backend name to the value emitted; an unmapped name is emitted as-is. + // hosts maps backend name to the value emitted. hosts map[string]string // inject is false when the query shape rules synthesis out. Suppression of an // upstream fact of the same name does not depend on it. @@ -38,13 +38,6 @@ func (si *sourceInjector) claims(factName string) bool { return si != nil && factName != "" && factName == si.name } -func (si *sourceInjector) value(backend string) string { - if h, ok := si.hosts[backend]; ok { - return h - } - return backend -} - // injects reports whether this response may carry the synthetic record. func (si *sourceInjector) injects() bool { return si != nil && si.inject @@ -79,7 +72,7 @@ func (si *sourceInjector) factRecord(certname, backend, environment string) json Environment string `json:"environment"` Name string `json:"name"` Value string `json:"value"` - }{Certname: certname, Environment: environment, Name: si.name, Value: si.value(backend)}) + }{Certname: certname, Environment: environment, Name: si.name, Value: si.hosts[backend]}) if err != nil { return nil } @@ -96,7 +89,7 @@ func (si *sourceInjector) stamp(raw json.RawMessage, backend string) json.RawMes if json.Unmarshal(raw, &obj) != nil || obj == nil { return raw } - value, err := json.Marshal(si.value(backend)) + value, err := json.Marshal(si.hosts[backend]) if err != nil { return raw } diff --git a/source_test.go b/source_test.go index cb1fca3..1763c50 100644 --- a/source_test.go +++ b/source_test.go @@ -560,10 +560,11 @@ func TestMergeFacts_GatedSuppressesUpstream(t *testing.T) { func TestMergeFacts_SourceOrderedAfterOwnersFacts(t *testing.T) { a := recs(t, "a", fact("h1", "role", "web-a", ""), fact("h1", "kernel", "Linux", "")) b := recs(t, "b", fact("h1", "role", "web-b", "")) - merged := mergeFacts([]backendResult{a, b}, nil, &sourceInjector{name: defaultSourceFact, inject: true}) + si := &sourceInjector{name: defaultSourceFact, hosts: map[string]string{"a": "a.example", "b": "b.example"}, inject: true} + merged := mergeFacts([]backendResult{a, b}, nil, si) got := factValues(t, merged) - want := []string{"h1:role=web-a", "h1:kernel=Linux", "h1:" + defaultSourceFact + "=a"} + want := []string{"h1:role=web-a", "h1:kernel=Linux", "h1:" + defaultSourceFact + "=a.example"} if !slices.Equal(got, want) { t.Errorf("merged = %v, want %v", got, want) }