diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/formats.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/formats.go index f818010f1b7..c11f1c627a7 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/formats.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/formats.go @@ -105,6 +105,22 @@ func StripUnsupportedFormatsPostProcessorForVersion(compatibilityVersion *versio } } +// GetUnrecognizedFormats returns a list of unrecognized formats found in the given schema. +// It uses the same source of truth as StripUnsupportedFormatsPostProcessorForVersion. +func GetUnrecognizedFormats(schema *spec.Schema, compatibilityVersion *version.Version) []string { + var unrecognizedFormats []string + if len(schema.Format) == 0 { + return unrecognizedFormats + } + + normalized := strings.ReplaceAll(schema.Format, "-", "") // go-openapi default format name normalization + if !supportedFormatsAtVersion(compatibilityVersion).supported.Has(normalized) { + unrecognizedFormats = append(unrecognizedFormats, schema.Format) + } + + return unrecognizedFormats +} + type versionedFormats struct { introducedVersion *version.Version formats sets.Set[string] diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/formats_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/formats_test.go index c93a7f74d1d..7558444b218 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/formats_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/formats_test.go @@ -138,3 +138,86 @@ func TestSupportedFormats(t *testing.T) { }) } } + +func TestGetUnrecognizedFormats(t *testing.T) { + testCases := []struct { + name string + schema *spec.Schema + compatibilityVersion *version.Version + expectedFormats []string + }{ + { + name: "empty format", + schema: &spec.Schema{SchemaProps: spec.SchemaProps{Format: ""}}, + compatibilityVersion: version.MajorMinor(1, 0), + expectedFormats: []string{}, + }, + { + name: "recognized format at version 1.0", + schema: &spec.Schema{SchemaProps: spec.SchemaProps{Format: "email"}}, + compatibilityVersion: version.MajorMinor(1, 0), + expectedFormats: []string{}, + }, + { + name: "unrecognized format at version 1.0", + schema: &spec.Schema{SchemaProps: spec.SchemaProps{Format: "unknown-format"}}, + compatibilityVersion: version.MajorMinor(1, 0), + expectedFormats: []string{"unknown-format"}, + }, + { + name: "recognized format with normalization", + schema: &spec.Schema{SchemaProps: spec.SchemaProps{Format: "k8s-short-name"}}, + compatibilityVersion: version.MajorMinor(1, 34), + expectedFormats: []string{}, + }, + { + name: "unrecognized format with normalization", + schema: &spec.Schema{SchemaProps: spec.SchemaProps{Format: "k8s-long-name"}}, + compatibilityVersion: version.MajorMinor(1, 33), + expectedFormats: []string{"k8s-long-name"}, + }, + { + name: "format introduced in later version", + schema: &spec.Schema{SchemaProps: spec.SchemaProps{Format: "k8s-short-name"}}, + compatibilityVersion: version.MajorMinor(1, 0), + expectedFormats: []string{"k8s-short-name"}, + }, + { + name: "format with dash normalization", + schema: &spec.Schema{SchemaProps: spec.SchemaProps{Format: "k8sshortname"}}, + compatibilityVersion: version.MajorMinor(1, 34), + expectedFormats: []string{}, + }, + { + name: "recognized format at exact version", + schema: &spec.Schema{SchemaProps: spec.SchemaProps{Format: "uuid"}}, + compatibilityVersion: version.MajorMinor(1, 0), + expectedFormats: []string{}, + }, + { + name: "recognized format at higher version", + schema: &spec.Schema{SchemaProps: spec.SchemaProps{Format: "email"}}, + compatibilityVersion: version.MajorMinor(1, 35), + expectedFormats: []string{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got := GetUnrecognizedFormats(tc.schema, tc.compatibilityVersion) + + if len(got) != len(tc.expectedFormats) { + t.Errorf("expected %d unrecognized formats, got %d", len(tc.expectedFormats), len(got)) + return + } + + // Convert to sets for comparison to handle order differences + gotSet := sets.New(got...) + expectedSet := sets.New(tc.expectedFormats...) + + if !gotSet.Equal(expectedSet) { + t.Errorf("expected unrecognized formats %v, got %v", tc.expectedFormats, got) + } + }) + } +}