diff --git a/go.mod b/go.mod index 89c8a2305..a931a4a1f 100644 --- a/go.mod +++ b/go.mod @@ -42,12 +42,12 @@ require ( google.golang.org/grpc v1.40.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/square/go-jose.v2 v2.2.2 - k8s.io/api v0.0.0-20220330011019-6031be5d2a4d - k8s.io/apimachinery v0.0.0-20220329130813-31e52c987dc1 - k8s.io/client-go v0.0.0-20220330011324-9cfda74c80a4 - k8s.io/component-base v0.0.0-20220330011832-6ef47acafa8e + k8s.io/api v0.0.0-20220330051021-b754a94214be + k8s.io/apimachinery v0.0.0-20220330050810-00f071187c12 + k8s.io/client-go v0.0.0-20220330051330-b1e85f6f0092 + k8s.io/component-base v0.0.0-20220330051900-ffc9c87ab39a k8s.io/klog/v2 v2.60.1 - k8s.io/kube-openapi v0.0.0-20220324211241-9f9c01d62a3a + k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.30 sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 @@ -56,8 +56,8 @@ require ( ) replace ( - k8s.io/api => k8s.io/api v0.0.0-20220330011019-6031be5d2a4d - k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20220329130813-31e52c987dc1 - k8s.io/client-go => k8s.io/client-go v0.0.0-20220330011324-9cfda74c80a4 - k8s.io/component-base => k8s.io/component-base v0.0.0-20220330011832-6ef47acafa8e + k8s.io/api => k8s.io/api v0.0.0-20220330051021-b754a94214be + k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20220330050810-00f071187c12 + k8s.io/client-go => k8s.io/client-go v0.0.0-20220330051330-b1e85f6f0092 + k8s.io/component-base => k8s.io/component-base v0.0.0-20220330051900-ffc9c87ab39a ) diff --git a/go.sum b/go.sum index 1fecc3fed..5b0b919bf 100644 --- a/go.sum +++ b/go.sum @@ -954,21 +954,21 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.0.0-20220330011019-6031be5d2a4d h1:JITAFP7p1OtAeKzWnjkjjJxn6ctWoQvwwxabOyc8v7E= -k8s.io/api v0.0.0-20220330011019-6031be5d2a4d/go.mod h1:DtY4NYpJTdEZbyZNYT6OchYvejyTwdm7DK07dMzPcII= -k8s.io/apimachinery v0.0.0-20220329130813-31e52c987dc1 h1:ESix+UawmXFu9oem7ngnHSbufLCiIQrvn9TXHB1x/w4= -k8s.io/apimachinery v0.0.0-20220329130813-31e52c987dc1/go.mod h1:WkN7hnr/sIpKTK8v3BZKqLkdqTMz00TBdMWqE0M0O7Q= -k8s.io/client-go v0.0.0-20220330011324-9cfda74c80a4 h1:/DhMBD0xkS7DvAriCuffRTIsz1G4fI4NswWFfSNbLgQ= -k8s.io/client-go v0.0.0-20220330011324-9cfda74c80a4/go.mod h1:YxULbpSLECQQvysnBfh02ucl8mDQnaxBbWzj/j4OpyI= -k8s.io/component-base v0.0.0-20220330011832-6ef47acafa8e h1:LVTPrdPXjDa/QVeuJGpWfryOq7PJ4TEzICMomzm0hNE= -k8s.io/component-base v0.0.0-20220330011832-6ef47acafa8e/go.mod h1:X2whuyKi0x6081clOWdbkh0RA29kBroU/3eEhlbWHBU= +k8s.io/api v0.0.0-20220330051021-b754a94214be h1:UnXBdsSVy3e2Y6cyf9rVmzn0wPXyoUuGIZAM43vdTj0= +k8s.io/api v0.0.0-20220330051021-b754a94214be/go.mod h1:gL+2VXubbVTH6gK18uLau2gxoDmu43bdiQmz5TTa3zw= +k8s.io/apimachinery v0.0.0-20220330050810-00f071187c12 h1:+Cp1h0ojFtOluw7Bgiju2A/LqoWzeYyaB+sY/YSflwE= +k8s.io/apimachinery v0.0.0-20220330050810-00f071187c12/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/client-go v0.0.0-20220330051330-b1e85f6f0092 h1:juuWgiTcvrIkUVtF+C5CYsVA0YK7eNA6M/uD1J4WxNU= +k8s.io/client-go v0.0.0-20220330051330-b1e85f6f0092/go.mod h1:891OlZJw6BK4fpxlZNVtaoFv0uIX9BfbxEsmMJBv1xM= +k8s.io/component-base v0.0.0-20220330051900-ffc9c87ab39a h1:5GgsJwT9dZE4ta+/a+DDXa2FB0f7ZkmwqJbFE6CHZHo= +k8s.io/component-base v0.0.0-20220330051900-ffc9c87ab39a/go.mod h1:LJBddfwf8ppsSrBEKyFor1D79VOnf+xhBp7/sE+XB/E= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.60.1 h1:VW25q3bZx9uE3vvdL6M8ezOX79vA2Aq1nEWLqNQclHc= k8s.io/klog/v2 v2.60.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220324211241-9f9c01d62a3a h1:91uLlZzSWSkvb1xuJeHwRMTMLHRHXz7eXPNuKxXJb+0= -k8s.io/kube-openapi v0.0.0-20220324211241-9f9c01d62a3a/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= +k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 h1:Gii5eqf+GmIEwGNKQYQClCayuJCe2/4fZUvF7VG99sU= +k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 760568e68..8c95cacb5 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -167,6 +167,7 @@ const ( // owner: @jefftree // kep: http://kep.k8s.io/2896 // alpha: v1.23 + // beta: v1.24 // // Enables kubernetes to publish OpenAPI v3 OpenAPIV3 featuregate.Feature = "OpenAPIV3" @@ -212,7 +213,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS APIServerTracing: {Default: false, PreRelease: featuregate.Alpha}, OpenAPIEnums: {Default: true, PreRelease: featuregate.Beta}, CustomResourceValidationExpressions: {Default: false, PreRelease: featuregate.Alpha}, - OpenAPIV3: {Default: false, PreRelease: featuregate.Alpha}, + OpenAPIV3: {Default: true, PreRelease: featuregate.Beta}, ServerSideFieldValidation: {Default: true, PreRelease: featuregate.Beta}, CronJobTimeZone: {Default: false, PreRelease: featuregate.Alpha}, } diff --git a/pkg/server/config.go b/pkg/server/config.go index 5c72800a0..f2ea85b82 100644 --- a/pkg/server/config.go +++ b/pkg/server/config.go @@ -171,6 +171,8 @@ type Config struct { Serializer runtime.NegotiatedSerializer // OpenAPIConfig will be used in generating OpenAPI spec. This is nil by default. Use DefaultOpenAPIConfig for "working" defaults. OpenAPIConfig *openapicommon.Config + // OpenAPIV3Config will be used in generating OpenAPI V3 spec. This is nil by default. Use DefaultOpenAPIV3Config for "working" defaults. + OpenAPIV3Config *openapicommon.Config // SkipOpenAPIInstallation avoids installing the OpenAPI handler if set to true. SkipOpenAPIInstallation bool @@ -382,6 +384,7 @@ func NewRecommendedConfig(codecs serializer.CodecFactory) *RecommendedConfig { } } +// DefaultOpenAPIConfig provides the default OpenAPIConfig used to build the OpenAPI V2 spec func DefaultOpenAPIConfig(getDefinitions openapicommon.GetOpenAPIDefinitions, defNamer *apiopenapi.DefinitionNamer) *openapicommon.Config { return &openapicommon.Config{ ProtocolList: []string{"https"}, @@ -402,6 +405,17 @@ func DefaultOpenAPIConfig(getDefinitions openapicommon.GetOpenAPIDefinitions, de } } +// DefaultOpenAPIV3Config provides the default OpenAPIV3Config used to build the OpenAPI V3 spec +func DefaultOpenAPIV3Config(getDefinitions openapicommon.GetOpenAPIDefinitions, defNamer *apiopenapi.DefinitionNamer) *openapicommon.Config { + defaultConfig := DefaultOpenAPIConfig(getDefinitions, defNamer) + defaultConfig.Definitions = getDefinitions(func(name string) spec.Ref { + defName, _ := defaultConfig.GetDefinitionName(name) + return spec.MustCreateRef("#/components/schemas/" + openapicommon.EscapeJsonPointer(defName)) + }) + + return defaultConfig +} + func (c *AuthenticationInfo) ApplyClientCert(clientCA dynamiccertificates.CAContentProvider, servingInfo *SecureServingInfo) error { if servingInfo == nil { return nil @@ -475,6 +489,45 @@ func (c *Config) AddPostStartHookOrDie(name string, hook PostStartHookFunc) { } } +func completeOpenAPI(config *openapicommon.Config, version *version.Info) { + if config == nil { + return + } + if config.SecurityDefinitions != nil { + // Setup OpenAPI security: all APIs will have the same authentication for now. + config.DefaultSecurity = []map[string][]string{} + keys := []string{} + for k := range *config.SecurityDefinitions { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + config.DefaultSecurity = append(config.DefaultSecurity, map[string][]string{k: {}}) + } + if config.CommonResponses == nil { + config.CommonResponses = map[int]spec.Response{} + } + if _, exists := config.CommonResponses[http.StatusUnauthorized]; !exists { + config.CommonResponses[http.StatusUnauthorized] = spec.Response{ + ResponseProps: spec.ResponseProps{ + Description: "Unauthorized", + }, + } + } + } + // make sure we populate info, and info.version, if not manually set + if config.Info == nil { + config.Info = &spec.Info{} + } + if config.Info.Version == "" { + if version != nil { + config.Info.Version = strings.Split(version.String(), "-")[0] + } else { + config.Info.Version = "unversioned" + } + } +} + // Complete fills in any fields not set that are required to have valid data and can be derived // from other fields. If you're going to `ApplyOptions`, do that first. It's mutating the receiver. func (c *Config) Complete(informers informers.SharedInformerFactory) CompletedConfig { @@ -494,42 +547,9 @@ func (c *Config) Complete(informers informers.SharedInformerFactory) CompletedCo c.ExternalAddress = net.JoinHostPort(c.ExternalAddress, strconv.Itoa(port)) } - if c.OpenAPIConfig != nil { - if c.OpenAPIConfig.SecurityDefinitions != nil { - // Setup OpenAPI security: all APIs will have the same authentication for now. - c.OpenAPIConfig.DefaultSecurity = []map[string][]string{} - keys := []string{} - for k := range *c.OpenAPIConfig.SecurityDefinitions { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - c.OpenAPIConfig.DefaultSecurity = append(c.OpenAPIConfig.DefaultSecurity, map[string][]string{k: {}}) - } - if c.OpenAPIConfig.CommonResponses == nil { - c.OpenAPIConfig.CommonResponses = map[int]spec.Response{} - } - if _, exists := c.OpenAPIConfig.CommonResponses[http.StatusUnauthorized]; !exists { - c.OpenAPIConfig.CommonResponses[http.StatusUnauthorized] = spec.Response{ - ResponseProps: spec.ResponseProps{ - Description: "Unauthorized", - }, - } - } - } + completeOpenAPI(c.OpenAPIConfig, c.Version) + completeOpenAPI(c.OpenAPIV3Config, c.Version) - // make sure we populate info, and info.version, if not manually set - if c.OpenAPIConfig.Info == nil { - c.OpenAPIConfig.Info = &spec.Info{} - } - if c.OpenAPIConfig.Info.Version == "" { - if c.Version != nil { - c.OpenAPIConfig.Info.Version = strings.Split(c.Version.String(), "-")[0] - } else { - c.OpenAPIConfig.Info.Version = "unversioned" - } - } - } if c.DiscoveryAddresses == nil { c.DiscoveryAddresses = discovery.DefaultAddresses{DefaultAddress: c.ExternalAddress} } @@ -606,6 +626,7 @@ func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*G ExternalAddress: c.ExternalAddress, openAPIConfig: c.OpenAPIConfig, + openAPIV3Config: c.OpenAPIV3Config, skipOpenAPIInstallation: c.SkipOpenAPIInstallation, postStartHooks: map[string]postStartHookEntry{}, diff --git a/pkg/server/genericapiserver.go b/pkg/server/genericapiserver.go index 7e410b8e5..b84357928 100644 --- a/pkg/server/genericapiserver.go +++ b/pkg/server/genericapiserver.go @@ -135,6 +135,9 @@ type GenericAPIServer struct { // Enable swagger and/or OpenAPI if these configs are non-nil. openAPIConfig *openapicommon.Config + // Enable swagger and/or OpenAPI V3 if these configs are non-nil. + openAPIV3Config *openapicommon.Config + // SkipOpenAPIInstallation indicates not to install the OpenAPI handler // during PrepareRun. // Set this to true when the specific API Server has its own OpenAPI handler @@ -351,9 +354,12 @@ func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer { s.OpenAPIVersionedService, s.StaticOpenAPISpec = routes.OpenAPI{ Config: s.openAPIConfig, }.InstallV2(s.Handler.GoRestfulContainer, s.Handler.NonGoRestfulMux) + } + + if s.openAPIV3Config != nil && !s.skipOpenAPIInstallation { if utilfeature.DefaultFeatureGate.Enabled(features.OpenAPIV3) { s.OpenAPIV3VersionedService = routes.OpenAPI{ - Config: s.openAPIConfig, + Config: s.openAPIV3Config, }.InstallV3(s.Handler.GoRestfulContainer, s.Handler.NonGoRestfulMux) } }