diff --git a/hack/import-restrictions.yaml b/hack/import-restrictions.yaml index f0e098a4573..3ae27381c33 100644 --- a/hack/import-restrictions.yaml +++ b/hack/import-restrictions.yaml @@ -123,7 +123,6 @@ - k8s.io/apiserver - k8s.io/client-go - k8s.io/klog - - k8s.io/kube-openapi - baseImportPath: "./vendor/k8s.io/kube-openapi/" allowedImports: diff --git a/staging/src/k8s.io/apiextensions-apiserver/BUILD b/staging/src/k8s.io/apiextensions-apiserver/BUILD index 476d9658fae..e555fb761c0 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/BUILD @@ -50,7 +50,6 @@ filegroup( "//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/crdserverscheme:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/features:all-srcs", - "//staging/src/k8s.io/apiextensions-apiserver/pkg/openapi:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/test/integration:all-srcs", diff --git a/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json b/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json index 06f0b31eb0d..df164ef7363 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json @@ -2262,10 +2262,6 @@ "ImportPath": "k8s.io/klog", "Rev": "8139d8cb77af419532b33dfa7dd09fbc5f1d344f" }, - { - "ImportPath": "k8s.io/kube-openapi/pkg/aggregator", - "Rev": "c59034cc13d587f5ef4e85ca0ade0c1866ae8e1d" - }, { "ImportPath": "k8s.io/kube-openapi/pkg/builder", "Rev": "c59034cc13d587f5ef4e85ca0ade0c1866ae8e1d" diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD index d2ac06cd03e..5da6c14cda3 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD @@ -36,7 +36,6 @@ go_library( "//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/crdserverscheme:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library", - "//staging/src/k8s.io/apiextensions-apiserver/pkg/openapi:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition:go_default_library", diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go index 30ec82074ee..b6830d762c8 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go @@ -41,7 +41,6 @@ import ( "k8s.io/apiextensions-apiserver/pkg/controller/establish" "k8s.io/apiextensions-apiserver/pkg/controller/finalizer" "k8s.io/apiextensions-apiserver/pkg/controller/status" - openapiaggregator "k8s.io/apiextensions-apiserver/pkg/openapi" "k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition" _ "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" @@ -205,13 +204,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) return nil }) s.GenericAPIServer.AddPostStartHook("start-apiextensions-controllers", func(context genericapiserver.PostStartHookContext) error { - // create OpenAPI aggregation manager in the last step because only now genericapiserver's the OpenAPI services and spec is available (after PrepareRun). - crdOpenAPIAggregationManager, err := openapiaggregator.NewAggregationManager(s.GenericAPIServer.OpenAPIService, s.GenericAPIServer.OpenAPIVersionedService, s.GenericAPIServer.StaticOpenAPISpec) - if err != nil { - return err - } - - go crdController.Run(context.StopCh, crdOpenAPIAggregationManager) + go crdController.Run(context.StopCh) go namingController.Run(context.StopCh) go establishingController.Run(context.StopCh) go finalizingController.Run(5, context.StopCh) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go index da54fca6af5..3b13e8357db 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go @@ -31,16 +31,12 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/version" "k8s.io/apiserver/pkg/endpoints/discovery" - utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" informers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion" listers "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion" - apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features" - apiextensionsopenapi "k8s.io/apiextensions-apiserver/pkg/openapi" ) type DiscoveryController struct { @@ -54,8 +50,6 @@ type DiscoveryController struct { syncFn func(version schema.GroupVersion) error queue workqueue.RateLimitingInterface - - openAPIAggregationManager apiextensionsopenapi.AggregationManager } func NewDiscoveryController(crdInformer informers.CustomResourceDefinitionInformer, versionHandler *versionDiscoveryHandler, groupHandler *groupDiscoveryHandler) *DiscoveryController { @@ -89,7 +83,6 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { if err != nil { return err } - apiServiceName := version.Group + "." + version.Version foundVersion := false foundGroup := false for _, crd := range crds { @@ -126,33 +119,6 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { continue } foundVersion = true - if c.openAPIAggregationManager != nil && utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) { - validationSchema, err := getSchemaForVersion(crd, version.Version) - if err != nil { - return err - } - // Convert internal CustomResourceValidation to versioned CustomResourceValidation - versionedSchema := new(v1beta1.CustomResourceValidation) - if validationSchema == nil { - versionedSchema = nil - } else { - if err := v1beta1.Convert_apiextensions_CustomResourceValidation_To_v1beta1_CustomResourceValidation(validationSchema, versionedSchema, nil); err != nil { - return err - } - } - // We aggregate the schema even if it's nil as it maybe a removal of the schema for this CRD, - // and the aggreated OpenAPI spec should reflect this change. - crdspec, etag, err := apiextensionsopenapi.CustomResourceDefinitionOpenAPISpec(&crd.Spec, version.Version, versionedSchema) - if err != nil { - return err - } - - // Add/update the local API service's spec for the CRD in apiExtensionsServer's - // openAPIAggregationManager - if err := c.openAPIAggregationManager.AddUpdateLocalAPIServiceSpec(apiServiceName, crdspec, etag); err != nil { - return err - } - } verbs := metav1.Verbs([]string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"}) // if we're terminating we don't allow some verbs @@ -198,14 +164,6 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { if !foundGroup { c.groupHandler.unsetDiscovery(version.Group) c.versionHandler.unsetDiscovery(version) - if c.openAPIAggregationManager != nil && utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) { - // Remove the local API service for the CRD in apiExtensionsServer's - // openAPIAggregationManager. - // Note that we don't check if apiServiceName exists in openAPIAggregationManager - // because RemoveAPIServiceSpec properly handles non-existing API service by - // returning no error. - return c.openAPIAggregationManager.RemoveAPIServiceSpec(apiServiceName) - } return nil } @@ -222,14 +180,6 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { if !foundVersion { c.versionHandler.unsetDiscovery(version) - if c.openAPIAggregationManager != nil && utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) { - // Remove the local API service for the CRD in apiExtensionsServer's - // openAPIAggregationManager. - // Note that we don't check if apiServiceName exists in openAPIAggregationManager - // because RemoveAPIServiceSpec properly handles non-existing API service by - // returning no error. - return c.openAPIAggregationManager.RemoveAPIServiceSpec(apiServiceName) - } return nil } c.versionHandler.setDiscovery(version, discovery.NewAPIVersionHandler(Codecs, version, discovery.APIResourceListerFunc(func() []metav1.APIResource { @@ -245,15 +195,13 @@ func sortGroupDiscoveryByKubeAwareVersion(gd []metav1.GroupVersionForDiscovery) }) } -func (c *DiscoveryController) Run(stopCh <-chan struct{}, crdOpenAPIAggregationManager apiextensionsopenapi.AggregationManager) { +func (c *DiscoveryController) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() defer c.queue.ShutDown() defer klog.Infof("Shutting down DiscoveryController") klog.Infof("Starting DiscoveryController") - c.openAPIAggregationManager = crdOpenAPIAggregationManager - if !cache.WaitForCacheSync(stopCh, c.crdsSynced) { utilruntime.HandleError(fmt.Errorf("timed out waiting for caches to sync")) return diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD deleted file mode 100644 index d1d11eabec9..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD +++ /dev/null @@ -1,48 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = [ - "aggregator.go", - "construction.go", - "conversion.go", - "swagger_util.go", - ], - importmap = "k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/openapi", - importpath = "k8s.io/apiextensions-apiserver/pkg/openapi", - visibility = ["//visibility:public"], - deps = [ - "//staging/src/k8s.io/api/autoscaling/v1:go_default_library", - "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library", - "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", - "//vendor/github.com/go-openapi/spec:go_default_library", - "//vendor/k8s.io/kube-openapi/pkg/aggregator:go_default_library", - "//vendor/k8s.io/kube-openapi/pkg/handler:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["conversion_test.go"], - embed = [":go_default_library"], - deps = [ - "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library", - "//vendor/github.com/go-openapi/spec:go_default_library", - ], -) - -filegroup( - name = "package-srcs", - srcs = glob(["**"]), - tags = ["automanaged"], - visibility = ["//visibility:private"], -) - -filegroup( - name = "all-srcs", - srcs = [":package-srcs"], - tags = ["automanaged"], - visibility = ["//visibility:public"], -) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/aggregator.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/aggregator.go deleted file mode 100644 index 90bd042f097..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/aggregator.go +++ /dev/null @@ -1,232 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -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 openapi - -import ( - "fmt" - "sync" - - "github.com/go-openapi/spec" - - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/kube-openapi/pkg/aggregator" - "k8s.io/kube-openapi/pkg/handler" -) - -// AggregationManager is the interface between OpenAPI Aggregator service and a controller -// that manages CRD openapi spec aggregation -type AggregationManager interface { - // AddUpdateLocalAPIService allows adding/updating local API service with nil handler and - // nil Spec.Service. This function can be used for local dynamic OpenAPI spec aggregation - // management (e.g. CRD) - AddUpdateLocalAPIServiceSpec(name string, spec *spec.Swagger, etag string) error - RemoveAPIServiceSpec(apiServiceName string) error -} - -type specAggregator struct { - // mutex protects all members of this struct. - rwMutex sync.RWMutex - - // Map of API Services' OpenAPI specs by their name - openAPISpecs map[string]*openAPISpecInfo - - // provided for dynamic OpenAPI spec - openAPIService *handler.OpenAPIService - openAPIVersionedService *handler.OpenAPIService -} - -var _ AggregationManager = &specAggregator{} - -// NewAggregationManager constructs a specAggregator from input openAPIService, openAPIVersionedService and -// recorded static OpenAPI spec. The function returns an AggregationManager interface. -func NewAggregationManager(openAPIService, openAPIVersionedService *handler.OpenAPIService, staticSpec *spec.Swagger) (AggregationManager, error) { - // openAPIVersionedService and deprecated openAPIService should be initialized together - if (openAPIService == nil) != (openAPIVersionedService == nil) { - return nil, fmt.Errorf("unexpected openapi service initialization error") - } - return &specAggregator{ - openAPISpecs: map[string]*openAPISpecInfo{ - "initial_static_spec": { - spec: staticSpec, - }, - }, - openAPIService: openAPIService, - openAPIVersionedService: openAPIVersionedService, - }, nil -} - -// openAPISpecInfo is used to store OpenAPI spec with its priority. -// It can be used to sort specs with their priorities. -type openAPISpecInfo struct { - // Name of a registered ApiService - name string - - // Specification of this API Service. If null then the spec is not loaded yet. - spec *spec.Swagger - etag string -} - -// buildOpenAPISpec aggregates all OpenAPI specs. It is not thread-safe. The caller is responsible to hold proper locks. -func (s *specAggregator) buildOpenAPISpec() (specToReturn *spec.Swagger, err error) { - specs := []openAPISpecInfo{} - for _, specInfo := range s.openAPISpecs { - if specInfo.spec == nil { - continue - } - specs = append(specs, *specInfo) - } - if len(specs) == 0 { - return &spec.Swagger{}, nil - } - for _, specInfo := range specs { - if specToReturn == nil { - specToReturn, err = aggregator.CloneSpec(specInfo.spec) - if err != nil { - return nil, err - } - continue - } - mergeSpecs(specToReturn, specInfo.spec) - } - // Add minimum required keys if missing, to properly serve the OpenAPI spec - // through apiextensions-apiserver HTTP handler. These keys will not be - // aggregated to top-level OpenAPI spec (only paths and definitions will). - // However these keys make the OpenAPI->proto serialization happy. - if specToReturn.Info == nil { - specToReturn.Info = &spec.Info{ - InfoProps: spec.InfoProps{ - Title: "Kubernetes", - }, - } - } - if len(specToReturn.Swagger) == 0 { - specToReturn.Swagger = "2.0" - } - return specToReturn, nil -} - -// updateOpenAPISpec aggregates all OpenAPI specs. It is not thread-safe. The caller is responsible to hold proper locks. -func (s *specAggregator) updateOpenAPISpec() error { - if s.openAPIService == nil || s.openAPIVersionedService == nil { - // openAPIVersionedService and deprecated openAPIService should be initialized together - if !(s.openAPIService == nil && s.openAPIVersionedService == nil) { - return fmt.Errorf("unexpected openapi service initialization error") - } - return nil - } - specToServe, err := s.buildOpenAPISpec() - if err != nil { - return err - } - // openAPIService.UpdateSpec and openAPIVersionedService.UpdateSpec read the same swagger spec - // serially and update their local caches separately. Both endpoints will have same spec in - // their caches if the caller is holding proper locks. - err = s.openAPIService.UpdateSpec(specToServe) - if err != nil { - return err - } - return s.openAPIVersionedService.UpdateSpec(specToServe) -} - -// tryUpdatingServiceSpecs tries updating openAPISpecs map with specified specInfo, and keeps the map intact -// if the update fails. -func (s *specAggregator) tryUpdatingServiceSpecs(specInfo *openAPISpecInfo) error { - orgSpecInfo, exists := s.openAPISpecs[specInfo.name] - s.openAPISpecs[specInfo.name] = specInfo - if err := s.updateOpenAPISpec(); err != nil { - if exists { - s.openAPISpecs[specInfo.name] = orgSpecInfo - } else { - delete(s.openAPISpecs, specInfo.name) - } - return err - } - return nil -} - -// tryDeleteServiceSpecs tries delete specified specInfo from openAPISpecs map, and keeps the map intact -// if the update fails. -func (s *specAggregator) tryDeleteServiceSpecs(apiServiceName string) error { - orgSpecInfo, exists := s.openAPISpecs[apiServiceName] - if !exists { - return nil - } - delete(s.openAPISpecs, apiServiceName) - if err := s.updateOpenAPISpec(); err != nil { - s.openAPISpecs[apiServiceName] = orgSpecInfo - return err - } - return nil -} - -// AddUpdateLocalAPIService allows adding/updating local API service with nil handler and -// nil Spec.Service. This function can be used for local dynamic OpenAPI spec aggregation -// management (e.g. CRD) -func (s *specAggregator) AddUpdateLocalAPIServiceSpec(name string, spec *spec.Swagger, etag string) error { - s.rwMutex.Lock() - defer s.rwMutex.Unlock() - - return s.tryUpdatingServiceSpecs(&openAPISpecInfo{ - name: name, - spec: spec, - etag: etag, - }) -} - -// RemoveAPIServiceSpec removes an api service from OpenAPI aggregation. If it does not exist, no error is returned. -// It is thread safe. -func (s *specAggregator) RemoveAPIServiceSpec(apiServiceName string) error { - s.rwMutex.Lock() - defer s.rwMutex.Unlock() - - if _, existingService := s.openAPISpecs[apiServiceName]; !existingService { - return nil - } - - return s.tryDeleteServiceSpecs(apiServiceName) -} - -// mergeSpecs simply adds source openapi spec to dest and ignores any path/definition -// conflicts because CRD openapi spec should not have conflict -func mergeSpecs(dest, source *spec.Swagger) { - // Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). - if source.Paths == nil { - // If Path is nil, none of the model defined in Definitions is used and we - // should not do anything. - // NOTE: this should not happen for CRD specs, because we automatically construct - // the Paths for CRD specs. We use utilruntime.HandleError to log this impossible - // case - utilruntime.HandleError(fmt.Errorf("unexpected CRD spec with empty Path: %v", *source)) - return - } - if dest.Paths == nil { - dest.Paths = &spec.Paths{} - } - for k, v := range source.Definitions { - if dest.Definitions == nil { - dest.Definitions = spec.Definitions{} - } - dest.Definitions[k] = v - } - for k, v := range source.Paths.Paths { - // PathItem may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). - if dest.Paths.Paths == nil { - dest.Paths.Paths = map[string]spec.PathItem{} - } - dest.Paths.Paths[k] = v - } -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/construction.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/construction.go deleted file mode 100644 index 804681da1f3..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/construction.go +++ /dev/null @@ -1,371 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -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 openapi - -import ( - "fmt" - "strings" - - "github.com/go-openapi/spec" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" -) - -// ResourceKind determines the scope of an API object: if it's the parent resource, -// scale subresource or status subresource. -type ResourceKind string - -const ( - // Resource specifies an object of custom resource kind - Resource ResourceKind = "Resource" - // Scale specifies an object of custom resource's scale subresource kind - Scale ResourceKind = "Scale" - // Status specifies an object of custom resource's status subresource kind - Status ResourceKind = "Status" - - scaleSchemaRef = "#/definitions/io.k8s.api.autoscaling.v1.Scale" - statusSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - listMetaSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - patchSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" -) - -// SwaggerConstructor takes in CRD OpenAPI schema and CustomResourceDefinitionSpec, and -// constructs the OpenAPI swagger that an apiserver serves. -type SwaggerConstructor struct { - // schema is the CRD's OpenAPI v2 schema - schema *spec.Schema - - status, scale bool - - group string - version string - kind string - listKind string - plural string - scope apiextensions.ResourceScope -} - -// NewSwaggerConstructor creates a new SwaggerConstructor using the CRD OpenAPI schema -// and CustomResourceDefinitionSpec -func NewSwaggerConstructor(schema *spec.Schema, crdSpec *apiextensions.CustomResourceDefinitionSpec, version string) (*SwaggerConstructor, error) { - ret := &SwaggerConstructor{ - schema: schema, - group: crdSpec.Group, - version: version, - kind: crdSpec.Names.Kind, - listKind: crdSpec.Names.ListKind, - plural: crdSpec.Names.Plural, - scope: crdSpec.Scope, - } - - sub, err := getSubresourcesForVersion(crdSpec, version) - if err != nil { - return nil, err - } - if sub != nil { - ret.status = sub.Status != nil - ret.scale = sub.Scale != nil - } - - return ret, nil -} - -// ConstructCRDOpenAPISpec constructs the complete OpenAPI swagger (spec). -func (c *SwaggerConstructor) ConstructCRDOpenAPISpec() *spec.Swagger { - basePath := fmt.Sprintf("/apis/%s/%s/%s", c.group, c.version, c.plural) - if c.scope == apiextensions.NamespaceScoped { - basePath = fmt.Sprintf("/apis/%s/%s/namespaces/{namespace}/%s", c.group, c.version, c.plural) - } - - model := fmt.Sprintf("%s.%s.%s", c.group, c.version, c.kind) - listModel := fmt.Sprintf("%s.%s.%s", c.group, c.version, c.listKind) - - var schema spec.Schema - if c.schema != nil { - schema = *c.schema - } - - ret := &spec.Swagger{ - SwaggerProps: spec.SwaggerProps{ - Paths: &spec.Paths{ - Paths: map[string]spec.PathItem{ - basePath: { - PathItemProps: spec.PathItemProps{ - Get: c.listOperation(), - Post: c.createOperation(), - Delete: c.deleteCollectionOperation(), - Parameters: pathParameters(), - }, - }, - fmt.Sprintf("%s/{name}", basePath): { - PathItemProps: spec.PathItemProps{ - Get: c.readOperation(Resource), - Put: c.replaceOperation(Resource), - Delete: c.deleteOperation(), - Patch: c.patchOperation(Resource), - Parameters: pathParameters(), - }, - }, - }, - }, - Definitions: spec.Definitions{ - model: schema, - listModel: *c.listSchema(), - }, - }, - } - - if c.status { - ret.SwaggerProps.Paths.Paths[fmt.Sprintf("%s/{name}/status", basePath)] = spec.PathItem{ - PathItemProps: spec.PathItemProps{ - Get: c.readOperation(Status), - Put: c.replaceOperation(Status), - Patch: c.patchOperation(Status), - Parameters: pathParameters(), - }, - } - } - - if c.scale { - ret.SwaggerProps.Paths.Paths[fmt.Sprintf("%s/{name}/scale", basePath)] = spec.PathItem{ - PathItemProps: spec.PathItemProps{ - Get: c.readOperation(Scale), - Put: c.replaceOperation(Scale), - Patch: c.patchOperation(Scale), - Parameters: pathParameters(), - }, - } - // TODO(roycaihw): this is a hack to let apiExtension apiserver and generic kube-apiserver - // to have the same io.k8s.api.autoscaling.v1.Scale definition, so that aggregator server won't - // detect name conflict and create a duplicate io.k8s.api.autoscaling.v1.Scale_V2 schema - // when aggregating the openapi spec. It would be better if apiExtension apiserver serves - // identical definition through the same code path (using routes) as generic kube-apiserver. - ret.SwaggerProps.Definitions["io.k8s.api.autoscaling.v1.Scale"] = *scaleSchema() - ret.SwaggerProps.Definitions["io.k8s.api.autoscaling.v1.ScaleSpec"] = *scaleSpecSchema() - ret.SwaggerProps.Definitions["io.k8s.api.autoscaling.v1.ScaleStatus"] = *scaleStatusSchema() - } - - return ret -} - -// baseOperation initializes a base operation that all operations build upon -func (c *SwaggerConstructor) baseOperation(kind ResourceKind, action string) *spec.Operation { - op := spec.NewOperation(c.operationID(kind, action)). - WithConsumes( - "application/json", - "application/yaml", - ). - WithProduces( - "application/json", - "application/yaml", - ). - WithTags(fmt.Sprintf("%s_%s", c.group, c.version)). - RespondsWith(401, unauthorizedResponse()) - op.Schemes = []string{"https"} - op.AddExtension("x-kubernetes-action", action) - - // Add x-kubernetes-group-version-kind extension - // For CRD scale subresource, the x-kubernetes-group-version-kind is autoscaling.v1.Scale - switch kind { - case Scale: - op.AddExtension("x-kubernetes-group-version-kind", []map[string]string{ - { - "group": "autoscaling", - "kind": "Scale", - "version": "v1", - }, - }) - default: - op.AddExtension("x-kubernetes-group-version-kind", []map[string]string{ - { - "group": c.group, - "kind": c.kind, - "version": c.version, - }, - }) - } - return op -} - -// listOperation constructs a list operation for a CRD -func (c *SwaggerConstructor) listOperation() *spec.Operation { - op := c.baseOperation(Resource, "list"). - WithDescription(fmt.Sprintf("list or watch objects of kind %s", c.kind)). - RespondsWith(200, okResponse(fmt.Sprintf("#/definitions/%s.%s.%s", c.group, c.version, c.listKind))) - return addCollectionOperationParameters(op) -} - -// createOperation constructs a create operation for a CRD -func (c *SwaggerConstructor) createOperation() *spec.Operation { - ref := c.constructSchemaRef(Resource) - return c.baseOperation(Resource, "create"). - WithDescription(fmt.Sprintf("create a %s", c.kind)). - RespondsWith(200, okResponse(ref)). - RespondsWith(201, createdResponse(ref)). - RespondsWith(202, acceptedResponse(ref)). - AddParam((&spec.Parameter{ParamProps: spec.ParamProps{Schema: spec.RefSchema(ref)}}). - Named("body"). - WithLocation("body"). - AsRequired()) -} - -// deleteOperation constructs a delete operation for a CRD -func (c *SwaggerConstructor) deleteOperation() *spec.Operation { - op := c.baseOperation(Resource, "delete"). - WithDescription(fmt.Sprintf("delete a %s", c.kind)). - RespondsWith(200, okResponse(statusSchemaRef)). - RespondsWith(202, acceptedResponse(statusSchemaRef)) - return addDeleteOperationParameters(op) -} - -// deleteCollectionOperation constructs a deletecollection operation for a CRD -func (c *SwaggerConstructor) deleteCollectionOperation() *spec.Operation { - op := c.baseOperation(Resource, "deletecollection"). - WithDescription(fmt.Sprintf("delete collection of %s", c.kind)) - return addCollectionOperationParameters(op) -} - -// readOperation constructs a read operation for a CRD, CRD's scale subresource -// or CRD's status subresource -func (c *SwaggerConstructor) readOperation(kind ResourceKind) *spec.Operation { - ref := c.constructSchemaRef(kind) - action := "read" - return c.baseOperation(kind, action). - WithDescription(c.constructDescription(kind, action)). - RespondsWith(200, okResponse(ref)) -} - -// replaceOperation constructs a replace operation for a CRD, CRD's scale subresource -// or CRD's status subresource -func (c *SwaggerConstructor) replaceOperation(kind ResourceKind) *spec.Operation { - ref := c.constructSchemaRef(kind) - action := "replace" - return c.baseOperation(kind, action). - WithDescription(c.constructDescription(kind, action)). - RespondsWith(200, okResponse(ref)). - RespondsWith(201, createdResponse(ref)). - AddParam((&spec.Parameter{ParamProps: spec.ParamProps{Schema: spec.RefSchema(ref)}}). - Named("body"). - WithLocation("body"). - AsRequired()) -} - -// patchOperation constructs a patch operation for a CRD, CRD's scale subresource -// or CRD's status subresource -func (c *SwaggerConstructor) patchOperation(kind ResourceKind) *spec.Operation { - ref := c.constructSchemaRef(kind) - action := "patch" - return c.baseOperation(kind, action). - WithDescription(c.constructDescription(kind, "partially update")). - RespondsWith(200, okResponse(ref)). - AddParam((&spec.Parameter{ParamProps: spec.ParamProps{Schema: spec.RefSchema(patchSchemaRef)}}). - Named("body"). - WithLocation("body"). - AsRequired()) -} - -// listSchema constructs the OpenAPI schema for a list of CRD objects -func (c *SwaggerConstructor) listSchema() *spec.Schema { - ref := c.constructSchemaRef(Resource) - s := new(spec.Schema). - WithDescription(fmt.Sprintf("%s is a list of %s", c.listKind, c.kind)). - WithRequired("items"). - SetProperty("apiVersion", *spec.StringProperty(). - WithDescription(swaggerTypeMetaDescriptions["apiVersion"])). - SetProperty("items", *spec.ArrayProperty(spec.RefSchema(ref)). - WithDescription(fmt.Sprintf("List of %s. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md", c.plural))). - SetProperty("kind", *spec.StringProperty(). - WithDescription(swaggerTypeMetaDescriptions["kind"])). - SetProperty("metadata", *spec.RefSchema(listMetaSchemaRef). - WithDescription(swaggerListDescriptions["metadata"])) - s.AddExtension("x-kubernetes-group-version-kind", map[string]string{ - "group": c.group, - "kind": c.listKind, - "version": c.version, - }) - return s -} - -// operationID generates the ID for an operation -func (c *SwaggerConstructor) operationID(kind ResourceKind, action string) string { - var collectionTemplate, namespacedTemplate, subresourceTemplate string - if action == "deletecollection" { - action = "delete" - collectionTemplate = "Collection" - } - if c.scope == apiextensions.NamespaceScoped { - namespacedTemplate = "Namespaced" - } - switch kind { - case Status: - subresourceTemplate = "Status" - case Scale: - subresourceTemplate = "Scale" - } - return fmt.Sprintf("%s%s%s%s%s%s%s", action, strings.Title(c.group), strings.Title(c.version), collectionTemplate, namespacedTemplate, c.kind, subresourceTemplate) -} - -// constructSchemaRef generates a reference to an object schema, based on the ResourceKind -// used by an operation -func (c *SwaggerConstructor) constructSchemaRef(kind ResourceKind) string { - var ref string - switch kind { - case Scale: - ref = scaleSchemaRef - default: - ref = fmt.Sprintf("#/definitions/%s.%s.%s", c.group, c.version, c.kind) - } - return ref -} - -// constructDescription generates a description for READ, REPLACE and PATCH operations, based on -// the ResourceKind used by the operation -func (c *SwaggerConstructor) constructDescription(kind ResourceKind, action string) string { - var descriptionTemplate string - switch kind { - case Status: - descriptionTemplate = "status of " - case Scale: - descriptionTemplate = "scale of " - } - return fmt.Sprintf("%s %sthe specified %s", action, descriptionTemplate, c.kind) -} - -// hasPerVersionSubresources returns true if a CRD spec uses per-version subresources. -func hasPerVersionSubresources(versions []apiextensions.CustomResourceDefinitionVersion) bool { - for _, v := range versions { - if v.Subresources != nil { - return true - } - } - return false -} - -// getSubresourcesForVersion returns the subresources for given version in given CRD spec. -func getSubresourcesForVersion(spec *apiextensions.CustomResourceDefinitionSpec, version string) (*apiextensions.CustomResourceSubresources, error) { - if !hasPerVersionSubresources(spec.Versions) { - return spec.Subresources, nil - } - if spec.Subresources != nil { - return nil, fmt.Errorf("malformed CustomResourceDefinitionSpec version %s: top-level and per-version subresources must be mutual exclusive", version) - } - for _, v := range spec.Versions { - if version == v.Name { - return v.Subresources, nil - } - } - return nil, fmt.Errorf("version %s not found in CustomResourceDefinitionSpec", version) -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go deleted file mode 100644 index 61853acb2ff..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -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 openapi - -import ( - "encoding/json" - - "github.com/go-openapi/spec" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" -) - -// ConvertJSONSchemaPropsToOpenAPIv2Schema converts our internal OpenAPI v3 schema -// (*apiextensions.JSONSchemaProps) to an OpenAPI v2 schema (*spec.Schema). -// NOTE: we use versioned type (v1beta1) here so that we can properly marshal the object -// using the JSON tags -func ConvertJSONSchemaPropsToOpenAPIv2Schema(in *v1beta1.JSONSchemaProps) (*spec.Schema, error) { - if in == nil { - return nil, nil - } - - // Marshal JSONSchemaProps into JSON and unmarshal the data into spec.Schema - data, err := json.Marshal(*in) - if err != nil { - return nil, err - } - out := new(spec.Schema) - if err := out.UnmarshalJSON(data); err != nil { - return nil, err - } - // Remove unsupported fields in OpenAPI v2 - out.OneOf = nil - out.AnyOf = nil - out.Not = nil - return out, nil -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go deleted file mode 100644 index d8c330280b6..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go +++ /dev/null @@ -1,448 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -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 openapi - -import ( - "encoding/json" - "reflect" - "testing" - - "github.com/go-openapi/spec" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" -) - -func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { - testStr := "test" - testStr2 := "test2" - testFloat64 := float64(6.4) - testInt64 := int64(64) - raw, _ := json.Marshal(testStr) - raw2, _ := json.Marshal(testStr2) - testApiextensionsJSON := v1beta1.JSON{Raw: raw} - - tests := []struct { - name string - in *v1beta1.JSONSchemaProps - expected *spec.Schema - }{ - { - name: "id", - in: &v1beta1.JSONSchemaProps{ - ID: testStr, - }, - expected: new(spec.Schema). - WithID(testStr), - }, - { - name: "$schema", - in: &v1beta1.JSONSchemaProps{ - Schema: "test", - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Schema: "test", - }, - }, - }, - { - name: "$ref", - in: &v1beta1.JSONSchemaProps{ - Ref: &testStr, - }, - expected: spec.RefSchema(testStr), - }, - { - name: "description", - in: &v1beta1.JSONSchemaProps{ - Description: testStr, - }, - expected: new(spec.Schema). - WithDescription(testStr), - }, - { - name: "type and format", - in: &v1beta1.JSONSchemaProps{ - Type: testStr, - Format: testStr2, - }, - expected: new(spec.Schema). - Typed(testStr, testStr2), - }, - { - name: "title", - in: &v1beta1.JSONSchemaProps{ - Title: testStr, - }, - expected: new(spec.Schema). - WithTitle(testStr), - }, - { - name: "default", - in: &v1beta1.JSONSchemaProps{ - Default: &testApiextensionsJSON, - }, - expected: new(spec.Schema). - WithDefault(testStr), - }, - { - name: "maximum and exclusiveMaximum", - in: &v1beta1.JSONSchemaProps{ - Maximum: &testFloat64, - ExclusiveMaximum: true, - }, - expected: new(spec.Schema). - WithMaximum(testFloat64, true), - }, - { - name: "minimum and exclusiveMinimum", - in: &v1beta1.JSONSchemaProps{ - Minimum: &testFloat64, - ExclusiveMinimum: true, - }, - expected: new(spec.Schema). - WithMinimum(testFloat64, true), - }, - { - name: "maxLength", - in: &v1beta1.JSONSchemaProps{ - MaxLength: &testInt64, - }, - expected: new(spec.Schema). - WithMaxLength(testInt64), - }, - { - name: "minLength", - in: &v1beta1.JSONSchemaProps{ - MinLength: &testInt64, - }, - expected: new(spec.Schema). - WithMinLength(testInt64), - }, - { - name: "pattern", - in: &v1beta1.JSONSchemaProps{ - Pattern: testStr, - }, - expected: new(spec.Schema). - WithPattern(testStr), - }, - { - name: "maxItems", - in: &v1beta1.JSONSchemaProps{ - MaxItems: &testInt64, - }, - expected: new(spec.Schema). - WithMaxItems(testInt64), - }, - { - name: "minItems", - in: &v1beta1.JSONSchemaProps{ - MinItems: &testInt64, - }, - expected: new(spec.Schema). - WithMinItems(testInt64), - }, - { - name: "uniqueItems", - in: &v1beta1.JSONSchemaProps{ - UniqueItems: true, - }, - expected: new(spec.Schema). - UniqueValues(), - }, - { - name: "multipleOf", - in: &v1beta1.JSONSchemaProps{ - MultipleOf: &testFloat64, - }, - expected: new(spec.Schema). - WithMultipleOf(testFloat64), - }, - { - name: "enum", - in: &v1beta1.JSONSchemaProps{ - Enum: []v1beta1.JSON{{Raw: raw}, {Raw: raw2}}, - }, - expected: new(spec.Schema). - WithEnum(testStr, testStr2), - }, - { - name: "maxProperties", - in: &v1beta1.JSONSchemaProps{ - MaxProperties: &testInt64, - }, - expected: new(spec.Schema). - WithMaxProperties(testInt64), - }, - { - name: "minProperties", - in: &v1beta1.JSONSchemaProps{ - MinProperties: &testInt64, - }, - expected: new(spec.Schema). - WithMinProperties(testInt64), - }, - { - name: "required", - in: &v1beta1.JSONSchemaProps{ - Required: []string{testStr, testStr2}, - }, - expected: new(spec.Schema). - WithRequired(testStr, testStr2), - }, - { - name: "items single props", - in: &v1beta1.JSONSchemaProps{ - Items: &v1beta1.JSONSchemaPropsOrArray{ - Schema: &v1beta1.JSONSchemaProps{ - Type: "boolean", - }, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Items: &spec.SchemaOrArray{ - Schema: spec.BooleanProperty(), - }, - }, - }, - }, - { - name: "items array props", - in: &v1beta1.JSONSchemaProps{ - Items: &v1beta1.JSONSchemaPropsOrArray{ - JSONSchemas: []v1beta1.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Items: &spec.SchemaOrArray{ - Schemas: []spec.Schema{ - *spec.BooleanProperty(), - *spec.StringProperty(), - }, - }, - }, - }, - }, - { - name: "allOf", - in: &v1beta1.JSONSchemaProps{ - AllOf: []v1beta1.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - expected: new(spec.Schema). - WithAllOf(*spec.BooleanProperty(), *spec.StringProperty()), - }, - { - name: "oneOf", - in: &v1beta1.JSONSchemaProps{ - OneOf: []v1beta1.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - expected: new(spec.Schema), - // expected: &spec.Schema{ - // SchemaProps: spec.SchemaProps{ - // OneOf: []spec.Schema{ - // *spec.BooleanProperty(), - // *spec.StringProperty(), - // }, - // }, - // }, - }, - { - name: "anyOf", - in: &v1beta1.JSONSchemaProps{ - AnyOf: []v1beta1.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - expected: new(spec.Schema), - // expected: &spec.Schema{ - // SchemaProps: spec.SchemaProps{ - // AnyOf: []spec.Schema{ - // *spec.BooleanProperty(), - // *spec.StringProperty(), - // }, - // }, - // }, - }, - { - name: "not", - in: &v1beta1.JSONSchemaProps{ - Not: &v1beta1.JSONSchemaProps{ - Type: "boolean", - }, - }, - expected: new(spec.Schema), - // expected: &spec.Schema{ - // SchemaProps: spec.SchemaProps{ - // Not: spec.BooleanProperty(), - // }, - // }, - }, - { - name: "properties", - in: &v1beta1.JSONSchemaProps{ - Properties: map[string]v1beta1.JSONSchemaProps{ - testStr: {Type: "boolean"}, - }, - }, - expected: new(spec.Schema). - SetProperty(testStr, *spec.BooleanProperty()), - }, - { - name: "additionalProperties", - in: &v1beta1.JSONSchemaProps{ - AdditionalProperties: &v1beta1.JSONSchemaPropsOrBool{ - Allows: true, - Schema: &v1beta1.JSONSchemaProps{Type: "boolean"}, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: spec.BooleanProperty(), - }, - }, - }, - }, - { - name: "patternProperties", - in: &v1beta1.JSONSchemaProps{ - PatternProperties: map[string]v1beta1.JSONSchemaProps{ - testStr: {Type: "boolean"}, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - PatternProperties: map[string]spec.Schema{ - testStr: *spec.BooleanProperty(), - }, - }, - }, - }, - { - name: "dependencies schema", - in: &v1beta1.JSONSchemaProps{ - Dependencies: v1beta1.JSONSchemaDependencies{ - testStr: v1beta1.JSONSchemaPropsOrStringArray{ - Schema: &v1beta1.JSONSchemaProps{Type: "boolean"}, - }, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Dependencies: spec.Dependencies{ - testStr: spec.SchemaOrStringArray{ - Schema: spec.BooleanProperty(), - }, - }, - }, - }, - }, - { - name: "dependencies string array", - in: &v1beta1.JSONSchemaProps{ - Dependencies: v1beta1.JSONSchemaDependencies{ - testStr: v1beta1.JSONSchemaPropsOrStringArray{ - Property: []string{testStr2}, - }, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Dependencies: spec.Dependencies{ - testStr: spec.SchemaOrStringArray{ - Property: []string{testStr2}, - }, - }, - }, - }, - }, - { - name: "additionalItems", - in: &v1beta1.JSONSchemaProps{ - AdditionalItems: &v1beta1.JSONSchemaPropsOrBool{ - Allows: true, - Schema: &v1beta1.JSONSchemaProps{Type: "boolean"}, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - AdditionalItems: &spec.SchemaOrBool{ - Allows: true, - Schema: spec.BooleanProperty(), - }, - }, - }, - }, - { - name: "definitions", - in: &v1beta1.JSONSchemaProps{ - Definitions: v1beta1.JSONSchemaDefinitions{ - testStr: v1beta1.JSONSchemaProps{Type: "boolean"}, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Definitions: spec.Definitions{ - testStr: *spec.BooleanProperty(), - }, - }, - }, - }, - { - name: "externalDocs", - in: &v1beta1.JSONSchemaProps{ - ExternalDocs: &v1beta1.ExternalDocumentation{ - Description: testStr, - URL: testStr2, - }, - }, - expected: new(spec.Schema). - WithExternalDocs(testStr, testStr2), - }, - { - name: "example", - in: &v1beta1.JSONSchemaProps{ - Example: &testApiextensionsJSON, - }, - expected: new(spec.Schema). - WithExample(testStr), - }, - } - - for _, test := range tests { - out, err := ConvertJSONSchemaPropsToOpenAPIv2Schema(test.in) - if err != nil { - t.Errorf("unexpected error in converting openapi schema: %v", test.name) - } - if !reflect.DeepEqual(out, test.expected) { - t.Errorf("result of conversion test '%v' didn't match, want: %v; got: %v", test.name, *test.expected, *out) - } - } -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go deleted file mode 100644 index 797fbeddc3a..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go +++ /dev/null @@ -1,228 +0,0 @@ -/* -Copyright 2018 The Kubernetes Authors. - -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 openapi - -import ( - "crypto/sha512" - "encoding/json" - "fmt" - - "github.com/go-openapi/spec" - autoscalingv1 "k8s.io/api/autoscaling/v1" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - deleteOptionsSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" - objectMetaSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" - scaleSpecSchemaRef = "#/definitions/io.k8s.api.autoscaling.v1.ScaleSpec" - scaleStatusSchemaRef = "#/definitions/io.k8s.api.autoscaling.v1.ScaleStatus" -) - -var swaggerTypeMetaDescriptions = metav1.TypeMeta{}.SwaggerDoc() -var swaggerDeleteOptionsDescriptions = metav1.DeleteOptions{}.SwaggerDoc() -var swaggerListDescriptions = metav1.List{}.SwaggerDoc() -var swaggerListOptionsDescriptions = metav1.ListOptions{}.SwaggerDoc() -var swaggerScaleDescriptions = autoscalingv1.Scale{}.SwaggerDoc() -var swaggerScaleSpecDescriptions = autoscalingv1.ScaleSpec{}.SwaggerDoc() -var swaggerScaleStatusDescriptions = autoscalingv1.ScaleStatus{}.SwaggerDoc() - -// calcSwaggerEtag calculates an etag of the OpenAPI swagger (spec) -func calcSwaggerEtag(openAPISpec *spec.Swagger) (string, error) { - specBytes, err := json.MarshalIndent(openAPISpec, " ", " ") - if err != nil { - return "", err - } - return fmt.Sprintf("\"%X\"", sha512.Sum512(specBytes)), nil -} - -// pathParameters constructs the Parameter used by all paths in the CRD swagger (spec) -func pathParameters() []spec.Parameter { - return []spec.Parameter{ - *spec.QueryParam("pretty"). - Typed("string", ""). - UniqueValues(). - WithDescription("If 'true', then the output is pretty printed."), - } -} - -// addDeleteOperationParameters add the body&query parameters used by a delete operation -func addDeleteOperationParameters(op *spec.Operation) *spec.Operation { - return op. - AddParam((&spec.Parameter{ParamProps: spec.ParamProps{Schema: spec.RefSchema(deleteOptionsSchemaRef)}}). - Named("body"). - WithLocation("body"). - AsRequired()). - AddParam(spec.QueryParam("dryRun"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerDeleteOptionsDescriptions["dryRun"])). - AddParam(spec.QueryParam("gracePeriodSeconds"). - Typed("integer", ""). - UniqueValues(). - WithDescription(swaggerDeleteOptionsDescriptions["gracePeriodSeconds"])). - AddParam(spec.QueryParam("orphanDependents"). - Typed("boolean", ""). - UniqueValues(). - WithDescription(swaggerDeleteOptionsDescriptions["orphanDependents"])). - AddParam(spec.QueryParam("propagationPolicy"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerDeleteOptionsDescriptions["propagationPolicy"])) -} - -// addCollectionOperationParameters adds the query parameters used by list and deletecollection -// operations -func addCollectionOperationParameters(op *spec.Operation) *spec.Operation { - return op. - AddParam(spec.QueryParam("continue"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["continue"])). - AddParam(spec.QueryParam("fieldSelector"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["fieldSelector"])). - AddParam(spec.QueryParam("includeUninitialized"). - Typed("boolean", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["includeUninitialized"])). - AddParam(spec.QueryParam("labelSelector"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["labelSelector"])). - AddParam(spec.QueryParam("limit"). - Typed("integer", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["limit"])). - AddParam(spec.QueryParam("resourceVersion"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["resourceVersion"])). - AddParam(spec.QueryParam("timeoutSeconds"). - Typed("integer", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["timeoutSeconds"])). - AddParam(spec.QueryParam("watch"). - Typed("boolean", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["watch"])) -} - -// okResponse constructs a 200 OK response with the input object schema reference -func okResponse(ref string) *spec.Response { - return spec.NewResponse(). - WithDescription("OK"). - WithSchema(spec.RefSchema(ref)) -} - -// createdResponse constructs a 201 Created response with the input object schema reference -func createdResponse(ref string) *spec.Response { - return spec.NewResponse(). - WithDescription("Created"). - WithSchema(spec.RefSchema(ref)) -} - -// acceptedResponse constructs a 202 Accepted response with the input object schema reference -func acceptedResponse(ref string) *spec.Response { - return spec.NewResponse(). - WithDescription("Accepted"). - WithSchema(spec.RefSchema(ref)) -} - -// unauthorizedResponse constructs a 401 Unauthorized response -func unauthorizedResponse() *spec.Response { - return spec.NewResponse(). - WithDescription("Unauthorized") -} - -// scaleSchema constructs the OpenAPI schema for io.k8s.api.autoscaling.v1.Scale objects -// TODO(roycaihw): this is a hack to let apiExtension apiserver and generic kube-apiserver -// to have the same io.k8s.api.autoscaling.v1.Scale definition, so that aggregator server won't -// detect name conflict and create a duplicate io.k8s.api.autoscaling.v1.Scale_V2 schema -// when aggregating the openapi spec. It would be better if apiExtension apiserver serves -// identical definition through the same code path (using routes) as generic kube-apiserver. -func scaleSchema() *spec.Schema { - s := new(spec.Schema). - WithDescription(swaggerScaleDescriptions[""]). - SetProperty("apiVersion", *spec.StringProperty(). - WithDescription(swaggerTypeMetaDescriptions["apiVersion"])). - SetProperty("kind", *spec.StringProperty(). - WithDescription(swaggerTypeMetaDescriptions["kind"])). - SetProperty("metadata", *spec.RefSchema(objectMetaSchemaRef). - WithDescription(swaggerScaleDescriptions["metadata"])). - SetProperty("spec", *spec.RefSchema(scaleSpecSchemaRef). - WithDescription(swaggerScaleDescriptions["spec"])). - SetProperty("status", *spec.RefSchema(scaleStatusSchemaRef). - WithDescription(swaggerScaleDescriptions["status"])) - - s.AddExtension("x-kubernetes-group-version-kind", []map[string]string{ - { - "group": "autoscaling", - "kind": "Scale", - "version": "v1", - }, - }) - return s -} - -// scaleSchema constructs the OpenAPI schema for io.k8s.api.autoscaling.v1.ScaleSpec objects -func scaleSpecSchema() *spec.Schema { - return new(spec.Schema). - WithDescription(swaggerScaleSpecDescriptions[""]). - SetProperty("replicas", *spec.Int32Property(). - WithDescription(swaggerScaleSpecDescriptions["replicas"])) -} - -// scaleSchema constructs the OpenAPI schema for io.k8s.api.autoscaling.v1.ScaleStatus objects -func scaleStatusSchema() *spec.Schema { - return new(spec.Schema). - WithDescription(swaggerScaleStatusDescriptions[""]). - WithRequired("replicas"). - SetProperty("replicas", *spec.Int32Property(). - WithDescription(swaggerScaleStatusDescriptions["replicas"])). - SetProperty("selector", *spec.StringProperty(). - WithDescription(swaggerScaleStatusDescriptions["selector"])) -} - -// CustomResourceDefinitionOpenAPISpec constructs the OpenAPI spec (swagger) and calculates -// etag for a given CustomResourceDefinitionSpec. -// NOTE: in apiserver we general operates on internal types. We are using versioned (v1beta1) -// validation schema here because we need the json tags to properly marshal the object to -// JSON. -func CustomResourceDefinitionOpenAPISpec(crdSpec *apiextensions.CustomResourceDefinitionSpec, version string, validationSchema *v1beta1.CustomResourceValidation) (*spec.Swagger, string, error) { - schema := &spec.Schema{} - if validationSchema != nil && validationSchema.OpenAPIV3Schema != nil { - var err error - schema, err = ConvertJSONSchemaPropsToOpenAPIv2Schema(validationSchema.OpenAPIV3Schema) - if err != nil { - return nil, "", err - } - } - crdSwaggerConstructor, err := NewSwaggerConstructor(schema, crdSpec, version) - if err != nil { - return nil, "", err - } - crdOpenAPISpec := crdSwaggerConstructor.ConstructCRDOpenAPISpec() - etag, err := calcSwaggerEtag(crdOpenAPISpec) - if err != nil { - return nil, "", err - } - return crdOpenAPISpec, etag, nil -} diff --git a/staging/src/k8s.io/apiserver/pkg/server/BUILD b/staging/src/k8s.io/apiserver/pkg/server/BUILD index 1d71c60a7d0..68588c5a83c 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/server/BUILD @@ -116,7 +116,6 @@ go_library( "//vendor/k8s.io/klog:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library", - "//vendor/k8s.io/kube-openapi/pkg/handler:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/util:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library", ], diff --git a/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go b/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go index fac46af815e..4efc82427e2 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go +++ b/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go @@ -25,8 +25,7 @@ import ( "time" systemd "github.com/coreos/go-systemd/daemon" - swagger "github.com/emicklei/go-restful-swagger12" - "github.com/go-openapi/spec" + "github.com/emicklei/go-restful-swagger12" "k8s.io/klog" "k8s.io/apimachinery/pkg/api/meta" @@ -48,7 +47,6 @@ import ( restclient "k8s.io/client-go/rest" openapibuilder "k8s.io/kube-openapi/pkg/builder" openapicommon "k8s.io/kube-openapi/pkg/common" - "k8s.io/kube-openapi/pkg/handler" openapiutil "k8s.io/kube-openapi/pkg/util" openapiproto "k8s.io/kube-openapi/pkg/util/proto" ) @@ -126,11 +124,6 @@ type GenericAPIServer struct { swaggerConfig *swagger.Config openAPIConfig *openapicommon.Config - // Expose the registered OpenAPI Services and built static OpenAPI spec if openAPIConfig is non-nil - OpenAPIService *handler.OpenAPIService // for endpoint /swagger.json - OpenAPIVersionedService *handler.OpenAPIService // for endpoint /openapi/v2 - StaticOpenAPISpec *spec.Swagger - // PostStartHooks are each called after the server has started listening, in a separate go func for each // with no guarantee of ordering between them. The map key is a name used for error reporting. // It may kill the process with a panic if it wishes to by returning an error. @@ -247,7 +240,7 @@ func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer { routes.Swagger{Config: s.swaggerConfig}.Install(s.Handler.GoRestfulContainer) } if s.openAPIConfig != nil { - s.OpenAPIService, s.OpenAPIVersionedService, s.StaticOpenAPISpec = routes.OpenAPI{ + routes.OpenAPI{ Config: s.openAPIConfig, }.Install(s.Handler.GoRestfulContainer, s.Handler.NonGoRestfulMux) } diff --git a/staging/src/k8s.io/apiserver/pkg/server/routes/BUILD b/staging/src/k8s.io/apiserver/pkg/server/routes/BUILD index a2fc6075cb7..0b81332a3a4 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/routes/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/server/routes/BUILD @@ -32,10 +32,8 @@ go_library( "//vendor/github.com/elazarl/go-bindata-assetfs:go_default_library", "//vendor/github.com/emicklei/go-restful:go_default_library", "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", - "//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/prometheus/client_golang/prometheus:go_default_library", "//vendor/k8s.io/klog:go_default_library", - "//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/handler:go_default_library", ], diff --git a/staging/src/k8s.io/apiserver/pkg/server/routes/openapi.go b/staging/src/k8s.io/apiserver/pkg/server/routes/openapi.go index d080e471061..934bbf84a04 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/routes/openapi.go +++ b/staging/src/k8s.io/apiserver/pkg/server/routes/openapi.go @@ -18,11 +18,9 @@ package routes import ( restful "github.com/emicklei/go-restful" - "github.com/go-openapi/spec" "k8s.io/klog" "k8s.io/apiserver/pkg/server/mux" - "k8s.io/kube-openapi/pkg/builder" "k8s.io/kube-openapi/pkg/common" "k8s.io/kube-openapi/pkg/handler" ) @@ -32,27 +30,17 @@ type OpenAPI struct { Config *common.Config } -// Install adds the SwaggerUI webservice to the given mux. This function returns -// the built static OpenAPI spec and the registered OpenAPI services to allow -// further OpenAPI spec aggregation. -func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) (openAPIService, openAPIVersionedService *handler.OpenAPIService, spec *spec.Swagger) { - var err error - // Record the static OpenAPI spec to allow further OpenAPI spec aggregation - // with this static spec on the registered OpenAPI services - spec, err = builder.BuildOpenAPISpec(c.RegisteredWebServices(), oa.Config) - if err != nil { - klog.Fatalf("Failed to build open api spec for root: %v", err) - } +// Install adds the SwaggerUI webservice to the given mux. +func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) { // NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec, // and switch to a single /openapi/v2 endpoint in Kubernetes 1.10. The design doc and deprecation process // are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU. - openAPIService, err = handler.RegisterOpenAPIService(spec, "/swagger.json", mux) + _, err := handler.BuildAndRegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, mux) if err != nil { klog.Fatalf("Failed to register open api spec for root: %v", err) } - openAPIVersionedService, err = handler.RegisterOpenAPIVersionedService(spec, "/openapi/v2", mux) + _, err = handler.BuildAndRegisterOpenAPIVersionedService("/openapi/v2", c.RegisteredWebServices(), oa.Config, mux) if err != nil { klog.Fatalf("Failed to register versioned open api spec for root: %v", err) } - return openAPIService, openAPIVersionedService, spec } diff --git a/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator.go b/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator.go index e236f27d890..4c1f75bd6d9 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator.go @@ -53,19 +53,12 @@ type specAggregator struct { // provided for dynamic OpenAPI spec openAPIService *handler.OpenAPIService openAPIVersionedService *handler.OpenAPIService - - // initialized is set to be true at the end of startup. All local specs - // must be registered before initialized is set, we panic otherwise. - initialized bool } var _ AggregationManager = &specAggregator{} // This function is not thread safe as it only being called on startup. func (s *specAggregator) addLocalSpec(spec *spec.Swagger, localHandler http.Handler, name, etag string) { - if s.initialized { - panic("Local spec must not be added after startup") - } localAPIService := apiregistration.APIService{} localAPIService.Name = name s.openAPISpecs[name] = &openAPISpecInfo{ @@ -76,17 +69,6 @@ func (s *specAggregator) addLocalSpec(spec *spec.Swagger, localHandler http.Hand } } -// GetAPIServicesName returns the names of APIServices recorded in specAggregator.openAPISpecs. -// We use this function to pass the names of local APIServices to the controller in this package, -// so that the controller can periodically sync the OpenAPI spec from delegation API servers. -func (s *specAggregator) GetAPIServiceNames() []string { - names := make([]string, len(s.openAPISpecs)) - for key := range s.openAPISpecs { - names = append(names, key) - } - return names -} - // BuildAndRegisterAggregator registered OpenAPI aggregator handler. This function is not thread safe as it only being called on startup. func BuildAndRegisterAggregator(downloader *Downloader, delegationTarget server.DelegationTarget, webServices []*restful.WebService, config *common.Config, pathHandler common.PathHandler) (AggregationManager, error) { @@ -142,9 +124,6 @@ func BuildAndRegisterAggregator(downloader *Downloader, delegationTarget server. return nil, err } - // We set initialized to be true to forbid any future local spec addition - s.initialized = true - return s, nil } diff --git a/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go b/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go index a349deea761..49d190e9020 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go @@ -49,9 +49,6 @@ type AggregationManager interface { UpdateAPIServiceSpec(apiServiceName string, spec *spec.Swagger, etag string) error RemoveAPIServiceSpec(apiServiceName string) error GetAPIServiceInfo(apiServiceName string) (handler http.Handler, etag string, exists bool) - - // GetAPIServicesName returns the names of APIServices recorded in AggregationManager. - GetAPIServiceNames() []string } // AggregationController periodically check for changes in OpenAPI specs of APIServices and update/remove @@ -75,18 +72,6 @@ func NewAggregationController(downloader *Downloader, openAPIAggregationManager } c.syncHandler = c.sync - // During initialization, openAPIAggregationManager only has record of local APIServices. There must be - // no aggregated APIService recorded, because aggregated APIServices only get added to openAPIAggregationManager - // by calling AggregationController.AddAPIService or AggregationController.UpdateAPIService after the - // controller is initialized. - // Here we add delegation target API services to queue, to periodically sync dynamic OpenAPI spec from - // delegation target. - // NOTE: openAPIAggregationManager.GetAPIServiceNames() will also return the APIService of non-name spec - // for aggregator, which has no http.Handler. The first time sync (when popping off from queue) for - // this APIService will be a no-op, and the controller will drop the APIService from queue. - for _, name := range openAPIAggregationManager.GetAPIServiceNames() { - c.queue.AddAfter(name, time.Second) - } return c } diff --git a/test/e2e/apimachinery/custom_resource_definition.go b/test/e2e/apimachinery/custom_resource_definition.go index b282bc184cf..fb0469487e6 100644 --- a/test/e2e/apimachinery/custom_resource_definition.go +++ b/test/e2e/apimachinery/custom_resource_definition.go @@ -17,22 +17,16 @@ limitations under the License. package apimachinery import ( - "fmt" - "strings" - "time" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/test/integration/fixtures" utilversion "k8s.io/apimachinery/pkg/util/version" - "k8s.io/apimachinery/pkg/util/wait" "k8s.io/kubernetes/test/e2e/framework" . "github.com/onsi/ginkgo" ) var crdVersion = utilversion.MustParseSemantic("v1.7.0") -var crdOpenAPIVersion = utilversion.MustParseSemantic("v1.13.0") var _ = SIGDescribe("CustomResourceDefinition resources", func() { @@ -73,58 +67,5 @@ var _ = SIGDescribe("CustomResourceDefinition resources", func() { } }() }) - - It("has OpenAPI spec served with CRD Validation chema", func() { - framework.SkipUnlessServerVersionGTE(crdOpenAPIVersion, f.ClientSet.Discovery()) - - config, err := framework.LoadConfig() - if err != nil { - framework.Failf("failed to load config: %v", err) - } - - apiExtensionClient, err := clientset.NewForConfig(config) - if err != nil { - framework.Failf("failed to initialize apiExtensionClient: %v", err) - } - - randomDefinition := fixtures.NewRandomNameCustomResourceDefinition(v1beta1.ClusterScoped) - - //create CRD and waits for the resource to be recognized and available. - randomDefinition, err = fixtures.CreateNewCustomResourceDefinition(randomDefinition, apiExtensionClient, f.DynamicClient) - if err != nil { - framework.Failf("failed to create CustomResourceDefinition: %v", err) - } - - // TODO(roycaihw): think about tweaking feature gates in e2e test (is it possible/easy - // to do so?) and have CRD use top-level/per-version schema - // Also need to test NamespaceScoped CRDs - - // We use a wait.Poll block here because the kube-aggregator openapi - // controller takes time to rotate the queue and resync apiextensions-apiserver's spec - if err := wait.Poll(5*time.Second, 120*time.Second, func() (bool, error) { - data, err := f.ClientSet.CoreV1().RESTClient().Get(). - AbsPath("/swagger.json"). - DoRaw() - - if err != nil { - return false, err - } - // TODO(roycaihw): verify more Paths and List Definitions, also for multiple versions - baseDefinition := fmt.Sprintf("%s.%s.%s", randomDefinition.Spec.Group, randomDefinition.Spec.Version, randomDefinition.Spec.Names.Kind) - basePath := fmt.Sprintf("/apis/%s/%s/%s", randomDefinition.Spec.Group, randomDefinition.Spec.Version, randomDefinition.Spec.Names.Plural) - return strings.Contains(string(data), basePath) && - strings.Contains(string(data), baseDefinition), nil - }); err != nil { - framework.Failf("failed to wait for apiserver to serve openapi spec for registered CRD: %v", err) - } - - defer func() { - err = fixtures.DeleteCustomResourceDefinition(randomDefinition, apiExtensionClient) - if err != nil { - framework.Failf("failed to delete CustomResourceDefinition: %v", err) - } - }() - }) - }) }) diff --git a/test/integration/master/BUILD b/test/integration/master/BUILD index ff8f048bb6a..87c9afda8fe 100644 --- a/test/integration/master/BUILD +++ b/test/integration/master/BUILD @@ -66,7 +66,6 @@ go_test( "//test/integration/framework:go_default_library", "//test/utils:go_default_library", "//vendor/github.com/evanphx/json-patch:go_default_library", - "//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/sigs.k8s.io/yaml:go_default_library", ] + select({ "@io_bazel_rules_go//go/platform:android": [ diff --git a/test/integration/master/crd_test.go b/test/integration/master/crd_test.go index 56e4a037ed2..ac28b037c30 100644 --- a/test/integration/master/crd_test.go +++ b/test/integration/master/crd_test.go @@ -18,12 +18,9 @@ package master import ( "encoding/json" - "fmt" "testing" "time" - "github.com/go-openapi/spec" - admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1" networkingv1 "k8s.io/api/networking/v1" apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" @@ -275,91 +272,6 @@ func TestCRD(t *testing.T) { } } -func TestCRDOpenAPI(t *testing.T) { - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.Initializers, true)() - result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()) - defer result.TearDownFn() - kubeclient, err := kubernetes.NewForConfig(result.ClientConfig) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - apiextensionsclient, err := apiextensionsclientset.NewForConfig(result.ClientConfig) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - t.Logf("Trying to create a custom resource without conflict") - crd := &apiextensionsv1beta1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foos.cr.bar.com", - }, - Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ - Group: "cr.bar.com", - Version: "v1", - Scope: apiextensionsv1beta1.NamespaceScoped, - Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ - Plural: "foos", - Kind: "Foo", - }, - Validation: &apiextensionsv1beta1.CustomResourceValidation{ - OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{ - Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ - "foo": {Type: "string"}, - }, - }, - }, - }, - } - etcd.CreateTestCRDs(t, apiextensionsclient, false, crd) - waitForSpec := func(expectedType string) { - t.Logf(`Waiting for {properties: {"foo": {"type":"%s"}}} to show up in schema`, expectedType) - lastMsg := "" - if err := wait.PollImmediate(500*time.Millisecond, 120*time.Second, func() (bool, error) { - lastMsg = "" - bs, err := kubeclient.RESTClient().Get().AbsPath("openapi", "v2").DoRaw() - if err != nil { - return false, err - } - spec := spec.Swagger{} - if err := json.Unmarshal(bs, &spec); err != nil { - return false, err - } - if spec.SwaggerProps.Paths == nil { - lastMsg = "spec.SwaggerProps.Paths is nil" - return false, nil - } - d, ok := spec.SwaggerProps.Definitions["cr.bar.com.v1.Foo"] - if !ok { - lastMsg = `spec.SwaggerProps.Definitions["cr.bar.com.v1.Foo"] not found` - return false, nil - } - p, ok := d.Properties["foo"] - if !ok { - lastMsg = `spec.SwaggerProps.Definitions["cr.bar.com.v1.Foo"].Properties["foo"] not found` - return false, nil - } - if !p.Type.Contains(expectedType) { - lastMsg = fmt.Sprintf(`spec.SwaggerProps.Definitions["cr.bar.com.v1.Foo"].Properties["foo"].Type should be %q, but got: %q`, expectedType, p.Type) - return false, nil - } - return true, nil - }); err != nil { - t.Fatalf("Failed to see %s OpenAPI spec in discovery: %v, last message: %s", crd.Name, err, lastMsg) - } - } - waitForSpec("string") - crd, err = apiextensionsclient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{}) - if err != nil { - t.Fatal(err) - } - prop := crd.Spec.Validation.OpenAPIV3Schema.Properties["foo"] - prop.Type = "boolean" - crd.Spec.Validation.OpenAPIV3Schema.Properties["foo"] = prop - if _, err = apiextensionsclient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(crd); err != nil { - t.Fatal(err) - } - waitForSpec("boolean") -} - type Foo struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`