Hide OpeAPI details behind the NewTypeConverter

The fact that we're building the OpenAPI using the proto.Models is an
implementation detail that we shouldn't have to expose. Since we're
going to change the way this is transformed, let's first hide it behind
the common NewTypeConverter so that the next change is transparent.

This will also enable other clean-ups like hiding the gvkParser which
shouldn't be exposed and prevent some refactoring.
This commit is contained in:
Antoine Pelisse 2023-01-03 14:26:06 -08:00
parent 88c952d256
commit a7ab6b86db
7 changed files with 65 additions and 79 deletions

View File

@ -71,13 +71,11 @@ import (
apirequest "k8s.io/apiserver/pkg/endpoints/request" apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
genericfilters "k8s.io/apiserver/pkg/server/filters" genericfilters "k8s.io/apiserver/pkg/server/filters"
utilopenapi "k8s.io/apiserver/pkg/util/openapi"
"k8s.io/apiserver/pkg/warning" "k8s.io/apiserver/pkg/warning"
"k8s.io/client-go/scale" "k8s.io/client-go/scale"
"k8s.io/client-go/scale/scheme/autoscalingv1" "k8s.io/client-go/scale/scheme/autoscalingv1"
"k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/cache"
"k8s.io/klog/v2" "k8s.io/klog/v2"
"k8s.io/kube-openapi/pkg/util/proto"
"k8s.io/kube-openapi/pkg/validation/spec" "k8s.io/kube-openapi/pkg/validation/spec"
"k8s.io/kube-openapi/pkg/validation/strfmt" "k8s.io/kube-openapi/pkg/validation/strfmt"
"k8s.io/kube-openapi/pkg/validation/validate" "k8s.io/kube-openapi/pkg/validation/validate"
@ -1371,7 +1369,7 @@ func hasServedCRDVersion(spec *apiextensionsv1.CustomResourceDefinitionSpec, ver
// buildOpenAPIModelsForApply constructs openapi models from any validation schemas specified in the custom resource, // buildOpenAPIModelsForApply constructs openapi models from any validation schemas specified in the custom resource,
// and merges it with the models defined in the static OpenAPI spec. // and merges it with the models defined in the static OpenAPI spec.
// Returns nil models ifthe static spec is nil, or an error is encountered. // Returns nil models ifthe static spec is nil, or an error is encountered.
func buildOpenAPIModelsForApply(staticOpenAPISpec *spec.Swagger, crd *apiextensionsv1.CustomResourceDefinition) (proto.Models, error) { func buildOpenAPIModelsForApply(staticOpenAPISpec *spec.Swagger, crd *apiextensionsv1.CustomResourceDefinition) (*spec.Swagger, error) {
if staticOpenAPISpec == nil { if staticOpenAPISpec == nil {
return nil, nil return nil, nil
} }
@ -1391,9 +1389,5 @@ func buildOpenAPIModelsForApply(staticOpenAPISpec *spec.Swagger, crd *apiextensi
if err != nil { if err != nil {
return nil, err return nil, err
} }
models, err := utilopenapi.ToProtoModels(mergedOpenAPI) return mergedOpenAPI, nil
if err != nil {
return nil, err
}
return models, nil
} }

View File

@ -34,7 +34,7 @@ import (
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager" "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storageversion" "k8s.io/apiserver/pkg/storageversion"
openapiproto "k8s.io/kube-openapi/pkg/util/proto" "k8s.io/kube-openapi/pkg/validation/spec"
) )
// ConvertabilityChecker indicates what versions a GroupKind is available in. // ConvertabilityChecker indicates what versions a GroupKind is available in.
@ -96,7 +96,7 @@ type APIGroupVersion struct {
MinRequestTimeout time.Duration MinRequestTimeout time.Duration
// OpenAPIModels exposes the OpenAPI models to each individual handler. // OpenAPIModels exposes the OpenAPI models to each individual handler.
OpenAPIModels openapiproto.Models OpenAPIModels *spec.Swagger
// The limit on the request body size that would be accepted and decoded in a write request. // The limit on the request body size that would be accepted and decoded in a write request.
// 0 means no limit. // 0 means no limit.

View File

@ -17,8 +17,10 @@ limitations under the License.
package fieldmanagertest package fieldmanagertest
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"path/filepath" "path/filepath"
"strings" "strings"
@ -28,39 +30,34 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager" "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
"k8s.io/kube-openapi/pkg/util/proto" "k8s.io/kube-openapi/pkg/validation/spec"
prototesting "k8s.io/kube-openapi/pkg/util/proto/testing"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath" "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
"sigs.k8s.io/structured-merge-diff/v4/merge" "sigs.k8s.io/structured-merge-diff/v4/merge"
"sigs.k8s.io/structured-merge-diff/v4/typed" "sigs.k8s.io/structured-merge-diff/v4/typed"
) )
var kubernetesSwaggerSchema = prototesting.Fake{ var builtinConverter = func() fieldmanager.TypeConverter {
Path: filepath.Join( data, err := ioutil.ReadFile(filepath.Join(
strings.Repeat(".."+string(filepath.Separator), 8), strings.Repeat(".."+string(filepath.Separator), 8),
"api", "openapi-spec", "swagger.json"), "api", "openapi-spec", "swagger.json"))
} if err != nil {
panic(err)
// NewBuiltinTypeConverter creates a TypeConverter with all the built-in }
// types defined, given the committed kubernetes swagger.json. spec := spec.Swagger{}
func NewBuiltinTypeConverter() fieldmanager.TypeConverter { if err := json.Unmarshal(data, &spec); err != nil {
tc, err := fieldmanager.NewTypeConverter(newFakeOpenAPIModels(), false) panic(err)
}
tc, err := fieldmanager.NewTypeConverter(&spec, false)
if err != nil { if err != nil {
panic(fmt.Errorf("Failed to build TypeConverter: %v", err)) panic(fmt.Errorf("Failed to build TypeConverter: %v", err))
} }
return tc return tc
} }()
func newFakeOpenAPIModels() proto.Models { // NewBuiltinTypeConverter creates a TypeConverter with all the built-in
d, err := kubernetesSwaggerSchema.OpenAPISchema() // types defined, given the committed kubernetes swagger.json.
if err != nil { func NewBuiltinTypeConverter() fieldmanager.TypeConverter {
panic(err) return builtinConverter
}
m, err := proto.NewOpenAPIData(d)
if err != nil {
panic(err)
}
return m
} }
type fakeObjectConvertor struct { type fakeObjectConvertor struct {

View File

@ -23,7 +23,8 @@ import (
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/managedfields" "k8s.io/apimachinery/pkg/util/managedfields"
"k8s.io/kube-openapi/pkg/util/proto" utilopenapi "k8s.io/apiserver/pkg/util/openapi"
"k8s.io/kube-openapi/pkg/validation/spec"
"sigs.k8s.io/structured-merge-diff/v4/typed" "sigs.k8s.io/structured-merge-diff/v4/typed"
"sigs.k8s.io/structured-merge-diff/v4/value" "sigs.k8s.io/structured-merge-diff/v4/value"
) )
@ -70,10 +71,14 @@ type typeConverter struct {
var _ TypeConverter = &typeConverter{} var _ TypeConverter = &typeConverter{}
// NewTypeConverter builds a TypeConverter from a proto.Models. This // NewTypeConverter builds a TypeConverter from a spec.Swagger. This
// will automatically find the proper version of the object, and the // will automatically find the proper version of the object, and the
// corresponding schema information. // corresponding schema information.
func NewTypeConverter(models proto.Models, preserveUnknownFields bool) (TypeConverter, error) { func NewTypeConverter(openapiSpec *spec.Swagger, preserveUnknownFields bool) (TypeConverter, error) {
models, err := utilopenapi.ToProtoModels(openapiSpec)
if err != nil {
return nil, err
}
parser, err := managedfields.NewGVKParser(models, preserveUnknownFields) parser, err := managedfields.NewGVKParser(models, preserveUnknownFields)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -17,7 +17,9 @@ limitations under the License.
package fieldmanager_test package fieldmanager_test
import ( import (
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"path/filepath" "path/filepath"
"reflect" "reflect"
"testing" "testing"
@ -27,25 +29,23 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager" "k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager"
"k8s.io/kube-openapi/pkg/util/proto" "k8s.io/kube-openapi/pkg/validation/spec"
prototesting "k8s.io/kube-openapi/pkg/util/proto/testing"
) )
var testSchema = prototesting.Fake{ var testSchema = func() *spec.Swagger {
Path: filepath.Join("testdata", "swagger.json"), data, err := ioutil.ReadFile(filepath.Join("testdata", "swagger.json"))
} if err != nil {
panic(err)
}
spec := spec.Swagger{}
if err := json.Unmarshal(data, &spec); err != nil {
panic(err)
}
return &spec
}()
func TestTypeConverter(t *testing.T) { func TestTypeConverter(t *testing.T) {
d, err := testSchema.OpenAPISchema() tc, err := fieldmanager.NewTypeConverter(testSchema, false)
if err != nil {
t.Fatalf("Failed to parse OpenAPI schema: %v", err)
}
m, err := proto.NewOpenAPIData(d)
if err != nil {
t.Fatalf("Failed to build OpenAPI models: %v", err)
}
tc, err := fieldmanager.NewTypeConverter(m, false)
if err != nil { if err != nil {
t.Fatalf("Failed to build TypeConverter: %v", err) t.Fatalf("Failed to build TypeConverter: %v", err)
} }
@ -177,16 +177,7 @@ spec:
b.Fatalf("Failed to parse yaml object: %v", err) b.Fatalf("Failed to parse yaml object: %v", err)
} }
d, err := testSchema.OpenAPISchema() tc, err := fieldmanager.NewTypeConverter(testSchema, false)
if err != nil {
b.Fatalf("Failed to parse OpenAPI schema: %v", err)
}
m, err := proto.NewOpenAPIData(d)
if err != nil {
b.Fatalf("Failed to build OpenAPI models: %v", err)
}
tc, err := fieldmanager.NewTypeConverter(m, false)
if err != nil { if err != nil {
b.Fatalf("Failed to build TypeConverter: %v", err) b.Fatalf("Failed to build TypeConverter: %v", err)
} }

View File

@ -17,7 +17,9 @@ limitations under the License.
package fieldmanager package fieldmanager
import ( import (
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"path/filepath" "path/filepath"
"reflect" "reflect"
"testing" "testing"
@ -25,26 +27,25 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kube-openapi/pkg/util/proto" "k8s.io/kube-openapi/pkg/validation/spec"
prototesting "k8s.io/kube-openapi/pkg/util/proto/testing"
"sigs.k8s.io/structured-merge-diff/v4/fieldpath" "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
) )
var testSchema = prototesting.Fake{ var testSchema = func() *spec.Swagger {
Path: filepath.Join("testdata", "swagger.json"), data, err := ioutil.ReadFile(filepath.Join("testdata", "swagger.json"))
} if err != nil {
panic(err)
}
spec := spec.Swagger{}
if err := json.Unmarshal(data, &spec); err != nil {
panic(err)
}
return &spec
}()
// TestVersionConverter tests the version converter // TestVersionConverter tests the version converter
func TestVersionConverter(t *testing.T) { func TestVersionConverter(t *testing.T) {
d, err := testSchema.OpenAPISchema() tc, err := NewTypeConverter(testSchema, false)
if err != nil {
t.Fatalf("Failed to parse OpenAPI schema: %v", err)
}
m, err := proto.NewOpenAPIData(d)
if err != nil {
t.Fatalf("Failed to build OpenAPI models: %v", err)
}
tc, err := NewTypeConverter(m, false)
if err != nil { if err != nil {
t.Fatalf("Failed to build TypeConverter: %v", err) t.Fatalf("Failed to build TypeConverter: %v", err)
} }

View File

@ -49,7 +49,6 @@ import (
"k8s.io/apiserver/pkg/server/routes" "k8s.io/apiserver/pkg/server/routes"
"k8s.io/apiserver/pkg/storageversion" "k8s.io/apiserver/pkg/storageversion"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
utilopenapi "k8s.io/apiserver/pkg/util/openapi"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
"k8s.io/klog/v2" "k8s.io/klog/v2"
openapibuilder2 "k8s.io/kube-openapi/pkg/builder" openapibuilder2 "k8s.io/kube-openapi/pkg/builder"
@ -57,7 +56,6 @@ import (
"k8s.io/kube-openapi/pkg/handler" "k8s.io/kube-openapi/pkg/handler"
"k8s.io/kube-openapi/pkg/handler3" "k8s.io/kube-openapi/pkg/handler3"
openapiutil "k8s.io/kube-openapi/pkg/util" openapiutil "k8s.io/kube-openapi/pkg/util"
openapiproto "k8s.io/kube-openapi/pkg/util/proto"
"k8s.io/kube-openapi/pkg/validation/spec" "k8s.io/kube-openapi/pkg/validation/spec"
"k8s.io/utils/clock" "k8s.io/utils/clock"
) )
@ -666,7 +664,7 @@ func (s preparedGenericAPIServer) NonBlockingRun(stopCh <-chan struct{}, shutdow
} }
// installAPIResources is a private method for installing the REST storage backing each api groupversionresource // installAPIResources is a private method for installing the REST storage backing each api groupversionresource
func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo, openAPIModels openapiproto.Models) error { func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo, openAPIModels *spec.Swagger) error {
var resourceInfos []*storageversion.ResourceInfo var resourceInfos []*storageversion.ResourceInfo
for _, groupVersion := range apiGroupInfo.PrioritizedVersions { for _, groupVersion := range apiGroupInfo.PrioritizedVersions {
if len(apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version]) == 0 { if len(apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version]) == 0 {
@ -881,7 +879,7 @@ func NewDefaultAPIGroupInfo(group string, scheme *runtime.Scheme, parameterCodec
} }
// getOpenAPIModels is a private method for getting the OpenAPI models // getOpenAPIModels is a private method for getting the OpenAPI models
func (s *GenericAPIServer) getOpenAPIModels(apiPrefix string, apiGroupInfos ...*APIGroupInfo) (openapiproto.Models, error) { func (s *GenericAPIServer) getOpenAPIModels(apiPrefix string, apiGroupInfos ...*APIGroupInfo) (*spec.Swagger, error) {
if s.openAPIConfig == nil { if s.openAPIConfig == nil {
return nil, nil return nil, nil
} }
@ -903,7 +901,7 @@ func (s *GenericAPIServer) getOpenAPIModels(apiPrefix string, apiGroupInfos ...*
for _, apiGroupInfo := range apiGroupInfos { for _, apiGroupInfo := range apiGroupInfos {
apiGroupInfo.StaticOpenAPISpec = openAPISpec apiGroupInfo.StaticOpenAPISpec = openAPISpec
} }
return utilopenapi.ToProtoModels(openAPISpec) return openAPISpec, nil
} }
// getResourceNamesForGroup is a private method for getting the canonical names for each resource to build in an api group // getResourceNamesForGroup is a private method for getting the canonical names for each resource to build in an api group