Skip to content

Commit 840cb13

Browse files
refactor(gateway): single-source API-level cluster name
Both xDS builders now build "<env>_<hash>" through clusterkey.APILevelName so the two paths cannot drift. Add known-answer vectors pinning SHA-256[:8].
1 parent a99b916 commit 840cb13

4 files changed

Lines changed: 40 additions & 2 deletions

File tree

gateway/gateway-controller/pkg/transform/restapi.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ func (t *RestAPITransformer) addUpstreamCluster(
344344
// keys stay continuous). ClusterKey and EnvoyClusterName are intentionally
345345
// the same string so the policy engine's `default_upstream_cluster` metadata
346346
// points at the actual Envoy cluster.
347-
clusterKey := upstreamName + "_" + clusterkey.APILevel(rdc.Metadata.UUID)
347+
clusterKey := clusterkey.APILevelName(upstreamName, rdc.Metadata.UUID)
348348

349349
rdc.UpstreamClusters[clusterKey] = &models.UpstreamCluster{
350350
BasePath: basePath,

gateway/gateway-controller/pkg/utils/clusterkey/clusterkey.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,9 @@ func APILevel(apiID string) string {
4444
sum := sha256.Sum256([]byte(apiID))
4545
return hex.EncodeToString(sum[:8])
4646
}
47+
48+
// APILevelName joins the env prefix ("main"/"sandbox") to the APILevel fragment
49+
// to form the full Envoy cluster name, so both xDS builders name clusters identically.
50+
func APILevelName(env, apiID string) string {
51+
return env + "_" + APILevel(apiID)
52+
}

gateway/gateway-controller/pkg/utils/clusterkey/clusterkey_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,36 @@ func TestAPILevel(t *testing.T) {
5050
b := APILevel("api-2")
5151
assert.NotEqual(t, a, b)
5252
})
53+
54+
// Known-answer vectors pin the algorithm to SHA-256[:8]. Without these, any
55+
// deterministic 16-hex function would satisfy the shape checks above.
56+
t.Run("known-answer vectors", func(t *testing.T) {
57+
assert.Equal(t, "f9811b73ac5d1a8d", APILevel("api-1"))
58+
assert.Equal(t, "2a28373e2cacc6ea", APILevel("test-api"))
59+
})
60+
61+
// Empty input is deterministic (the SHA-256 of the empty string), documenting
62+
// that APILevel itself does not reject empty apiIDs; non-emptiness is enforced
63+
// upstream at deploy time.
64+
t.Run("empty input is deterministic", func(t *testing.T) {
65+
assert.Equal(t, "e3b0c44298fc1c14", APILevel(""))
66+
})
67+
}
68+
69+
// TestAPILevelName validates the full cluster-name contract: the env prefix
70+
// joined to the APILevel fragment. Both xDS builders go through this helper, so
71+
// the two paths cannot drift.
72+
func TestAPILevelName(t *testing.T) {
73+
t.Run("joins env prefix to fragment", func(t *testing.T) {
74+
assert.Equal(t, "main_"+APILevel("api-1"), APILevelName("main", "api-1"))
75+
assert.Equal(t, "sandbox_"+APILevel("api-1"), APILevelName("sandbox", "api-1"))
76+
})
77+
78+
t.Run("main and sandbox share the fragment, differ by prefix", func(t *testing.T) {
79+
main := APILevelName("main", "api-1")
80+
sandbox := APILevelName("sandbox", "api-1")
81+
assert.NotEqual(t, main, sandbox)
82+
assert.Equal(t, "main_f9811b73ac5d1a8d", main)
83+
assert.Equal(t, "sandbox_f9811b73ac5d1a8d", sandbox)
84+
})
5385
}

gateway/gateway-controller/pkg/xds/translator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -983,7 +983,7 @@ func (t *Translator) resolveUpstreamCluster(apiID, upstreamName string, up *api.
983983
}
984984

985985
// Generate cluster name from URL-stable hash (URL intentionally excluded).
986-
clusterName := upstreamName + "_" + clusterkey.APILevel(apiID)
986+
clusterName := clusterkey.APILevelName(upstreamName, apiID)
987987

988988
return clusterName, parsedURL, timeout, nil
989989
}

0 commit comments

Comments
 (0)