2014-11-02 15:52:31 -05:00
/ *
2016-06-02 20:25:58 -04:00
Copyright 2014 The Kubernetes Authors .
2014-11-02 15:52:31 -05:00
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 .
* /
2020-09-02 12:08:35 -04:00
package controlplane
2014-11-02 15:52:31 -05:00
import (
2018-05-18 18:14:37 -04:00
"context"
2015-10-09 01:18:16 -04:00
"crypto/tls"
2015-08-20 01:08:26 -04:00
"encoding/json"
2024-05-31 23:29:48 -04:00
"fmt"
2023-05-02 09:08:18 -04:00
"io"
2015-09-03 13:35:04 -04:00
"net"
2015-08-19 14:02:01 -04:00
"net/http"
"net/http/httptest"
2015-08-20 01:08:26 -04:00
"reflect"
2017-09-05 21:51:04 -04:00
"strings"
2014-11-02 15:52:31 -05:00
"testing"
2023-06-27 06:07:05 -04:00
"github.com/stretchr/testify/assert"
2024-05-01 10:10:32 -04:00
autoscalingrest "k8s.io/kubernetes/pkg/registry/autoscaling/rest"
resourcerest "k8s.io/kubernetes/pkg/registry/resource/rest"
2023-06-27 06:07:05 -04:00
2022-02-07 13:32:01 -05:00
batchapiv1beta1 "k8s.io/api/batch/v1beta1"
2017-07-18 10:52:42 -04:00
certificatesapiv1beta1 "k8s.io/api/certificates/v1beta1"
2022-02-07 13:32:01 -05:00
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
eventsv1beta1 "k8s.io/api/events/v1beta1"
nodev1beta1 "k8s.io/api/node/v1beta1"
policyapiv1beta1 "k8s.io/api/policy/v1beta1"
storageapiv1beta1 "k8s.io/api/storage/v1beta1"
2023-03-03 13:49:50 -05:00
extensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver"
2017-01-11 09:09:48 -05:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2019-01-14 22:31:25 -05:00
"k8s.io/apimachinery/pkg/runtime/schema"
2017-01-11 09:09:48 -05:00
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets"
2017-01-16 06:05:15 -05:00
"k8s.io/apimachinery/pkg/version"
2024-08-30 03:47:15 -04:00
"k8s.io/apiserver/pkg/authorization/authorizer"
2019-01-14 22:31:25 -05:00
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
2023-03-03 13:49:50 -05:00
openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
2017-02-02 04:25:56 -05:00
genericapiserver "k8s.io/apiserver/pkg/server"
2017-02-11 06:14:34 -05:00
"k8s.io/apiserver/pkg/server/options"
2019-01-14 22:31:25 -05:00
"k8s.io/apiserver/pkg/server/resourceconfig"
2017-02-11 06:26:43 -05:00
serverstorage "k8s.io/apiserver/pkg/server/storage"
2019-07-01 04:38:19 -04:00
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
2024-12-20 02:03:03 -05:00
"k8s.io/apiserver/pkg/util/compatibility"
2023-03-03 13:49:50 -05:00
"k8s.io/apiserver/pkg/util/openapi"
2019-01-14 22:31:25 -05:00
"k8s.io/client-go/discovery"
2017-05-01 09:07:46 -04:00
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
2017-01-19 13:27:59 -05:00
restclient "k8s.io/client-go/rest"
2024-10-24 20:49:33 -04:00
utilversion "k8s.io/component-base/version"
2023-03-03 13:49:50 -05:00
aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
2023-06-27 06:07:05 -04:00
netutils "k8s.io/utils/net"
2017-10-16 07:41:50 -04:00
"k8s.io/kubernetes/pkg/api/legacyscheme"
2023-06-27 06:07:05 -04:00
controlplaneapiserver "k8s.io/kubernetes/pkg/controlplane/apiserver"
2020-09-02 13:47:23 -04:00
"k8s.io/kubernetes/pkg/controlplane/reconcilers"
"k8s.io/kubernetes/pkg/controlplane/storageversionhashdata"
2023-03-03 13:49:50 -05:00
generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
2022-07-01 03:20:56 -04:00
"k8s.io/kubernetes/pkg/kubeapiserver"
2020-09-02 13:49:40 -04:00
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
2024-05-01 10:10:32 -04:00
appsrest "k8s.io/kubernetes/pkg/registry/apps/rest"
batchrest "k8s.io/kubernetes/pkg/registry/batch/rest"
2017-07-18 10:52:42 -04:00
certificatesrest "k8s.io/kubernetes/pkg/registry/certificates/rest"
corerest "k8s.io/kubernetes/pkg/registry/core/rest"
2024-05-01 10:10:32 -04:00
discoveryrest "k8s.io/kubernetes/pkg/registry/discovery/rest"
networkingrest "k8s.io/kubernetes/pkg/registry/networking/rest"
noderest "k8s.io/kubernetes/pkg/registry/node/rest"
policyrest "k8s.io/kubernetes/pkg/registry/policy/rest"
2017-07-18 10:52:42 -04:00
"k8s.io/kubernetes/pkg/registry/registrytest"
2024-05-01 10:10:32 -04:00
schedulingrest "k8s.io/kubernetes/pkg/registry/scheduling/rest"
storagerest "k8s.io/kubernetes/pkg/registry/storage/rest"
2014-11-02 15:52:31 -05:00
)
2019-11-05 22:43:20 -05:00
// setUp is a convenience function for setting up for (most) tests.
2019-07-01 04:38:19 -04:00
func setUp ( t * testing . T ) ( * etcd3testing . EtcdTestServer , Config , * assert . Assertions ) {
server , storageConfig := etcd3testing . NewUnsecuredEtcd3TestClientServer ( t )
2015-11-17 08:35:00 -05:00
2016-09-26 07:51:04 -04:00
config := & Config {
2023-06-27 06:07:05 -04:00
ControlPlane : controlplaneapiserver . Config {
Generic : genericapiserver . NewConfig ( legacyscheme . Codecs ) ,
Extra : controlplaneapiserver . Extra {
APIResourceConfigSource : DefaultAPIResourceConfigSource ( ) ,
} ,
} ,
Extra : Extra {
APIServerServicePort : 443 ,
MasterCount : 1 ,
EndpointReconcilerType : reconcilers . MasterCountReconcilerType ,
ServiceIPRange : net . IPNet { IP : netutils . ParseIPSloppy ( "10.0.0.0" ) , Mask : net . CIDRMask ( 24 , 32 ) } ,
2017-09-06 11:46:05 -04:00
} ,
2015-11-16 16:46:00 -05:00
}
2016-03-16 10:17:04 -04:00
2024-12-20 02:03:03 -05:00
config . ControlPlane . Generic . EffectiveVersion = compatibility . DefaultKubeEffectiveVersionForTest ( )
2022-07-01 03:20:56 -04:00
storageFactoryConfig := kubeapiserver . NewStorageFactoryConfig ( )
2024-01-19 19:07:00 -05:00
storageFactoryConfig . DefaultResourceEncoding . SetEffectiveVersion ( config . ControlPlane . Generic . EffectiveVersion )
2023-06-27 06:07:05 -04:00
storageConfig . StorageObjectCountTracker = config . ControlPlane . Generic . StorageObjectCountTracker
2022-07-01 03:20:56 -04:00
resourceEncoding := resourceconfig . MergeResourceEncodingConfigs ( storageFactoryConfig . DefaultResourceEncoding , storageFactoryConfig . ResourceEncodingOverrides )
storageFactory := serverstorage . NewDefaultStorageFactory ( * storageConfig , "application/vnd.kubernetes.protobuf" , storageFactoryConfig . Serializer , resourceEncoding , DefaultAPIResourceConfigSource ( ) , nil )
2018-02-26 10:46:47 -05:00
etcdOptions := options . NewEtcdOptions ( storageConfig )
// unit tests don't need watch cache and it leaks lots of goroutines with etcd testing functions during unit tests
etcdOptions . EnableWatchCache = false
2023-06-27 06:07:05 -04:00
err := etcdOptions . ApplyWithStorageFactoryTo ( storageFactory , config . ControlPlane . Generic )
2017-02-11 06:14:34 -05:00
if err != nil {
t . Fatal ( err )
}
2023-06-27 06:07:05 -04:00
config . ControlPlane . Generic . Authorization . Authorizer = authorizerfactory . NewAlwaysAllowAuthorizer ( )
config . ControlPlane . StorageFactory = storageFactory
config . ControlPlane . Generic . LoopbackClientConfig = & restclient . Config { APIPath : "/api" , ContentConfig : restclient . ContentConfig { NegotiatedSerializer : legacyscheme . Codecs } }
config . ControlPlane . Generic . PublicAddress = netutils . ParseIPSloppy ( "192.168.10.4" )
config . ControlPlane . Generic . LegacyAPIGroupPrefixes = sets . NewString ( "/api" )
config . Extra . KubeletClientConfig = kubeletclient . KubeletClientConfig { Port : 10250 }
config . ControlPlane . ProxyTransport = utilnet . SetTransportDefaults ( & http . Transport {
2018-05-18 18:14:37 -04:00
DialContext : func ( ctx context . Context , network , addr string ) ( net . Conn , error ) { return nil , nil } ,
2016-10-17 15:16:58 -04:00
TLSClientConfig : & tls . Config { } ,
} )
2015-10-09 01:18:16 -04:00
2018-07-06 13:20:45 -04:00
// set fake SecureServingInfo because the listener port is needed for the kubernetes service
2023-06-27 06:07:05 -04:00
config . ControlPlane . Generic . SecureServing = & genericapiserver . SecureServingInfo { Listener : fakeLocalhost443Listener { } }
2018-07-06 13:20:45 -04:00
2023-03-03 13:49:50 -05:00
getOpenAPIDefinitions := openapi . GetOpenAPIDefinitionsWithoutDisabledFeatures ( generatedopenapi . GetOpenAPIDefinitions )
namer := openapinamer . NewDefinitionNamer ( legacyscheme . Scheme , extensionsapiserver . Scheme , aggregatorscheme . Scheme )
2023-06-27 06:07:05 -04:00
config . ControlPlane . Generic . OpenAPIV3Config = genericapiserver . DefaultOpenAPIV3Config ( getOpenAPIDefinitions , namer )
2023-03-03 13:49:50 -05:00
2023-06-27 06:07:05 -04:00
clientset , err := kubernetes . NewForConfig ( config . ControlPlane . Generic . LoopbackClientConfig )
2017-05-01 09:07:46 -04:00
if err != nil {
t . Fatalf ( "unable to create client set due to %v" , err )
}
2023-06-27 06:07:05 -04:00
config . ControlPlane . VersionedInformers = informers . NewSharedInformerFactory ( clientset , config . ControlPlane . Generic . LoopbackClientConfig . Timeout )
2017-05-01 09:07:46 -04:00
2018-08-02 00:11:58 -04:00
return server , * config , assert . New ( t )
2016-04-07 18:34:40 -04:00
}
2018-07-06 13:20:45 -04:00
type fakeLocalhost443Listener struct { }
func ( fakeLocalhost443Listener ) Accept ( ) ( net . Conn , error ) {
return nil , nil
}
func ( fakeLocalhost443Listener ) Close ( ) error {
return nil
}
func ( fakeLocalhost443Listener ) Addr ( ) net . Addr {
return & net . TCPAddr {
IP : net . IPv4 ( 127 , 0 , 0 , 1 ) ,
Port : 443 ,
}
}
2017-07-18 10:52:42 -04:00
// TestLegacyRestStorageStrategies ensures that all Storage objects which are using the generic registry Store have
// their various strategies properly wired up. This surfaced as a bug where strategies defined Export functions, but
// they were never used outside of unit tests because the export strategies were not assigned inside the Store.
func TestLegacyRestStorageStrategies ( t * testing . T ) {
2020-09-24 16:30:08 -04:00
_ , etcdserver , apiserverCfg , _ := newInstance ( t )
2017-08-15 23:18:06 -04:00
defer etcdserver . Terminate ( t )
2023-07-04 06:58:21 -04:00
storageProvider , err := corerest . New ( corerest . Config {
2024-05-01 10:10:32 -04:00
GenericConfig : * apiserverCfg . ControlPlane . NewCoreGenericConfig ( ) ,
2023-07-04 13:04:20 -04:00
Proxy : corerest . ProxyConfig {
2023-06-27 06:07:05 -04:00
Transport : apiserverCfg . ControlPlane . Extra . ProxyTransport ,
KubeletClientConfig : apiserverCfg . Extra . KubeletClientConfig ,
2023-07-04 13:04:20 -04:00
} ,
Services : corerest . ServicesConfig {
2023-06-27 06:07:05 -04:00
ClusterIPRange : apiserverCfg . Extra . ServiceIPRange ,
NodePortRange : apiserverCfg . Extra . ServiceNodePortRange ,
2023-07-04 13:04:20 -04:00
} ,
2025-08-27 16:06:02 -04:00
} , nil )
2023-07-04 06:58:21 -04:00
if err != nil {
t . Fatalf ( "unexpected error from REST storage: %v" , err )
2017-07-18 10:52:42 -04:00
}
2023-06-27 06:07:05 -04:00
apiGroupInfo , err := storageProvider . NewRESTStorage ( serverstorage . NewResourceConfig ( ) , apiserverCfg . ControlPlane . Generic . RESTOptionsGetter )
2017-07-18 10:52:42 -04:00
if err != nil {
t . Errorf ( "failed to create legacy REST storage: %v" , err )
}
2021-01-22 09:21:56 -05:00
strategyErrors := registrytest . ValidateStorageStrategies ( apiGroupInfo . VersionedResourcesStorageMap [ "v1" ] )
2017-07-18 10:52:42 -04:00
for _ , err := range strategyErrors {
t . Error ( err )
}
}
func TestCertificatesRestStorageStrategies ( t * testing . T ) {
2020-09-24 16:30:08 -04:00
_ , etcdserver , apiserverCfg , _ := newInstance ( t )
2017-08-15 23:18:06 -04:00
defer etcdserver . Terminate ( t )
2024-08-30 03:47:15 -04:00
certStorageProvider := certificatesrest . RESTStorageProvider {
Authorizer : & fakeAuthorizer {
decision : authorizer . DecisionAllow ,
} ,
}
2023-06-27 06:07:05 -04:00
apiGroupInfo , err := certStorageProvider . NewRESTStorage ( apiserverCfg . ControlPlane . APIResourceConfigSource , apiserverCfg . ControlPlane . Generic . RESTOptionsGetter )
2019-08-12 16:55:33 -04:00
if err != nil {
t . Fatalf ( "unexpected error from REST storage: %v" , err )
}
2017-07-18 10:52:42 -04:00
strategyErrors := registrytest . ValidateStorageStrategies (
2021-01-22 09:21:56 -05:00
apiGroupInfo . VersionedResourcesStorageMap [ certificatesapiv1beta1 . SchemeGroupVersion . Version ] )
2017-07-18 10:52:42 -04:00
for _ , err := range strategyErrors {
t . Error ( err )
}
}
2024-08-30 03:47:15 -04:00
type fakeAuthorizer struct {
decision authorizer . Decision
reason string
err error
}
func ( f * fakeAuthorizer ) Authorize ( ctx context . Context , a authorizer . Attributes ) ( authorizer . Decision , string , error ) {
return f . decision , f . reason , f . err
}
2024-05-01 10:10:32 -04:00
func newInstance ( t * testing . T ) ( * Instance , * etcd3testing . EtcdTestServer , CompletedConfig , * assert . Assertions ) {
2018-08-02 00:11:58 -04:00
etcdserver , config , assert := setUp ( t )
2016-04-07 18:34:40 -04:00
2024-05-01 10:10:32 -04:00
completed := config . Complete ( )
apiserver , err := completed . New ( genericapiserver . NewEmptyDelegate ( ) )
2016-04-07 18:34:40 -04:00
if err != nil {
t . Fatalf ( "Error in bringing up the master: %v" , err )
}
2024-05-01 10:10:32 -04:00
return apiserver , etcdserver , completed , assert
2016-04-07 18:34:40 -04:00
}
2016-09-06 07:20:36 -04:00
// TestVersion tests /version
func TestVersion ( t * testing . T ) {
2020-09-24 16:30:08 -04:00
s , etcdserver , _ , _ := newInstance ( t )
2016-09-06 07:20:36 -04:00
defer etcdserver . Terminate ( t )
req , _ := http . NewRequest ( "GET" , "/version" , nil )
resp := httptest . NewRecorder ( )
2024-04-26 07:09:00 -04:00
s . ControlPlane . GenericAPIServer . Handler . ServeHTTP ( resp , req )
2016-09-06 07:20:36 -04:00
if resp . Code != 200 {
t . Fatalf ( "expected http 200, got: %d" , resp . Code )
}
var info version . Info
err := json . NewDecoder ( resp . Body ) . Decode ( & info )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
2024-10-24 20:49:33 -04:00
expectedInfo := utilversion . Get ( )
2024-12-20 02:03:03 -05:00
kubeVersion := compatibility . DefaultKubeEffectiveVersionForTest ( ) . BinaryVersion ( )
2025-02-06 19:11:12 -05:00
emulationVersion := compatibility . DefaultKubeEffectiveVersionForTest ( ) . EmulationVersion ( )
minCompatibilityVersion := compatibility . DefaultKubeEffectiveVersionForTest ( ) . MinCompatibilityVersion ( )
2024-05-31 23:29:48 -04:00
expectedInfo . Major = fmt . Sprintf ( "%d" , kubeVersion . Major ( ) )
expectedInfo . Minor = fmt . Sprintf ( "%d" , kubeVersion . Minor ( ) )
2025-02-06 19:11:12 -05:00
expectedInfo . EmulationMajor = fmt . Sprintf ( "%d" , emulationVersion . Major ( ) )
expectedInfo . EmulationMinor = fmt . Sprintf ( "%d" , emulationVersion . Minor ( ) )
expectedInfo . MinCompatibilityMajor = fmt . Sprintf ( "%d" , minCompatibilityVersion . Major ( ) )
expectedInfo . MinCompatibilityMinor = fmt . Sprintf ( "%d" , minCompatibilityVersion . Minor ( ) )
2016-09-06 07:20:36 -04:00
2024-05-31 23:29:48 -04:00
if ! reflect . DeepEqual ( expectedInfo , info ) {
t . Errorf ( "Expected %#v, Got %#v" , expectedInfo , info )
2016-09-06 07:20:36 -04:00
}
}
2016-09-29 13:01:49 -04:00
func decodeResponse ( resp * http . Response , obj interface { } ) error {
defer resp . Body . Close ( )
2023-05-02 09:08:18 -04:00
data , err := io . ReadAll ( resp . Body )
2016-09-29 13:01:49 -04:00
if err != nil {
return err
}
if err := json . Unmarshal ( data , obj ) ; err != nil {
return err
}
return nil
}
2016-02-11 20:03:43 -05:00
// Because we need to be backwards compatible with release 1.1, at endpoints
// that exist in release 1.1, the responses should have empty APIVersion.
func TestAPIVersionOfDiscoveryEndpoints ( t * testing . T ) {
2020-09-24 16:30:08 -04:00
apiserver , etcdserver , _ , assert := newInstance ( t )
2016-02-11 20:03:43 -05:00
defer etcdserver . Terminate ( t )
2024-04-26 07:09:00 -04:00
server := httptest . NewServer ( apiserver . ControlPlane . GenericAPIServer . Handler . GoRestfulContainer . ServeMux )
2016-02-11 20:03:43 -05:00
// /api exists in release-1.1
resp , err := http . Get ( server . URL + "/api" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
2016-12-03 13:57:26 -05:00
apiVersions := metav1 . APIVersions { }
2016-02-11 20:03:43 -05:00
assert . NoError ( decodeResponse ( resp , & apiVersions ) )
2024-09-27 01:56:31 -04:00
assert . Equal ( "" , apiVersions . APIVersion )
2016-02-11 20:03:43 -05:00
// /api/v1 exists in release-1.1
resp , err = http . Get ( server . URL + "/api/v1" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
2016-12-03 13:57:26 -05:00
resourceList := metav1 . APIResourceList { }
2016-02-11 20:03:43 -05:00
assert . NoError ( decodeResponse ( resp , & resourceList ) )
2024-09-27 01:56:31 -04:00
assert . Equal ( "" , resourceList . APIVersion )
2016-02-11 20:03:43 -05:00
// /apis exists in release-1.1
resp , err = http . Get ( server . URL + "/apis" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
2016-12-03 13:57:26 -05:00
groupList := metav1 . APIGroupList { }
2016-02-11 20:03:43 -05:00
assert . NoError ( decodeResponse ( resp , & groupList ) )
2024-09-27 01:56:31 -04:00
assert . Equal ( "" , groupList . APIVersion )
2016-02-11 20:03:43 -05:00
// /apis/autoscaling doesn't exist in release-1.1, so the APIVersion field
// should be non-empty in the results returned by the server.
resp , err = http . Get ( server . URL + "/apis/autoscaling" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
2021-03-25 13:10:07 -04:00
group := metav1 . APIGroup { }
2016-02-11 20:03:43 -05:00
assert . NoError ( decodeResponse ( resp , & group ) )
2024-09-27 01:56:31 -04:00
assert . Equal ( "v1" , group . APIVersion )
2016-02-11 20:03:43 -05:00
// apis/autoscaling/v1 doesn't exist in release-1.1, so the APIVersion field
// should be non-empty in the results returned by the server.
resp , err = http . Get ( server . URL + "/apis/autoscaling/v1" )
if err != nil {
t . Errorf ( "unexpected error: %v" , err )
}
2016-12-03 13:57:26 -05:00
resourceList = metav1 . APIResourceList { }
2016-02-11 20:03:43 -05:00
assert . NoError ( decodeResponse ( resp , & resourceList ) )
2024-09-27 01:56:31 -04:00
assert . Equal ( "v1" , resourceList . APIVersion )
2016-02-11 20:03:43 -05:00
}
2017-09-05 21:51:04 -04:00
2019-01-14 22:31:25 -05:00
// This test doesn't cover the apiregistration and apiextensions group, as they are installed by other apiservers.
func TestStorageVersionHashes ( t * testing . T ) {
2020-09-24 16:30:08 -04:00
apiserver , etcdserver , _ , _ := newInstance ( t )
2019-01-14 22:31:25 -05:00
defer etcdserver . Terminate ( t )
2024-04-26 07:09:00 -04:00
server := httptest . NewServer ( apiserver . ControlPlane . GenericAPIServer . Handler . GoRestfulContainer . ServeMux )
2019-01-14 22:31:25 -05:00
c := & restclient . Config {
Host : server . URL ,
APIPath : "/api" ,
ContentConfig : restclient . ContentConfig { NegotiatedSerializer : legacyscheme . Codecs } ,
}
2023-01-09 12:51:19 -05:00
discover := discovery . NewDiscoveryClientForConfigOrDie ( c ) . WithLegacy ( )
2021-12-22 03:14:09 -05:00
_ , all , err := discover . ServerGroupsAndResources ( )
2019-01-14 22:31:25 -05:00
if err != nil {
t . Error ( err )
}
var count int
2021-03-25 13:10:07 -04:00
apiResources := sets . NewString ( )
2019-01-14 22:31:25 -05:00
for _ , g := range all {
for _ , r := range g . APIResources {
2021-03-25 13:10:07 -04:00
apiResources . Insert ( g . GroupVersion + "/" + r . Name )
2019-01-14 22:31:25 -05:00
if strings . Contains ( r . Name , "/" ) ||
storageversionhashdata . NoStorageVersionHash . Has ( g . GroupVersion + "/" + r . Name ) {
if r . StorageVersionHash != "" {
t . Errorf ( "expect resource %s/%s to have empty storageVersionHash, got hash %q" , g . GroupVersion , r . Name , r . StorageVersionHash )
}
continue
}
if r . StorageVersionHash == "" {
t . Errorf ( "expect the storageVersionHash of %s/%s to exist" , g . GroupVersion , r . Name )
continue
}
// Uncomment the following line if you want to update storageversionhash/data.go
// fmt.Printf("\"%s/%s\": \"%s\",\n", g.GroupVersion, r.Name, r.StorageVersionHash)
expected := storageversionhashdata . GVRToStorageVersionHash [ g . GroupVersion + "/" + r . Name ]
if r . StorageVersionHash != expected {
t . Errorf ( "expect the storageVersionHash of %s/%s to be %q, got %q" , g . GroupVersion , r . Name , expected , r . StorageVersionHash )
}
count ++
}
}
if count != len ( storageversionhashdata . GVRToStorageVersionHash ) {
2021-03-25 13:10:07 -04:00
knownResources := sets . StringKeySet ( storageversionhashdata . GVRToStorageVersionHash )
t . Errorf ( "please remove the redundant entries from GVRToStorageVersionHash: %v" , knownResources . Difference ( apiResources ) . List ( ) )
2019-01-14 22:31:25 -05:00
}
}
2017-09-05 21:51:04 -04:00
func TestNoAlphaVersionsEnabledByDefault ( t * testing . T ) {
config := DefaultAPIResourceConfigSource ( )
2018-01-27 02:18:25 -05:00
for gv , enable := range config . GroupVersionConfigs {
if enable && strings . Contains ( gv . Version , "alpha" ) {
2017-09-05 21:51:04 -04:00
t . Errorf ( "Alpha API version %s enabled by default" , gv . String ( ) )
}
}
2022-09-12 13:09:41 -04:00
for gvr , enabled := range config . ResourceConfigs {
if ! strings . Contains ( gvr . Version , "alpha" ) || ! enabled {
continue
}
// we have enabled an alpha api by resource {g,v,r}, we also expect the
// alpha api by version {g,v} to be disabled. This is so a programmer
// remembers to add the new alpha version to alphaAPIGroupVersionsDisabledByDefault.
gr := gvr . GroupVersion ( )
if enabled , found := config . GroupVersionConfigs [ gr ] ; ! found || enabled {
t . Errorf ( "Alpha API version %q should be disabled by default" , gr . String ( ) )
}
}
2017-09-05 21:51:04 -04:00
}
2022-02-07 13:32:01 -05:00
func TestNoBetaVersionsEnabledByDefault ( t * testing . T ) {
config := DefaultAPIResourceConfigSource ( )
for gv , enable := range config . GroupVersionConfigs {
if enable && strings . Contains ( gv . Version , "beta" ) {
t . Errorf ( "Beta API version %s enabled by default" , gv . String ( ) )
}
}
2022-09-12 13:09:41 -04:00
for gvr , enabled := range config . ResourceConfigs {
if ! strings . Contains ( gvr . Version , "beta" ) || ! enabled {
continue
}
// we have enabled a beta api by resource {g,v,r}, we also expect the
// beta api by version {g,v} to be disabled. This is so a programmer
// remembers to add the new beta version to betaAPIGroupVersionsDisabledByDefault.
gr := gvr . GroupVersion ( )
if enabled , found := config . GroupVersionConfigs [ gr ] ; ! found || enabled {
t . Errorf ( "Beta API version %q should be disabled by default" , gr . String ( ) )
}
}
}
func TestDefaultVars ( t * testing . T ) {
// stableAPIGroupVersionsEnabledByDefault should not contain beta or alpha
for i := range stableAPIGroupVersionsEnabledByDefault {
gv := stableAPIGroupVersionsEnabledByDefault [ i ]
if strings . Contains ( gv . Version , "beta" ) || strings . Contains ( gv . Version , "alpha" ) {
t . Errorf ( "stableAPIGroupVersionsEnabledByDefault should contain stable version, but found: %q" , gv . String ( ) )
}
}
// betaAPIGroupVersionsDisabledByDefault should contain only beta version
for i := range betaAPIGroupVersionsDisabledByDefault {
gv := betaAPIGroupVersionsDisabledByDefault [ i ]
if ! strings . Contains ( gv . Version , "beta" ) {
t . Errorf ( "betaAPIGroupVersionsDisabledByDefault should contain beta version, but found: %q" , gv . String ( ) )
}
}
// alphaAPIGroupVersionsDisabledByDefault should contain only alpha version
for i := range alphaAPIGroupVersionsDisabledByDefault {
gv := alphaAPIGroupVersionsDisabledByDefault [ i ]
if ! strings . Contains ( gv . Version , "alpha" ) {
t . Errorf ( "alphaAPIGroupVersionsDisabledByDefault should contain alpha version, but found: %q" , gv . String ( ) )
}
}
2022-02-07 13:32:01 -05:00
}
func TestNewBetaResourcesEnabledByDefault ( t * testing . T ) {
// legacyEnabledBetaResources is nearly a duplication from elsewhere. This is intentional. These types already have
// GA equivalents available and should therefore never have a beta enabled by default again.
legacyEnabledBetaResources := map [ schema . GroupVersionResource ] bool {
2025-12-05 05:29:17 -05:00
batchapiv1beta1 . SchemeGroupVersion . WithResource ( "cronjobs" ) : true ,
discoveryv1beta1 . SchemeGroupVersion . WithResource ( "endpointslices" ) : true ,
eventsv1beta1 . SchemeGroupVersion . WithResource ( "events" ) : true ,
nodev1beta1 . SchemeGroupVersion . WithResource ( "runtimeclasses" ) : true ,
policyapiv1beta1 . SchemeGroupVersion . WithResource ( "poddisruptionbudgets" ) : true ,
policyapiv1beta1 . SchemeGroupVersion . WithResource ( "podsecuritypolicies" ) : true ,
storageapiv1beta1 . SchemeGroupVersion . WithResource ( "csinodes" ) : true ,
2022-02-07 13:32:01 -05:00
}
config := DefaultAPIResourceConfigSource ( )
for gvr , enable := range config . ResourceConfigs {
if ! strings . Contains ( gvr . Version , "beta" ) {
continue // only check beta things
}
if ! enable {
continue // only check things that are enabled
}
if legacyEnabledBetaResources [ gvr ] {
continue // this is a legacy beta resource
}
t . Errorf ( "no new beta resources can be enabled by default, see https://github.com/kubernetes/enhancements/blob/0ad0fc8269165ca300d05ca51c7ce190a79976a5/keps/sig-architecture/3136-beta-apis-off-by-default/README.md: %v" , gvr )
}
}
2024-05-01 10:10:32 -04:00
// TestGenericStorageProviders is a smoke test that ensures that the kube
// storage providers and the generic storage providers don't unexpectedly
// divert, i.e. the later is an equally ordered subset.
func TestGenericStorageProviders ( t * testing . T ) {
_ , config , _ := setUp ( t )
completed := config . Complete ( )
// create kube storage providers
client , err := kubernetes . NewForConfig ( config . ControlPlane . Generic . LoopbackClientConfig )
if err != nil {
t . Fatal ( err )
}
2024-12-30 18:56:45 -05:00
kube , err := completed . StorageProviders ( client )
2024-05-01 10:10:32 -04:00
if err != nil {
t . Fatal ( err )
}
// create generic storage providers. These should be an equally ordered subset
generic , err := completed . ControlPlane . GenericStorageProviders ( client . Discovery ( ) )
if err != nil {
t . Fatal ( err )
}
g := 0 // generic index
for k := range kube {
kt := reflect . TypeOf ( kube [ k ] )
var gt reflect . Type
if g < len ( generic ) {
gt = reflect . TypeOf ( generic [ g ] )
}
// special case: we identify full core and generic core
if kt . Kind ( ) == reflect . Ptr && kt . Elem ( ) . PkgPath ( ) == reflect . TypeOf ( corerest . Config { } ) . PkgPath ( ) {
kt = reflect . TypeOf ( & corerest . GenericConfig { } )
}
if kt == gt {
g ++
continue
}
switch kube [ k ] . ( type ) {
case autoscalingrest . RESTStorageProvider ,
batchrest . RESTStorageProvider ,
discoveryrest . StorageProvider ,
networkingrest . RESTStorageProvider ,
noderest . RESTStorageProvider ,
policyrest . RESTStorageProvider ,
schedulingrest . RESTStorageProvider ,
storagerest . RESTStorageProvider ,
appsrest . StorageProvider ,
resourcerest . RESTStorageProvider :
// all these are non-generic, but kube specific
continue
default :
t . Errorf ( "Unexpected, uncategorized storage %T from %s. Put into the list above for kube-specific APIs, or into GenericStorageProviders for generic APIs" , kube [ k ] , kt . PkgPath ( ) )
}
}
if g != len ( generic ) {
t . Errorf ( "Unexpected, generic APIs found: %#v" , generic [ g : ] )
}
}