Merge pull request #115403 from apelisse/hasschema-pool

apiextensions: Benchmark escaping in SchemaHas and pool Schemas
This commit is contained in:
Kubernetes Prow Robot 2023-02-05 22:35:02 -08:00 committed by GitHub
commit f3562d9dea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 12 deletions

View File

@ -23,6 +23,7 @@ import (
"reflect"
"regexp"
"strings"
"sync"
"unicode"
"unicode/utf8"
@ -1364,6 +1365,27 @@ func HasSchemaWith(spec *apiextensions.CustomResourceDefinitionSpec, pred func(s
return false
}
var schemaPool = sync.Pool{
New: func() any {
return new(apiextensions.JSONSchemaProps)
},
}
func schemaHasRecurse(s *apiextensions.JSONSchemaProps, pred func(s *apiextensions.JSONSchemaProps) bool) bool {
if s == nil {
return false
}
schema := schemaPool.Get().(*apiextensions.JSONSchemaProps)
defer schemaPool.Put(schema)
*schema = *s
return SchemaHas(schema, pred)
}
// SchemaHas recursively traverses the Schema and calls the `pred`
// predicate to see if the schema contains specific values.
//
// The predicate MUST NOT keep a copy of the json schema NOR modify the
// schema.
func SchemaHas(s *apiextensions.JSONSchemaProps, pred func(s *apiextensions.JSONSchemaProps) bool) bool {
if s == nil {
return false
@ -1374,60 +1396,60 @@ func SchemaHas(s *apiextensions.JSONSchemaProps, pred func(s *apiextensions.JSON
}
if s.Items != nil {
if s.Items != nil && SchemaHas(s.Items.Schema, pred) {
if s.Items != nil && schemaHasRecurse(s.Items.Schema, pred) {
return true
}
for i := range s.Items.JSONSchemas {
if SchemaHas(&s.Items.JSONSchemas[i], pred) {
if schemaHasRecurse(&s.Items.JSONSchemas[i], pred) {
return true
}
}
}
for i := range s.AllOf {
if SchemaHas(&s.AllOf[i], pred) {
if schemaHasRecurse(&s.AllOf[i], pred) {
return true
}
}
for i := range s.AnyOf {
if SchemaHas(&s.AnyOf[i], pred) {
if schemaHasRecurse(&s.AnyOf[i], pred) {
return true
}
}
for i := range s.OneOf {
if SchemaHas(&s.OneOf[i], pred) {
if schemaHasRecurse(&s.OneOf[i], pred) {
return true
}
}
if SchemaHas(s.Not, pred) {
if schemaHasRecurse(s.Not, pred) {
return true
}
for _, s := range s.Properties {
if SchemaHas(&s, pred) {
if schemaHasRecurse(&s, pred) {
return true
}
}
if s.AdditionalProperties != nil {
if SchemaHas(s.AdditionalProperties.Schema, pred) {
if schemaHasRecurse(s.AdditionalProperties.Schema, pred) {
return true
}
}
for _, s := range s.PatternProperties {
if SchemaHas(&s, pred) {
if schemaHasRecurse(&s, pred) {
return true
}
}
if s.AdditionalItems != nil {
if SchemaHas(s.AdditionalItems.Schema, pred) {
if schemaHasRecurse(s.AdditionalItems.Schema, pred) {
return true
}
}
for _, s := range s.Definitions {
if SchemaHas(&s, pred) {
if schemaHasRecurse(&s, pred) {
return true
}
}
for _, d := range s.Dependencies {
if SchemaHas(d.Schema, pred) {
if schemaHasRecurse(d.Schema, pred) {
return true
}
}

View File

@ -8624,6 +8624,30 @@ func TestSchemaHasDefaults(t *testing.T) {
}
}
func BenchmarkSchemaHas(b *testing.B) {
scheme := runtime.NewScheme()
codecs := serializer.NewCodecFactory(scheme)
if err := apiextensions.AddToScheme(scheme); err != nil {
b.Fatal(err)
}
fuzzerFuncs := fuzzer.MergeFuzzerFuncs(apiextensionsfuzzer.Funcs)
seed := int64(5577006791947779410)
f := fuzzer.FuzzerFor(fuzzerFuncs, rand.NewSource(seed), codecs)
// fuzz internal types
schema := &apiextensions.JSONSchemaProps{}
f.NilChance(0).NumElements(10, 10).MaxDepth(10).Fuzz(schema)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if SchemaHas(schema, func(_ *apiextensions.JSONSchemaProps) bool {
return false
}) {
b.Errorf("Function returned true")
}
}
}
var example = apiextensions.JSON(`"This is an example"`)
var validValidationSchema = &apiextensions.JSONSchemaProps{