Commit e2ca575d authored by Mehdy Bohlool's avatar Mehdy Bohlool

CRD Conversion

parent ea54a0c5
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
genericoptions "k8s.io/apiserver/pkg/server/options" genericoptions "k8s.io/apiserver/pkg/server/options"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/pkg/util/webhook"
kubeexternalinformers "k8s.io/client-go/informers" kubeexternalinformers "k8s.io/client-go/informers"
"k8s.io/kubernetes/cmd/kube-apiserver/app/options" "k8s.io/kubernetes/cmd/kube-apiserver/app/options"
) )
...@@ -38,6 +39,8 @@ func createAPIExtensionsConfig( ...@@ -38,6 +39,8 @@ func createAPIExtensionsConfig(
pluginInitializers []admission.PluginInitializer, pluginInitializers []admission.PluginInitializer,
commandOptions *options.ServerRunOptions, commandOptions *options.ServerRunOptions,
masterCount int, masterCount int,
serviceResolver webhook.ServiceResolver,
authResolverWrapper webhook.AuthenticationInfoResolverWrapper,
) (*apiextensionsapiserver.Config, error) { ) (*apiextensionsapiserver.Config, error) {
// make a shallow copy to let us twiddle a few things // make a shallow copy to let us twiddle a few things
// most of the config actually remains the same. We only need to mess with a couple items related to the particulars of the apiextensions // most of the config actually remains the same. We only need to mess with a couple items related to the particulars of the apiextensions
...@@ -74,6 +77,8 @@ func createAPIExtensionsConfig( ...@@ -74,6 +77,8 @@ func createAPIExtensionsConfig(
ExtraConfig: apiextensionsapiserver.ExtraConfig{ ExtraConfig: apiextensionsapiserver.ExtraConfig{
CRDRESTOptionsGetter: apiextensionsoptions.NewCRDRESTOptionsGetter(etcdOptions), CRDRESTOptionsGetter: apiextensionsoptions.NewCRDRESTOptionsGetter(etcdOptions),
MasterCount: masterCount, MasterCount: masterCount,
AuthResolverWrapper: authResolverWrapper,
ServiceResolver: serviceResolver,
}, },
} }
......
...@@ -165,7 +165,8 @@ func CreateServerChain(completedOptions completedServerRunOptions, stopCh <-chan ...@@ -165,7 +165,8 @@ func CreateServerChain(completedOptions completedServerRunOptions, stopCh <-chan
} }
// If additional API servers are added, they should be gated. // If additional API servers are added, they should be gated.
apiExtensionsConfig, err := createAPIExtensionsConfig(*kubeAPIServerConfig.GenericConfig, kubeAPIServerConfig.ExtraConfig.VersionedInformers, pluginInitializer, completedOptions.ServerRunOptions, completedOptions.MasterCount) apiExtensionsConfig, err := createAPIExtensionsConfig(*kubeAPIServerConfig.GenericConfig, kubeAPIServerConfig.ExtraConfig.VersionedInformers, pluginInitializer, completedOptions.ServerRunOptions, completedOptions.MasterCount,
serviceResolver, webhook.NewDefaultAuthenticationInfoResolverWrapper(proxyTransport, kubeAPIServerConfig.GenericConfig.LoopbackClientConfig))
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -31,6 +31,7 @@ import ( ...@@ -31,6 +31,7 @@ import (
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage" serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/apiserver/pkg/util/webhook"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install"
...@@ -78,6 +79,11 @@ type ExtraConfig struct { ...@@ -78,6 +79,11 @@ type ExtraConfig struct {
// MasterCount is used to detect whether cluster is HA, and if it is // MasterCount is used to detect whether cluster is HA, and if it is
// the CRD Establishing will be hold by 5 seconds. // the CRD Establishing will be hold by 5 seconds.
MasterCount int MasterCount int
// ServiceResolver is used in CR webhook converters to resolve webhook's service names
ServiceResolver webhook.ServiceResolver
// AuthResolverWrapper is used in CR webhook converters
AuthResolverWrapper webhook.AuthenticationInfoResolverWrapper
} }
type Config struct { type Config struct {
...@@ -167,7 +173,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) ...@@ -167,7 +173,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
delegate: delegateHandler, delegate: delegateHandler,
} }
establishingController := establish.NewEstablishingController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), crdClient.Apiextensions()) establishingController := establish.NewEstablishingController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), crdClient.Apiextensions())
crdHandler := NewCustomResourceDefinitionHandler( crdHandler, err := NewCustomResourceDefinitionHandler(
versionDiscoveryHandler, versionDiscoveryHandler,
groupDiscoveryHandler, groupDiscoveryHandler,
s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(),
...@@ -175,8 +181,13 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) ...@@ -175,8 +181,13 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
c.ExtraConfig.CRDRESTOptionsGetter, c.ExtraConfig.CRDRESTOptionsGetter,
c.GenericConfig.AdmissionControl, c.GenericConfig.AdmissionControl,
establishingController, establishingController,
c.ExtraConfig.ServiceResolver,
c.ExtraConfig.AuthResolverWrapper,
c.ExtraConfig.MasterCount, c.ExtraConfig.MasterCount,
) )
if err != nil {
return nil, err
}
s.GenericAPIServer.Handler.NonGoRestfulMux.Handle("/apis", crdHandler) s.GenericAPIServer.Handler.NonGoRestfulMux.Handle("/apis", crdHandler)
s.GenericAPIServer.Handler.NonGoRestfulMux.HandlePrefix("/apis/", crdHandler) s.GenericAPIServer.Handler.NonGoRestfulMux.HandlePrefix("/apis/", crdHandler)
......
...@@ -20,39 +20,74 @@ import ( ...@@ -20,39 +20,74 @@ import (
"fmt" "fmt"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
"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"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/pkg/util/webhook"
) )
// NewCRDConverter returns a new CRD converter based on the conversion settings in crd object. // CRConverterFactory is the factory for all CR converters.
func NewCRDConverter(crd *apiextensions.CustomResourceDefinition) (safe, unsafe runtime.ObjectConvertor) { type CRConverterFactory struct {
// webhookConverterFactory is the factory for webhook converters.
// This field should not be used if CustomResourceWebhookConversion feature is disabled.
webhookConverterFactory *webhookConverterFactory
}
// NewCRConverterFactory creates a new CRConverterFactory
func NewCRConverterFactory(serviceResolver webhook.ServiceResolver, authResolverWrapper webhook.AuthenticationInfoResolverWrapper) (*CRConverterFactory, error) {
converterFactory := &CRConverterFactory{}
if utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceWebhookConversion) {
webhookConverterFactory, err := newWebhookConverterFactory(serviceResolver, authResolverWrapper)
if err != nil {
return nil, err
}
converterFactory.webhookConverterFactory = webhookConverterFactory
}
return converterFactory, nil
}
// NewConverter returns a new CR converter based on the conversion settings in crd object.
func (m *CRConverterFactory) NewConverter(crd *apiextensions.CustomResourceDefinition) (safe, unsafe runtime.ObjectConvertor, err error) {
validVersions := map[schema.GroupVersion]bool{} validVersions := map[schema.GroupVersion]bool{}
for _, version := range crd.Spec.Versions { for _, version := range crd.Spec.Versions {
validVersions[schema.GroupVersion{Group: crd.Spec.Group, Version: version.Name}] = true validVersions[schema.GroupVersion{Group: crd.Spec.Group, Version: version.Name}] = true
} }
// The only converter right now is nopConverter. More converters will be returned based on the switch crd.Spec.Conversion.Strategy {
// CRD object when they introduced. case apiextensions.NoneConverter:
unsafe = &crdConverter{ unsafe = &crConverter{
clusterScoped: crd.Spec.Scope == apiextensions.ClusterScoped, clusterScoped: crd.Spec.Scope == apiextensions.ClusterScoped,
delegate: &nopConverter{ delegate: &nopConverter{
validVersions: validVersions, validVersions: validVersions,
}, },
}
return &safeConverterWrapper{unsafe}, unsafe, nil
case apiextensions.WebhookConverter:
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceWebhookConversion) {
return nil, nil, fmt.Errorf("webhook conversion is disabled on this cluster")
}
unsafe, err := m.webhookConverterFactory.NewWebhookConverter(validVersions, crd)
if err != nil {
return nil, nil, err
}
return &safeConverterWrapper{unsafe}, unsafe, nil
} }
return &safeConverterWrapper{unsafe}, unsafe
return nil, nil, fmt.Errorf("unknown conversion strategy %q for CRD %s", crd.Spec.Conversion.Strategy, crd.Name)
} }
var _ runtime.ObjectConvertor = &crdConverter{} var _ runtime.ObjectConvertor = &crConverter{}
// crdConverter extends the delegate with generic CRD conversion behaviour. The delegate will implement the // crConverter extends the delegate with generic CR conversion behaviour. The delegate will implement the
// user defined conversion strategy given in the CustomResourceDefinition. // user defined conversion strategy given in the CustomResourceDefinition.
type crdConverter struct { type crConverter struct {
delegate runtime.ObjectConvertor delegate runtime.ObjectConvertor
clusterScoped bool clusterScoped bool
} }
func (c *crdConverter) ConvertFieldLabel(gvk schema.GroupVersionKind, label, value string) (string, string, error) { func (c *crConverter) ConvertFieldLabel(gvk schema.GroupVersionKind, label, value string) (string, string, error) {
// We currently only support metadata.namespace and metadata.name. // We currently only support metadata.namespace and metadata.name.
switch { switch {
case label == "metadata.name": case label == "metadata.name":
...@@ -64,12 +99,12 @@ func (c *crdConverter) ConvertFieldLabel(gvk schema.GroupVersionKind, label, val ...@@ -64,12 +99,12 @@ func (c *crdConverter) ConvertFieldLabel(gvk schema.GroupVersionKind, label, val
} }
} }
func (c *crdConverter) Convert(in, out, context interface{}) error { func (c *crConverter) Convert(in, out, context interface{}) error {
return c.delegate.Convert(in, out, context) return c.delegate.Convert(in, out, context)
} }
// ConvertToVersion converts in object to the given gvk in place and returns the same `in` object. // ConvertToVersion converts in object to the given gvk in place and returns the same `in` object.
func (c *crdConverter) ConvertToVersion(in runtime.Object, target runtime.GroupVersioner) (runtime.Object, error) { func (c *crConverter) ConvertToVersion(in runtime.Object, target runtime.GroupVersioner) (runtime.Object, error) {
// Run the converter on the list items instead of list itself // Run the converter on the list items instead of list itself
if list, ok := in.(*unstructured.UnstructuredList); ok { if list, ok := in.(*unstructured.UnstructuredList); ok {
for i := range list.Items { for i := range list.Items {
......
...@@ -49,11 +49,11 @@ func (c *nopConverter) Convert(in, out, context interface{}) error { ...@@ -49,11 +49,11 @@ func (c *nopConverter) Convert(in, out, context interface{}) error {
outGVK := unstructOut.GroupVersionKind() outGVK := unstructOut.GroupVersionKind()
if !c.validVersions[outGVK.GroupVersion()] { if !c.validVersions[outGVK.GroupVersion()] {
return fmt.Errorf("request to convert CRD from an invalid group/version: %s", outGVK.String()) return fmt.Errorf("request to convert CR from an invalid group/version: %s", outGVK.String())
} }
inGVK := unstructIn.GroupVersionKind() inGVK := unstructIn.GroupVersionKind()
if !c.validVersions[inGVK.GroupVersion()] { if !c.validVersions[inGVK.GroupVersion()] {
return fmt.Errorf("request to convert CRD to an invalid group/version: %s", inGVK.String()) return fmt.Errorf("request to convert CR to an invalid group/version: %s", inGVK.String())
} }
unstructOut.SetUnstructuredContent(unstructIn.UnstructuredContent()) unstructOut.SetUnstructuredContent(unstructIn.UnstructuredContent())
...@@ -72,7 +72,7 @@ func (c *nopConverter) ConvertToVersion(in runtime.Object, target runtime.GroupV ...@@ -72,7 +72,7 @@ func (c *nopConverter) ConvertToVersion(in runtime.Object, target runtime.GroupV
return nil, fmt.Errorf("%v is unstructured and is not suitable for converting to %q", kind, target) return nil, fmt.Errorf("%v is unstructured and is not suitable for converting to %q", kind, target)
} }
if !c.validVersions[gvk.GroupVersion()] { if !c.validVersions[gvk.GroupVersion()] {
return nil, fmt.Errorf("request to convert CRD to an invalid group/version: %s", gvk.String()) return nil, fmt.Errorf("request to convert CR to an invalid group/version: %s", gvk.String())
} }
in.GetObjectKind().SetGroupVersionKind(gvk) in.GetObjectKind().SetGroupVersionKind(gvk)
return in, nil return in, nil
......
...@@ -67,6 +67,7 @@ import ( ...@@ -67,6 +67,7 @@ import (
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features" apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
"k8s.io/apiextensions-apiserver/pkg/registry/customresource" "k8s.io/apiextensions-apiserver/pkg/registry/customresource"
"k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor" "k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor"
"k8s.io/apiserver/pkg/util/webhook"
) )
// crdHandler serves the `/apis` endpoint. // crdHandler serves the `/apis` endpoint.
...@@ -93,6 +94,8 @@ type crdHandler struct { ...@@ -93,6 +94,8 @@ type crdHandler struct {
// MasterCount is used to implement sleep to improve // MasterCount is used to implement sleep to improve
// CRD establishing process for HA clusters. // CRD establishing process for HA clusters.
masterCount int masterCount int
converterFactory *conversion.CRConverterFactory
} }
// crdInfo stores enough information to serve the storage for the custom resource // crdInfo stores enough information to serve the storage for the custom resource
...@@ -129,7 +132,9 @@ func NewCustomResourceDefinitionHandler( ...@@ -129,7 +132,9 @@ func NewCustomResourceDefinitionHandler(
restOptionsGetter generic.RESTOptionsGetter, restOptionsGetter generic.RESTOptionsGetter,
admission admission.Interface, admission admission.Interface,
establishingController *establish.EstablishingController, establishingController *establish.EstablishingController,
masterCount int) *crdHandler { serviceResolver webhook.ServiceResolver,
authResolverWrapper webhook.AuthenticationInfoResolverWrapper,
masterCount int) (*crdHandler, error) {
ret := &crdHandler{ ret := &crdHandler{
versionDiscoveryHandler: versionDiscoveryHandler, versionDiscoveryHandler: versionDiscoveryHandler,
groupDiscoveryHandler: groupDiscoveryHandler, groupDiscoveryHandler: groupDiscoveryHandler,
...@@ -147,10 +152,15 @@ func NewCustomResourceDefinitionHandler( ...@@ -147,10 +152,15 @@ func NewCustomResourceDefinitionHandler(
ret.removeDeadStorage() ret.removeDeadStorage()
}, },
}) })
crConverterFactory, err := conversion.NewCRConverterFactory(serviceResolver, authResolverWrapper)
if err != nil {
return nil, err
}
ret.converterFactory = crConverterFactory
ret.customStorage.Store(crdStorageMap{}) ret.customStorage.Store(crdStorageMap{})
return ret return ret, nil
} }
func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (r *crdHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
...@@ -433,7 +443,10 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource ...@@ -433,7 +443,10 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
scaleScopes := map[string]handlers.RequestScope{} scaleScopes := map[string]handlers.RequestScope{}
for _, v := range crd.Spec.Versions { for _, v := range crd.Spec.Versions {
safeConverter, unsafeConverter := conversion.NewCRDConverter(crd) safeConverter, unsafeConverter, err := r.converterFactory.NewConverter(crd)
if err != nil {
return nil, err
}
// In addition to Unstructured objects (Custom Resources), we also may sometimes need to // In addition to Unstructured objects (Custom Resources), we also may sometimes need to
// decode unversioned Options objects, so we delegate to parameterScheme for such types. // decode unversioned Options objects, so we delegate to parameterScheme for such types.
parameterScheme := runtime.NewScheme() parameterScheme := runtime.NewScheme()
......
...@@ -79,14 +79,24 @@ func TestConvertFieldLabel(t *testing.T) { ...@@ -79,14 +79,24 @@ func TestConvertFieldLabel(t *testing.T) {
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
crd := apiextensions.CustomResourceDefinition{} crd := apiextensions.CustomResourceDefinition{
Spec: apiextensions.CustomResourceDefinitionSpec{
Conversion: &apiextensions.CustomResourceConversion{
Strategy: "None",
},
},
}
if test.clusterScoped { if test.clusterScoped {
crd.Spec.Scope = apiextensions.ClusterScoped crd.Spec.Scope = apiextensions.ClusterScoped
} else { } else {
crd.Spec.Scope = apiextensions.NamespaceScoped crd.Spec.Scope = apiextensions.NamespaceScoped
} }
_, c := conversion.NewCRDConverter(&crd) f, err := conversion.NewCRConverterFactory(nil, nil)
if err != nil {
t.Fatal(err)
}
_, c, err := f.NewConverter(&crd)
label, value, err := c.ConvertFieldLabel(schema.GroupVersionKind{}, test.label, "value") label, value, err := c.ConvertFieldLabel(schema.GroupVersionKind{}, test.label, "value")
if e, a := test.expectError, err != nil; e != a { if e, a := test.expectError, err != nil; e != a {
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"net/url"
"github.com/spf13/pflag" "github.com/spf13/pflag"
...@@ -30,6 +31,9 @@ import ( ...@@ -30,6 +31,9 @@ import (
genericregistry "k8s.io/apiserver/pkg/registry/generic" genericregistry "k8s.io/apiserver/pkg/registry/generic"
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
genericoptions "k8s.io/apiserver/pkg/server/options" genericoptions "k8s.io/apiserver/pkg/server/options"
"k8s.io/apiserver/pkg/util/proxy"
"k8s.io/apiserver/pkg/util/webhook"
"k8s.io/client-go/listers/core/v1"
) )
const defaultEtcdPathPrefix = "/registry/apiextensions.kubernetes.io" const defaultEtcdPathPrefix = "/registry/apiextensions.kubernetes.io"
...@@ -94,6 +98,8 @@ func (o CustomResourceDefinitionsServerOptions) Config() (*apiserver.Config, err ...@@ -94,6 +98,8 @@ func (o CustomResourceDefinitionsServerOptions) Config() (*apiserver.Config, err
GenericConfig: serverConfig, GenericConfig: serverConfig,
ExtraConfig: apiserver.ExtraConfig{ ExtraConfig: apiserver.ExtraConfig{
CRDRESTOptionsGetter: NewCRDRESTOptionsGetter(*o.RecommendedOptions.Etcd), CRDRESTOptionsGetter: NewCRDRESTOptionsGetter(*o.RecommendedOptions.Etcd),
ServiceResolver: &serviceResolver{serverConfig.SharedInformerFactory.Core().V1().Services().Lister()},
AuthResolverWrapper: webhook.NewDefaultAuthenticationInfoResolverWrapper(nil, serverConfig.LoopbackClientConfig),
}, },
} }
return config, nil return config, nil
...@@ -114,3 +120,11 @@ func NewCRDRESTOptionsGetter(etcdOptions genericoptions.EtcdOptions) genericregi ...@@ -114,3 +120,11 @@ func NewCRDRESTOptionsGetter(etcdOptions genericoptions.EtcdOptions) genericregi
return ret return ret
} }
type serviceResolver struct {
services v1.ServiceLister
}
func (r *serviceResolver) ResolveEndpoint(namespace, name string) (*url.URL, error) {
return proxy.ResolveCluster(r.services, namespace, name)
}
...@@ -76,8 +76,8 @@ func newNamespacedCustomResourceClient(ns string, client dynamic.Interface, crd ...@@ -76,8 +76,8 @@ func newNamespacedCustomResourceClient(ns string, client dynamic.Interface, crd
return newNamespacedCustomResourceVersionedClient(ns, client, crd, crd.Spec.Versions[0].Name) return newNamespacedCustomResourceVersionedClient(ns, client, crd, crd.Spec.Versions[0].Name)
} }
// updateCustomResourceDefinitionWithRetry updates a CRD, retrying up to 5 times on version conflict errors. // UpdateCustomResourceDefinitionWithRetry updates a CRD, retrying up to 5 times on version conflict errors.
func updateCustomResourceDefinitionWithRetry(client clientset.Interface, name string, update func(*apiextensionsv1beta1.CustomResourceDefinition)) (*apiextensionsv1beta1.CustomResourceDefinition, error) { func UpdateCustomResourceDefinitionWithRetry(client clientset.Interface, name string, update func(*apiextensionsv1beta1.CustomResourceDefinition)) (*apiextensionsv1beta1.CustomResourceDefinition, error) {
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
crd, err := client.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{}) crd, err := client.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
if err != nil { if err != nil {
......
...@@ -445,7 +445,7 @@ func TestCRValidationOnCRDUpdate(t *testing.T) { ...@@ -445,7 +445,7 @@ func TestCRValidationOnCRDUpdate(t *testing.T) {
} }
// update the CRD to a less stricter schema // update the CRD to a less stricter schema
_, err = updateCustomResourceDefinitionWithRetry(apiExtensionClient, "noxus.mygroup.example.com", func(crd *apiextensionsv1beta1.CustomResourceDefinition) { _, err = UpdateCustomResourceDefinitionWithRetry(apiExtensionClient, "noxus.mygroup.example.com", func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
validationSchema, err := getSchemaForVersion(crd, v.Name) validationSchema, err := getSchemaForVersion(crd, v.Name)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
......
...@@ -64,7 +64,7 @@ func TestInternalVersionIsHandlerVersion(t *testing.T) { ...@@ -64,7 +64,7 @@ func TestInternalVersionIsHandlerVersion(t *testing.T) {
// update validation via update because the cache priming in CreateNewCustomResourceDefinition will fail otherwise // update validation via update because the cache priming in CreateNewCustomResourceDefinition will fail otherwise
t.Logf("Updating CRD to validate apiVersion") t.Logf("Updating CRD to validate apiVersion")
noxuDefinition, err = updateCustomResourceDefinitionWithRetry(apiExtensionClient, noxuDefinition.Name, func(crd *apiextensionsv1beta1.CustomResourceDefinition) { noxuDefinition, err = UpdateCustomResourceDefinitionWithRetry(apiExtensionClient, noxuDefinition.Name, func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{ crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{ OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
......
...@@ -164,6 +164,7 @@ func (a *Webhook) Dispatch(attr admission.Attributes) error { ...@@ -164,6 +164,7 @@ func (a *Webhook) Dispatch(attr admission.Attributes) error {
return admission.NewForbidden(attr, fmt.Errorf("not yet ready to handle request")) return admission.NewForbidden(attr, fmt.Errorf("not yet ready to handle request"))
} }
hooks := a.hookSource.Webhooks() hooks := a.hookSource.Webhooks()
// TODO: Figure out if adding one second timeout make sense here.
ctx := context.TODO() ctx := context.TODO()
var relevantHooks []*v1beta1.Webhook var relevantHooks []*v1beta1.Webhook
......
...@@ -17,13 +17,13 @@ limitations under the License. ...@@ -17,13 +17,13 @@ limitations under the License.
package apimachinery package apimachinery
import ( import (
"k8s.io/kubernetes/staging/src/k8s.io/apiextensions-apiserver/test/integration"
"time" "time"
apps "k8s.io/api/apps/v1" apps "k8s.io/api/apps/v1"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/test/integration"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment