Skip to content

Commit 0214d1d

Browse files
authored
Merge pull request #924 from guan404ming/replace-with-kin
Replace hand-rolled $ref resolver with kin-openapi
2 parents 8dab599 + 54fbda2 commit 0214d1d

File tree

5 files changed

+523
-13
lines changed

5 files changed

+523
-13
lines changed

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ require (
1818
github.com/docker/cli v27.5.1+incompatible
1919
github.com/fluxcd/pkg/oci v0.43.1
2020
github.com/fluxcd/pkg/tar v0.10.0
21+
github.com/getkin/kin-openapi v0.133.0
2122
github.com/go-git/go-git/v5 v5.16.4
2223
github.com/go-logr/logr v1.4.3
2324
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1
@@ -190,6 +191,7 @@ require (
190191
github.com/jmespath/go-jmespath v0.4.0 // indirect
191192
github.com/jmoiron/sqlx v1.4.0 // indirect
192193
github.com/joho/godotenv v1.5.1 // indirect
194+
github.com/josharian/intern v1.0.0 // indirect
193195
github.com/json-iterator/go v1.1.12 // indirect
194196
github.com/kevinburke/ssh_config v1.4.0 // indirect
195197
github.com/klauspost/compress v1.18.2 // indirect
@@ -206,6 +208,7 @@ require (
206208
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
207209
github.com/lib/pq v1.10.9 // indirect
208210
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
211+
github.com/mailru/easyjson v0.9.0 // indirect
209212
github.com/mattn/go-colorable v0.1.14 // indirect
210213
github.com/mattn/go-isatty v0.0.20 // indirect
211214
github.com/mattn/go-runewidth v0.0.19 // indirect
@@ -225,6 +228,7 @@ require (
225228
github.com/moby/term v0.5.2 // indirect
226229
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
227230
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
231+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
228232
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
229233
github.com/morikuni/aec v1.0.0 // indirect
230234
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
@@ -233,9 +237,12 @@ require (
233237
github.com/nats-io/nuid v1.0.1 // indirect
234238
github.com/novln/docker-parser v1.0.0 // indirect
235239
github.com/oapi-codegen/runtime v1.1.2 // indirect
240+
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
241+
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
236242
github.com/opencontainers/go-digest v1.0.0 // indirect
237243
github.com/openshift/api v3.9.0+incompatible // indirect
238244
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
245+
github.com/perimeterx/marshmallow v1.1.5 // indirect
239246
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
240247
github.com/pjbgf/sha1cd v0.5.0 // indirect
241248
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
@@ -265,6 +272,7 @@ require (
265272
github.com/valyala/fastjson v1.6.5 // indirect
266273
github.com/vbatts/tar-split v0.12.2 // indirect
267274
github.com/vektah/gqlparser/v2 v2.5.31 // indirect
275+
github.com/woodsbury/decimal128 v1.3.0 // indirect
268276
github.com/x448/float16 v0.8.4 // indirect
269277
github.com/xanzy/ssh-agent v0.3.3 // indirect
270278
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect

go.sum

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ github.com/fsouza/go-dockerclient v1.12.0 h1:S2f2crEUbBNCFiF06kR/GvioEB8EMsb3Td/
218218
github.com/fsouza/go-dockerclient v1.12.0/go.mod h1:YWUtjg8japrqD/80L98nTtCoxQFp5B5wrSsnyeB5lFo=
219219
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
220220
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
221+
github.com/getkin/kin-openapi v0.133.0 h1:pJdmNohVIJ97r4AUFtEXRXwESr8b0bD721u/Tz6k8PQ=
222+
github.com/getkin/kin-openapi v0.133.0/go.mod h1:boAciF6cXk5FhPqe/NQeBTeenbjqU4LhWBf09ILVvWE=
221223
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
222224
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
223225
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
@@ -279,6 +281,8 @@ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpv
279281
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
280282
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
281283
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
284+
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
285+
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
282286
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
283287
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
284288
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
@@ -367,6 +371,8 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
367371
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
368372
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
369373
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
374+
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
375+
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
370376
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
371377
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
372378
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
@@ -415,6 +421,8 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn
415421
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
416422
github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY=
417423
github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc=
424+
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
425+
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
418426
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
419427
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
420428
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -462,6 +470,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
462470
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
463471
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
464472
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
473+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
474+
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
465475
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
466476
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
467477
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@@ -480,6 +490,10 @@ github.com/novln/docker-parser v1.0.0 h1:PjEBd9QnKixcWczNGyEdfUrP6GR0YUilAqG7Wks
480490
github.com/novln/docker-parser v1.0.0/go.mod h1:oCeM32fsoUwkwByB5wVjsrsVQySzPWkl3JdlTn1txpE=
481491
github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI=
482492
github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
493+
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
494+
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
495+
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
496+
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
483497
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
484498
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
485499
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
@@ -494,6 +508,8 @@ github.com/openshift/api v3.9.0+incompatible h1:fJ/KsefYuZAjmrr3+5U9yZIZbTOpVkDD
494508
github.com/openshift/api v3.9.0+incompatible/go.mod h1:dh9o4Fs58gpFXGSYfnVxGR9PnV53I8TW84pQaJDdGiY=
495509
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
496510
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
511+
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
512+
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
497513
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
498514
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
499515
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
@@ -587,12 +603,16 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
587603
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
588604
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
589605
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
606+
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
607+
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
590608
github.com/valyala/fastjson v1.6.5 h1:LLabX0wszE1JDH9+IxLK6b+tb4B7gNdTEFTRasd0Ejw=
591609
github.com/valyala/fastjson v1.6.5/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
592610
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
593611
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
594612
github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k=
595613
github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts=
614+
github.com/woodsbury/decimal128 v1.3.0 h1:8pffMNWIlC0O5vbyHWFZAt5yWvWcrHA+3ovIIjVWss0=
615+
github.com/woodsbury/decimal128 v1.3.0/go.mod h1:C5UTmyTjW3JftjUFzOVhC20BEQa2a4ZKOB5I6Zjb+ds=
596616
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
597617
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
598618
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=

utils/component/openapi_generator.go

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"cuelang.org/go/cue"
1010
"cuelang.org/go/cue/cuecontext"
1111
cueJson "cuelang.org/go/encoding/json"
12+
"github.com/getkin/kin-openapi/openapi3"
1213
"github.com/meshery/meshkit/generators/models"
1314
"github.com/meshery/meshkit/utils"
1415
"github.com/meshery/meshkit/utils/manifests"
@@ -176,35 +177,74 @@ func getResourceScope(manifest string, kind string) (bool, error) {
176177
}
177178

178179
func getResolvedManifest(manifest string) (string, error) {
180+
// Normalize YAML input to JSON.
179181
var m map[string]interface{}
180-
181182
err := yaml.Unmarshal([]byte(manifest), &m)
182183
if err != nil {
183184
return "", utils.ErrDecodeYaml(err)
184185
}
185-
186186
byt, err := json.Marshal(m)
187187
if err != nil {
188188
return "", utils.ErrMarshal(err)
189189
}
190190

191-
cuectx := cuecontext.New()
192-
cueParsedManExpr, err := cueJson.Extract("", byt)
191+
loader := openapi3.NewLoader()
192+
doc, err := loader.LoadFromData(byt)
193193
if err != nil {
194194
return "", ErrGetSchema(err)
195195
}
196-
197-
parsedManifest := cuectx.BuildExpr(cueParsedManExpr)
198-
definitions, err := utils.Lookup(parsedManifest, "components.schemas")
199-
if err != nil {
196+
if doc.Components == nil || len(doc.Components.Schemas) == 0 {
200197
return "", ErrNoSchemasFound
201198
}
202-
resol := manifests.ResolveOpenApiRefs{}
203-
cache := make(map[string][]byte)
204-
resolved, err := resol.ResolveReferences(byt, definitions, cache)
199+
stack := make(map[*openapi3.Schema]bool)
200+
for _, schemaRef := range doc.Components.Schemas {
201+
clearSchemaRefs(schemaRef, stack)
202+
}
203+
resolved, err := json.Marshal(doc)
205204
if err != nil {
206205
return "", err
207206
}
208-
manifest = string(resolved)
209-
return manifest, nil
207+
return string(resolved), nil
208+
}
209+
210+
// clearSchemaRefs recursively clears $ref strings on all nested SchemaRefs
211+
// so that json.Marshal outputs fully inlined schemas. The stack set tracks
212+
// Schema values (not SchemaRef pointers) on the current recursion path to
213+
// detect circular references. kin-openapi resolves $refs by creating
214+
// different SchemaRef objects that share the same underlying Schema pointer,
215+
// so tracking by *Schema is necessary to catch all cycles.
216+
func clearSchemaRefs(sr *openapi3.SchemaRef, stack map[*openapi3.Schema]bool) {
217+
if sr == nil {
218+
return
219+
}
220+
sr.Ref = ""
221+
s := sr.Value
222+
if s == nil {
223+
return
224+
}
225+
if stack[s] {
226+
sr.Value = &openapi3.Schema{}
227+
return
228+
}
229+
stack[s] = true
230+
for _, child := range s.AllOf {
231+
clearSchemaRefs(child, stack)
232+
}
233+
for _, child := range s.AnyOf {
234+
clearSchemaRefs(child, stack)
235+
}
236+
for _, child := range s.OneOf {
237+
clearSchemaRefs(child, stack)
238+
}
239+
clearSchemaRefs(s.Not, stack)
240+
if s.Items != nil {
241+
clearSchemaRefs(s.Items, stack)
242+
}
243+
for _, prop := range s.Properties {
244+
clearSchemaRefs(prop, stack)
245+
}
246+
if s.AdditionalProperties.Schema != nil {
247+
clearSchemaRefs(s.AdditionalProperties.Schema, stack)
248+
}
249+
delete(stack, s)
210250
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package component
2+
3+
import (
4+
"fmt"
5+
"io"
6+
"net/http"
7+
"testing"
8+
)
9+
10+
// TestGetResolvedManifest_RealKubernetesSpec downloads the actual Kubernetes
11+
// OpenAPI spec and runs it through getResolvedManifest to verify that circular
12+
// references (e.g. JSONSchemaProps) do not cause a stack overflow.
13+
func TestGetResolvedManifest_RealKubernetesSpec(t *testing.T) {
14+
if testing.Short() {
15+
t.Skip("skipping integration test in short mode")
16+
}
17+
18+
// apiextensions spec contains JSONSchemaProps which references itself circularly.
19+
const specURL = "https://raw.githubusercontent.com/kubernetes/kubernetes/release-1.32/api/openapi-spec/v3/apis__apiextensions.k8s.io__v1_openapi.json"
20+
resp, err := http.Get(specURL)
21+
if err != nil {
22+
t.Fatalf("failed to fetch kubernetes spec: %v", err)
23+
}
24+
defer resp.Body.Close()
25+
26+
body, err := io.ReadAll(resp.Body)
27+
if err != nil {
28+
t.Fatalf("failed to read response body: %v", err)
29+
}
30+
31+
out, err := getResolvedManifest(string(body))
32+
if err != nil {
33+
t.Fatalf("getResolvedManifest failed: %v", err)
34+
}
35+
36+
if len(out) == 0 {
37+
t.Fatal("expected non-empty output")
38+
}
39+
fmt.Printf("resolved manifest length: %d bytes\n", len(out))
40+
}

0 commit comments

Comments
 (0)