Repository: kubernetes/kube-openapi

Issue:
FilterSpecByPathsWithoutSideEffects does not follow definitions references from parameter references
The OpenAPI V2 document we receive now looks like the following partial JSON document and after calling [FilterSpecByPathsWithoutSideEffects](https://github.com/kubernetes/kube-openapi/blob/c8a335a9a2ffc5aff16dfef74896a1ee34eb235d/pkg/aggregator/aggregator.go#L54) we no longer have `DeleteOptions` in the list of definitions.

This is because the definition is found in used in some parameters that the walker DOES find (eg: through eviction), but then in filtered path (eg: globalrolebindings) it doesn't find it, so filter removes it.


```json5
{
  "paths": {
    // Eviction references Eviction def ref, leading to DeleteOption be "used"
    "/api/v1/namespaces/{namespace}/pods/{name}/eviction": {
      "post": {
        "parameters": [
          {
            "name": "body",
            "in": "body",
            "required": true,
            "schema": {
              "$ref": "#/definitions/io.k8s.api.policy.v1.Eviction"
            }
          }
        ],
    ...

    "/apis/management.cattle.io/v3/globalrolebindings/{name}": {
      "delete": {
        "parameters": [
          {
            "$ref": "#/parameters/body-2Y1dVQaQ"
          },
  "definitions": {
    ...
    "io.k8s.api.policy.v1.Eviction": {
      "description": "Eviction evicts a pod from its node subject to certain policies and safety constraints. This is a subresource of Pod.  A request to cause such an eviction is created by POSTing to .../pods/<pod name>/evictions.",
      "type": "object",
      "properties": {
        "deleteOptions": {
          "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions"
        },
       ...
...
  "parameters": {
    "body-2Y1dVQaQ": {
      "name": "body",
      "in": "body",
      "schema": {
        "$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions"
      }
    },
```

Discussion:

Solution patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 72d8df2..ba5b874 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,7 +8,7 @@ jobs:
   ci:
     strategy:
       matrix:
-        go-version: [ '1.21', '1.22', '1.23', '1.24']
+        go-version: ['1.22', '1.23', '1.24']
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v4
@@ -37,7 +37,7 @@ jobs:
     # in k/k.
     - name: Run verify scripts
       run: |
-        ./hack/verify-go-directive.sh 1.21
+        ./hack/verify-go-directive.sh 1.22
   required:
     # The name of the ci jobs above change based on the golang version.
     # Use this as a stable required job that depends on the above jobs.
diff --git a/cmd/openapi2smd/openapi2smd.go b/cmd/openapi2smd/openapi2smd.go
index 2a6d0fb..a60babe 100644
--- a/cmd/openapi2smd/openapi2smd.go
+++ b/cmd/openapi2smd/openapi2smd.go
@@ -22,7 +22,7 @@ import (
 	"os"
 
 	openapi_v2 "github.com/google/gnostic-models/openapiv2"
-	yaml "sigs.k8s.io/yaml/goyaml.v2"
+	yaml "go.yaml.in/yaml/v2"
 
 	"k8s.io/kube-openapi/pkg/schemaconv"
 	"k8s.io/kube-openapi/pkg/util/proto"
diff --git a/go.mod b/go.mod
index bb920c3..c6bdba5 100644
--- a/go.mod
+++ b/go.mod
@@ -1,13 +1,13 @@
 module k8s.io/kube-openapi
 
-go 1.21
+go 1.22
 
 require (
 	github.com/NYTimes/gziphandler v1.1.1
 	github.com/emicklei/go-restful/v3 v3.11.0
 	github.com/go-openapi/jsonreference v0.20.1
 	github.com/go-openapi/swag v0.23.0
-	github.com/google/gnostic-models v0.6.9
+	github.com/google/gnostic-models v0.7.0
 	github.com/google/go-cmp v0.6.0
 	github.com/google/uuid v1.6.0
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
@@ -15,9 +15,10 @@ require (
 	github.com/onsi/gomega v1.33.1
 	github.com/spf13/pflag v1.0.5
 	github.com/stretchr/testify v1.9.0
+	go.yaml.in/yaml/v2 v2.4.2
+	go.yaml.in/yaml/v3 v3.0.3
 	golang.org/x/tools v0.24.0
 	google.golang.org/protobuf v1.35.1
-	gopkg.in/yaml.v3 v3.0.1
 	k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7
 	k8s.io/klog/v2 v2.130.1
 	k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
@@ -45,4 +46,5 @@ require (
 	golang.org/x/sync v0.8.0 // indirect
 	golang.org/x/sys v0.23.0 // indirect
 	golang.org/x/text v0.17.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index a75dd2d..4b80ad8 100644
--- a/go.sum
+++ b/go.sum
@@ -18,8 +18,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
 github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
 github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
-github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
-github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
+github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
+github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -67,6 +67,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
+go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
+go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
+go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
 golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
 golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
diff --git a/pkg/aggregator/walker.go b/pkg/aggregator/walker.go
index f3090fc..b4d6ae0 100644
--- a/pkg/aggregator/walker.go
+++ b/pkg/aggregator/walker.go
@@ -48,16 +48,25 @@ func walkOnAllReferences(walkRef func(ref *spec.Ref), root *spec.Swagger) {
 		walkRef(ref)
 
 		refStr := ref.String()
-		if refStr == "" || !strings.HasPrefix(refStr, definitionPrefix) {
+		if refStr == "" {
 			return
 		}
-		defName := refStr[len(definitionPrefix):]
 
-		if _, found := root.Definitions[defName]; found && !alreadyVisited[refStr] {
-			alreadyVisited[refStr] = true
-			def := root.Definitions[defName]
-			walker.walkSchema(&def)
+		if strings.HasPrefix(refStr, parameterPrefix) {
+			paramName := refStr[len(parameterPrefix):]
+			if param, found := root.Parameters[paramName]; found {
+				walker.walkParam(param)
+			}
+		} else if strings.HasPrefix(refStr, definitionPrefix) {
+			defName := refStr[len(definitionPrefix):]
+
+			if _, found := root.Definitions[defName]; found && !alreadyVisited[refStr] {
+				alreadyVisited[refStr] = true
+				def := root.Definitions[defName]
+				walker.walkSchema(&def)
+			}
 		}
+
 	}
 	walker.Start()
 }
@@ -116,11 +125,15 @@ func (s *readonlyReferenceWalker) walkParams(params []spec.Parameter) {
 		return
 	}
 	for _, param := range params {
-		s.walkRefCallback(&param.Ref)
-		s.walkSchema(param.Schema)
-		if param.Items != nil {
-			s.walkRefCallback(&param.Items.Ref)
-		}
+		s.walkParam(param)
+	}
+}
+
+func (s *readonlyReferenceWalker) walkParam(param spec.Parameter) {
+	s.walkRefCallback(&param.Ref)
+	s.walkSchema(param.Schema)
+	if param.Items != nil {
+		s.walkRefCallback(&param.Items.Ref)
 	}
 }
 
diff --git a/pkg/common/common.go b/pkg/common/common.go
index e4ce843..da2e8f1 100644
--- a/pkg/common/common.go
+++ b/pkg/common/common.go
@@ -48,11 +48,11 @@ type GetOpenAPIDefinitions func(ReferenceCallback) map[string]OpenAPIDefinition
 // GetOpenAPITypeFormat for more information about trade-offs of using this interface or GetOpenAPITypeFormat method when
 // possible.
 type OpenAPIDefinitionGetter interface {
-	OpenAPIDefinition() *OpenAPIDefinition
+	OpenAPIDefinition() OpenAPIDefinition
 }
 
 type OpenAPIV3DefinitionGetter interface {
-	OpenAPIV3Definition() *OpenAPIDefinition
+	OpenAPIV3Definition() OpenAPIDefinition
 }
 
 type PathHandler interface {
diff --git a/pkg/util/proto/document.go b/pkg/util/proto/document.go
index 1b758ab..c7b69b2 100644
--- a/pkg/util/proto/document.go
+++ b/pkg/util/proto/document.go
@@ -22,7 +22,7 @@ import (
 	"strings"
 
 	openapi_v2 "github.com/google/gnostic-models/openapiv2"
-	yaml "sigs.k8s.io/yaml/goyaml.v2"
+	yaml "go.yaml.in/yaml/v2"
 )
 
 func newSchemaError(path *Path, format string, a ...interface{}) error {
diff --git a/pkg/util/proto/document_v3.go b/pkg/util/proto/document_v3.go
index d9f2896..8694c6c 100644
--- a/pkg/util/proto/document_v3.go
+++ b/pkg/util/proto/document_v3.go
@@ -22,7 +22,7 @@ import (
 	"strings"
 
 	openapi_v3 "github.com/google/gnostic-models/openapiv3"
-	"gopkg.in/yaml.v3"
+	"go.yaml.in/yaml/v3"
 )
 
 // Temporary parse implementation to be used until gnostic->kube-openapi conversion
diff --git a/pkg/validation/strfmt/format.go b/pkg/validation/strfmt/format.go
index c85067a..6981a40 100644
--- a/pkg/validation/strfmt/format.go
+++ b/pkg/validation/strfmt/format.go
@@ -16,6 +16,7 @@ package strfmt
 
 import (
 	"encoding"
+	"encoding/json"
 	"reflect"
 	"strings"
 	"sync"
@@ -231,3 +232,26 @@ func (f *defaultFormats) Parse(name, data string) (interface{}, error) {
 	}
 	return nil, errors.InvalidTypeName(name)
 }
+
+// unmarshalJSON provides a generic implementation of json.Unmarshaler interface's UnmarshalJSON function for basic string formats.
+func unmarshalJSON[T ~string](r *T, data []byte) error {
+	if string(data) == jsonNull {
+		return nil
+	}
+	var ustr string
+	if err := json.Unmarshal(data, &ustr); err != nil {
+		return err
+	}
+	*r = T(ustr)
+	return nil
+}
+
+// deepCopy provides a generic implementation of DeepCopy for basic string formats.
+func deepCopy[T ~string](r *T) *T {
+	if r == nil {
+		return nil
+	}
+	out := new(T)
+	*out = *r
+	return out
+}
diff --git a/pkg/validation/strfmt/kubernetes-extensions.go b/pkg/validation/strfmt/kubernetes-extensions.go
new file mode 100644
index 0000000..64d6654
--- /dev/null
+++ b/pkg/validation/strfmt/kubernetes-extensions.go
@@ -0,0 +1,143 @@
+// Copyright 2024 go-swagger maintainers
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package strfmt
+
+import (
+	"encoding/json"
+	"regexp"
+)
+
+const k8sPrefix = "k8s-"
+
+func init() {
+	// register formats in the KubernetesExtensions registry:
+	//   - k8s-short-name
+	//   - k8s-long-name
+	shortName := ShortName("")
+	Default.Add(k8sPrefix+"short-name", &shortName, IsShortName)
+
+	longName := LongName("")
+	Default.Add(k8sPrefix+"long-name", &longName, IsLongName)
+}
+
+// ShortName is a name, up to 63 characters long, composed of alphanumeric
+// characters and dashes, which cannot begin or end with a dash.
+//
+// ShortName almost conforms to the definition of a label in DNS (RFC 1123),
+// except that uppercase letters are not allowed.
+//
+// xref: https://github.com/kubernetes/kubernetes/issues/71140
+//
+// swagger:strfmt k8s-short-name
+type ShortName string
+
+func (r ShortName) MarshalText() ([]byte, error) {
+	return []byte(string(r)), nil
+}
+
+func (r *ShortName) UnmarshalText(data []byte) error { // validation is performed later on
+	*r = ShortName(data)
+	return nil
+}
+
+func (r ShortName) String() string {
+	return string(r)
+}
+
+func (r ShortName) MarshalJSON() ([]byte, error) {
+	return json.Marshal(string(r))
+}
+
+func (r *ShortName) UnmarshalJSON(data []byte) error {
+	return unmarshalJSON(r, data)
+}
+
+func (r *ShortName) DeepCopyInto(out *ShortName) {
+	*out = *r
+}
+
+func (r *ShortName) DeepCopy() *ShortName {
+	return deepCopy(r)
+}
+
+const shortNameFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
+
+// ShortNameMaxLength is a label's max length in DNS (RFC 1123)
+const ShortNameMaxLength int = 63
+
+var shortNameRegexp = regexp.MustCompile("^" + shortNameFmt + "$")
+
+// IsShortName checks if a string is a valid ShortName.
+func IsShortName(value string) bool {
+	return len(value) <= ShortNameMaxLength &&
+		shortNameRegexp.MatchString(value)
+}
+
+// LongName is a name, up to 253 characters long, composed of dot-separated
+// segments; each segment uses only alphanumerics and dashes (no
+// leading/trailing).
+//
+// LongName almost conforms to the definition of a subdomain in DNS (RFC 1123),
+// except that uppercase letters are not allowed, and there is no max length
+// limit of 63 for each of the dot-separated DNS Labels that make up the
+// subdomain.
+//
+// xref: https://github.com/kubernetes/kubernetes/issues/71140
+// xref: https://github.com/kubernetes/kubernetes/issues/79351
+//
+// swagger:strfmt k8s-long-name
+type LongName string
+
+func (r LongName) MarshalText() ([]byte, error) {
+	return []byte(string(r)), nil
+}
+
+func (r *LongName) UnmarshalText(data []byte) error { // validation is performed later on
+	*r = LongName(data)
+	return nil
+}
+
+func (r LongName) String() string {
+	return string(r)
+}
+
+func (r LongName) MarshalJSON() ([]byte, error) {
+	return json.Marshal(string(r))
+}
+
+func (r *LongName) UnmarshalJSON(data []byte) error {
+	return unmarshalJSON(r, data)
+}
+
+func (r *LongName) DeepCopyInto(out *LongName) {
+	*out = *r
+}
+
+func (r *LongName) DeepCopy() *LongName {
+	return deepCopy(r)
+}
+
+const longNameFmt string = shortNameFmt + "(\\." + shortNameFmt + ")*"
+
+// LongNameMaxLength is a subdomain's max length in DNS (RFC 1123)
+const LongNameMaxLength int = 253
+
+var longNameRegexp = regexp.MustCompile("^" + longNameFmt + "$")
+
+// IsLongName checks if a string is a valid LongName.
+func IsLongName(value string) bool {
+	return len(value) <= LongNameMaxLength &&
+		longNameRegexp.MatchString(value)
+}



Test patch:
diff --git a/pkg/aggregator/aggregator_test.go b/pkg/aggregator/aggregator_test.go
index 62b871d..7a9f503 100644
--- a/pkg/aggregator/aggregator_test.go
+++ b/pkg/aggregator/aggregator_test.go
@@ -45,7 +45,9 @@ func (d DebugSpec) String() string {
 }
 func TestFilterSpecs(t *testing.T) {
 	var spec1, spec1_filtered *spec.Swagger
-	yaml.Unmarshal([]byte(`
+	ast := assert.New(t)
+
+	err := yaml.Unmarshal([]byte(`
 swagger: "2.0"
 paths:
   /test:
@@ -65,6 +67,11 @@ paths:
         405:
           description: "Invalid input"
           $ref: "#/definitions/InvalidInput"
+    delete:
+      summary: "Test API Delete"
+      operationId: "deleteTest"
+      parameters:
+      - $ref: "#/parameters/body-deleteoptions"
   /othertest:
     post:
       tags:
@@ -82,6 +89,12 @@ paths:
         required: true
         schema:
           $ref: "#/definitions/Test2"
+    delete:
+      summary: "Test2 API Delete"
+      operationId: "deleteTest2"
+      parameters:
+      - schema:
+          $ref: "#/definitions/DeleteOptions"
 definitions:
   Test:
     type: "object"
@@ -102,9 +115,24 @@ definitions:
         $ref: "#/definitions/Other"
   Other:
     type: "string"
+  DeleteOptions:
+    type: "object"
+    properties:
+      preconditions:
+        $ref: "#/definitions/Preconditions"
+  Preconditions:
+    type: "string"
+parameters:
+  body-deleteoptions:
+    name: body
+    in: body
+    schema:
+      $ref: "#/definitions/DeleteOptions"
 `), &spec1)
 
-	yaml.Unmarshal([]byte(`
+	ast.NoError(err)
+
+	err = yaml.Unmarshal([]byte(`
 swagger: "2.0"
 paths:
   /test:
@@ -124,6 +152,11 @@ paths:
         405:
           description: "Invalid input"
           $ref: "#/definitions/InvalidInput"
+    delete:
+      summary: "Test API Delete"
+      operationId: "deleteTest"
+      parameters:
+      - $ref: "#/parameters/body-deleteoptions"
 definitions:
   Test:
     type: "object"
@@ -137,9 +170,22 @@ definitions:
   InvalidInput:
     type: "string"
     format: "string"
+  DeleteOptions:
+    type: "object"
+    properties:
+      preconditions:
+        $ref: "#/definitions/Preconditions"
+  Preconditions:
+    type: "string"
+parameters:
+  body-deleteoptions:
+    name: body
+    in: body
+    schema:
+      $ref: "#/definitions/DeleteOptions"
 `), &spec1_filtered)
+	ast.NoError(err)
 
-	ast := assert.New(t)
 	orig_spec1, _ := cloneSpec(spec1)
 	new_spec1 := FilterSpecByPathsWithoutSideEffects(spec1, []string{"/test"})
 	ast.Equal(DebugSpec{spec1_filtered}, DebugSpec{new_spec1})
diff --git a/pkg/builder/openapi_test.go b/pkg/builder/openapi_test.go
index 42bdf4b..7ed2200 100644
--- a/pkg/builder/openapi_test.go
+++ b/pkg/builder/openapi_test.go
@@ -59,7 +59,7 @@ type TestOutput struct {
 
 type TestExtensionV2Schema struct{}
 
-func (_ TestExtensionV2Schema) OpenAPIDefinition() *openapi.OpenAPIDefinition {
+func (_ TestExtensionV2Schema) OpenAPIDefinition() openapi.OpenAPIDefinition {
 	schema := spec.Schema{
 		VendorExtensible: spec.VendorExtensible{
 			Extensions: map[string]interface{}{
@@ -81,13 +81,13 @@ func (_ TestExtensionV2Schema) OpenAPIDefinition() *openapi.OpenAPIDefinition {
 			},
 		},
 	}
-	return &openapi.OpenAPIDefinition{
+	return openapi.OpenAPIDefinition{
 		Schema:       schema,
 		Dependencies: []string{},
 	}
 }
 
-func (_ TestInput) OpenAPIDefinition() *openapi.OpenAPIDefinition {
+func (_ TestInput) OpenAPIDefinition() openapi.OpenAPIDefinition {
 	schema := spec.Schema{}
 	schema.Description = "Test input"
 	schema.Properties = map[string]spec.Schema{
@@ -121,13 +121,13 @@ func (_ TestInput) OpenAPIDefinition() *openapi.OpenAPIDefinition {
 		},
 	}
 	schema.Extensions = spec.Extensions{"x-test": "test"}
-	return &openapi.OpenAPIDefinition{
+	return openapi.OpenAPIDefinition{
 		Schema:       schema,
 		Dependencies: []string{},
 	}
 }
 
-func (_ TestOutput) OpenAPIDefinition() *openapi.OpenAPIDefinition {
+func (_ TestOutput) OpenAPIDefinition() openapi.OpenAPIDefinition {
 	schema := spec.Schema{}
 	schema.Description = "Test output"
 	schema.Properties = map[string]spec.Schema{
@@ -146,7 +146,7 @@ func (_ TestOutput) OpenAPIDefinition() *openapi.OpenAPIDefinition {
 			},
 		},
 	}
-	return &openapi.OpenAPIDefinition{
+	return openapi.OpenAPIDefinition{
 		Schema:       schema,
 		Dependencies: []string{},
 	}
@@ -214,9 +214,9 @@ func getConfig(fullMethods bool) (*openapi.Config, *restful.Container) {
 		},
 		GetDefinitions: func(_ openapi.ReferenceCallback) map[string]openapi.OpenAPIDefinition {
 			return map[string]openapi.OpenAPIDefinition{
-				"k8s.io/kube-openapi/pkg/builder.TestInput":             *TestInput{}.OpenAPIDefinition(),
-				"k8s.io/kube-openapi/pkg/builder.TestOutput":            *TestOutput{}.OpenAPIDefinition(),
-				"k8s.io/kube-openapi/pkg/builder.TestExtensionV2Schema": *TestExtensionV2Schema{}.OpenAPIDefinition(),
+				"k8s.io/kube-openapi/pkg/builder.TestInput":             TestInput{}.OpenAPIDefinition(),
+				"k8s.io/kube-openapi/pkg/builder.TestOutput":            TestOutput{}.OpenAPIDefinition(),
+				"k8s.io/kube-openapi/pkg/builder.TestExtensionV2Schema": TestExtensionV2Schema{}.OpenAPIDefinition(),
 			}
 		},
 		GetDefinitionName: func(name string) (string, spec.Extensions) {
diff --git a/pkg/builder3/openapi_test.go b/pkg/builder3/openapi_test.go
index d75c8c0..2565325 100644
--- a/pkg/builder3/openapi_test.go
+++ b/pkg/builder3/openapi_test.go
@@ -58,7 +58,7 @@ type TestOutput struct {
 	Count int `json:"count,omitempty"`
 }
 
-func (_ TestInput) OpenAPIDefinition() *openapi.OpenAPIDefinition {
+func (_ TestInput) OpenAPIDefinition() openapi.OpenAPIDefinition {
 	schema := spec.Schema{}
 	schema.Description = "Test input"
 	schema.Properties = map[string]spec.Schema{
@@ -118,10 +118,10 @@ func (_ TestInput) OpenAPIDefinition() *openapi.OpenAPIDefinition {
 	}, openapi.OpenAPIDefinition{
 		// this empty embedded v2 definition should not appear in the result
 	})
-	return &def
+	return def
 }
 
-func (_ TestOutput) OpenAPIDefinition() *openapi.OpenAPIDefinition {
+func (_ TestOutput) OpenAPIDefinition() openapi.OpenAPIDefinition {
 	schema := spec.Schema{}
 	schema.Description = "Test output"
 	schema.Properties = map[string]spec.Schema{
@@ -140,7 +140,7 @@ func (_ TestOutput) OpenAPIDefinition() *openapi.OpenAPIDefinition {
 			},
 		},
 	}
-	return &openapi.OpenAPIDefinition{
+	return openapi.OpenAPIDefinition{
 		Schema:       schema,
 		Dependencies: []string{},
 	}
@@ -203,8 +203,8 @@ func getConfig(fullMethods bool) (*openapi.OpenAPIV3Config, *restful.Container)
 		},
 		GetDefinitions: func(_ openapi.ReferenceCallback) map[string]openapi.OpenAPIDefinition {
 			return map[string]openapi.OpenAPIDefinition{
-				"k8s.io/kube-openapi/pkg/builder3.TestInput":  *TestInput{}.OpenAPIDefinition(),
-				"k8s.io/kube-openapi/pkg/builder3.TestOutput": *TestOutput{}.OpenAPIDefinition(),
+				"k8s.io/kube-openapi/pkg/builder3.TestInput":  TestInput{}.OpenAPIDefinition(),
+				"k8s.io/kube-openapi/pkg/builder3.TestOutput": TestOutput{}.OpenAPIDefinition(),
 			}
 		},
 		GetDefinitionName: func(name string) (string, spec.Extensions) {
diff --git a/pkg/schemaconv/smd_test.go b/pkg/schemaconv/smd_test.go
index 532eaf5..31746bf 100644
--- a/pkg/schemaconv/smd_test.go
+++ b/pkg/schemaconv/smd_test.go
@@ -22,7 +22,7 @@ import (
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
-	yaml "sigs.k8s.io/yaml/goyaml.v2"
+	yaml "go.yaml.in/yaml/v2"
 
 	"k8s.io/kube-openapi/pkg/util/proto"
 	prototesting "k8s.io/kube-openapi/pkg/util/proto/testing"
diff --git a/pkg/validation/spec/gnostic_test.go b/pkg/validation/spec/gnostic_test.go
index d30b879..19acd95 100644
--- a/pkg/validation/spec/gnostic_test.go
+++ b/pkg/validation/spec/gnostic_test.go
@@ -28,8 +28,8 @@ import (
 	openapi_v2 "github.com/google/gnostic-models/openapiv2"
 	"github.com/google/go-cmp/cmp"
 	"github.com/stretchr/testify/require"
+	"go.yaml.in/yaml/v3"
 	"google.golang.org/protobuf/proto"
-	"gopkg.in/yaml.v3"
 	jsontesting "k8s.io/kube-openapi/pkg/util/jsontesting"
 	. "k8s.io/kube-openapi/pkg/validation/spec"
 	"sigs.k8s.io/randfill"
diff --git a/pkg/validation/strfmt/default_test.go b/pkg/validation/strfmt/default_test.go
index b31bf0d..0b718da 100644
--- a/pkg/validation/strfmt/default_test.go
+++ b/pkg/validation/strfmt/default_test.go
@@ -317,6 +317,10 @@ type testableFormat interface {
 }
 
 func testStringFormat(t *testing.T, what testableFormat, format, with string, validSamples, invalidSamples []string) {
+	testStringFormatWithRegistry(t, Default, what, format, with, validSamples, invalidSamples)
+}
+
+func testStringFormatWithRegistry(t *testing.T, registry Registry, what testableFormat, format, with string, validSamples, invalidSamples []string) {
 	// text encoding interface
 	b := []byte(with)
 	err := what.UnmarshalText(b)
@@ -347,24 +351,39 @@ func testStringFormat(t *testing.T, what testableFormat, format, with string, va
 	assert.Equalf(t, bj, b, "[%s]MarshalJSON: expected %v and %v to be value equal as []byte", format, string(b), with)
 
 	// validation with Registry
-	for _, valid := range append(validSamples, with) {
-		testValid(t, format, valid)
-	}
-
-	for _, invalid := range invalidSamples {
-		testInvalid(t, format, invalid)
-	}
+	t.Run("valid", func(t *testing.T) {
+		for _, valid := range append(validSamples, with) {
+			t.Run(valid, func(t *testing.T) {
+				testValidWithRegistry(t, registry, format, valid)
+			})
+		}
+	})
+	t.Run("invalid", func(t *testing.T) {
+		for _, invalid := range invalidSamples {
+			t.Run(invalid, func(t *testing.T) {
+				testInvalidWithRegistry(t, registry, format, invalid)
+			})
+		}
+	})
 }
 
 func testValid(t *testing.T, name, value string) {
-	ok := Default.Validates(name, value)
+	testValidWithRegistry(t, Default, name, value)
+}
+
+func testValidWithRegistry(t *testing.T, registry Registry, name, value string) {
+	ok := registry.Validates(name, value)
 	if !ok {
 		t.Errorf("expected %q of type %s to be valid", value, name)
 	}
 }
 
 func testInvalid(t *testing.T, name, value string) {
-	ok := Default.Validates(name, value)
+	testInvalidWithRegistry(t, Default, name, value)
+}
+
+func testInvalidWithRegistry(t *testing.T, registry Registry, name, value string) {
+	ok := registry.Validates(name, value)
 	if ok {
 		t.Errorf("expected %q of type %s to be invalid", value, name)
 	}
diff --git a/pkg/validation/strfmt/kubernetes-extensions_test.go b/pkg/validation/strfmt/kubernetes-extensions_test.go
new file mode 100644
index 0000000..d58bc17
--- /dev/null
+++ b/pkg/validation/strfmt/kubernetes-extensions_test.go
@@ -0,0 +1,77 @@
+// Copyright 2024 go-swagger maintainers
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package strfmt
+
+import (
+	"strings"
+	"testing"
+)
+
+var goodShortName = []string{
+	"a", "ab", "abc", "a1", "a-1", "a--1--2--b",
+	"0", "01", "012", "1a", "1-a", "1--a--b--2",
+	strings.Repeat("a", 63),
+}
+var badShortName = []string{
+	"", "A", "ABC", "aBc", "A1", "A-1", "1-A",
+	"-", "-a", "-1",
+	"_", "a_", "_a", "a_b", "1_", "_1", "1_2",
+	".", "a.", ".a", "a.b", "1.", ".1", "1.2",
+	" ", "a ", " a", "a b", "1 ", " 1", "1 2",
+	strings.Repeat("a", 64),
+}
+
+var prefixOnlyShortName = []string{
+	"a-", "1-",
+}
+
+func TestIsShortName(t *testing.T) {
+	v := ShortName("a")
+	testStringFormatWithRegistry(t, Default, &v, "k8s-short-name", "a", goodShortName, append(badShortName, prefixOnlyShortName...))
+}
+
+var goodLongName = []string{
+	"a", "ab", "abc", "a1", "a-1", "a--1--2--b",
+	"0", "01", "012", "1a", "1-a", "1--a--b--2",
+	"a.a", "ab.a", "abc.a", "a1.a", "a-1.a", "a--1--2--b.a",
+	"a.1", "ab.1", "abc.1", "a1.1", "a-1.1", "a--1--2--b.1",
+	"0.a", "01.a", "012.a", "1a.a", "1-a.a", "1--a--b--2",
+	"0.1", "01.1", "012.1", "1a.1", "1-a.1", "1--a--b--2.1",
+	"a.b.c.d.e", "aa.bb.cc.dd.ee", "1.2.3.4.5", "11.22.33.44.55",
+	strings.Repeat("a", 253),
+}
+var badLongName = []string{
+	"", "A", "ABC", "aBc", "A1", "A-1", "1-A",
+	"-", "-a", "-1",
+	"_", "a_", "_a", "a_b", "1_", "_1", "1_2",
+	".", "a.", ".a", "a..b", "1.", ".1", "1..2",
+	" ", "a ", " a", "a b", "1 ", " 1", "1 2",
+	"A.a", "aB.a", "ab.A", "A1.a", "a1.A",
+	"A.1", "aB.1", "A1.1", "1A.1",
+	"0.A", "01.A", "012.A", "1A.a", "1a.A",
+	"A.B.C.D.E", "AA.BB.CC.DD.EE", "a.B.c.d.e", "aa.bB.cc.dd.ee",
+	"a@b", "a,b", "a_b", "a;b",
+	"a:b", "a%b", "a?b", "a$b",
+	strings.Repeat("a", 254),
+}
+
+var prefixOnlyLongName = []string{
+	"a-", "1-",
+}
+
+func TestFormatLongName(t *testing.T) {
+	v := LongName("a")
+	testStringFormatWithRegistry(t, Default, &v, "k8s-long-name", "a", goodLongName, append(badLongName, prefixOnlyLongName...))
+}
diff --git a/test/integration/go.mod b/test/integration/go.mod
index d8c7ae4..77b72c3 100644
--- a/test/integration/go.mod
+++ b/test/integration/go.mod
@@ -1,6 +1,6 @@
 module k8s.io/kube-openapi/test/integration
 
-go 1.21
+go 1.22
 
 replace k8s.io/kube-openapi => ../../
 
@@ -19,7 +19,7 @@ require (
 	github.com/go-openapi/jsonreference v0.20.1 // indirect
 	github.com/go-openapi/swag v0.23.0 // indirect
 	github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
-	github.com/google/gnostic-models v0.6.9 // indirect
+	github.com/google/gnostic-models v0.7.0 // indirect
 	github.com/google/go-cmp v0.6.0 // indirect
 	github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
 	github.com/invopop/yaml v0.1.0 // indirect
@@ -29,6 +29,7 @@ require (
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
+	go.yaml.in/yaml/v3 v3.0.3 // indirect
 	golang.org/x/mod v0.20.0 // indirect
 	golang.org/x/net v0.28.0 // indirect
 	golang.org/x/sync v0.8.0 // indirect
diff --git a/test/integration/go.sum b/test/integration/go.sum
index e15a14c..4d49f81 100644
--- a/test/integration/go.sum
+++ b/test/integration/go.sum
@@ -20,8 +20,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
 github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
 github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
 github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
-github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
-github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
+github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
+github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -71,6 +71,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
+go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
 golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
 golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=