fake.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package testing
  14. import (
  15. "bytes"
  16. "errors"
  17. "fmt"
  18. "io/ioutil"
  19. "os"
  20. "path/filepath"
  21. "time"
  22. "k8s.io/apimachinery/pkg/api/meta"
  23. "k8s.io/apimachinery/pkg/api/meta/testrestmapper"
  24. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  25. "k8s.io/apimachinery/pkg/runtime"
  26. "k8s.io/apimachinery/pkg/runtime/schema"
  27. "k8s.io/apimachinery/pkg/runtime/serializer"
  28. "k8s.io/cli-runtime/pkg/genericclioptions"
  29. "k8s.io/cli-runtime/pkg/resource"
  30. "k8s.io/client-go/discovery"
  31. diskcached "k8s.io/client-go/discovery/cached/disk"
  32. "k8s.io/client-go/dynamic"
  33. fakedynamic "k8s.io/client-go/dynamic/fake"
  34. "k8s.io/client-go/kubernetes"
  35. restclient "k8s.io/client-go/rest"
  36. "k8s.io/client-go/rest/fake"
  37. "k8s.io/client-go/restmapper"
  38. scaleclient "k8s.io/client-go/scale"
  39. "k8s.io/client-go/tools/clientcmd"
  40. clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
  41. "k8s.io/kubernetes/pkg/kubectl"
  42. cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  43. "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
  44. openapitesting "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/testing"
  45. "k8s.io/kubernetes/pkg/kubectl/scheme"
  46. "k8s.io/kubernetes/pkg/kubectl/validation"
  47. )
  48. // InternalType is the schema for internal type
  49. // +k8s:deepcopy-gen=true
  50. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
  51. type InternalType struct {
  52. Kind string
  53. APIVersion string
  54. Name string
  55. }
  56. // ExternalType is the schema for external type
  57. // +k8s:deepcopy-gen=true
  58. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
  59. type ExternalType struct {
  60. Kind string `json:"kind"`
  61. APIVersion string `json:"apiVersion"`
  62. Name string `json:"name"`
  63. }
  64. // ExternalType2 is another schema for external type
  65. // +k8s:deepcopy-gen=true
  66. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
  67. type ExternalType2 struct {
  68. Kind string `json:"kind"`
  69. APIVersion string `json:"apiVersion"`
  70. Name string `json:"name"`
  71. }
  72. // GetObjectKind returns the ObjectKind schema
  73. func (obj *InternalType) GetObjectKind() schema.ObjectKind { return obj }
  74. // SetGroupVersionKind sets the version and kind
  75. func (obj *InternalType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
  76. obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
  77. }
  78. // GroupVersionKind returns GroupVersionKind schema
  79. func (obj *InternalType) GroupVersionKind() schema.GroupVersionKind {
  80. return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
  81. }
  82. // GetObjectKind returns the ObjectKind schema
  83. func (obj *ExternalType) GetObjectKind() schema.ObjectKind { return obj }
  84. // SetGroupVersionKind returns the GroupVersionKind schema
  85. func (obj *ExternalType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
  86. obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
  87. }
  88. // GroupVersionKind returns the GroupVersionKind schema
  89. func (obj *ExternalType) GroupVersionKind() schema.GroupVersionKind {
  90. return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
  91. }
  92. // GetObjectKind returns the ObjectKind schema
  93. func (obj *ExternalType2) GetObjectKind() schema.ObjectKind { return obj }
  94. // SetGroupVersionKind sets the API version and obj kind from schema
  95. func (obj *ExternalType2) SetGroupVersionKind(gvk schema.GroupVersionKind) {
  96. obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
  97. }
  98. // GroupVersionKind returns the FromAPIVersionAndKind schema
  99. func (obj *ExternalType2) GroupVersionKind() schema.GroupVersionKind {
  100. return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
  101. }
  102. // NewInternalType returns an initialized InternalType instance
  103. func NewInternalType(kind, apiversion, name string) *InternalType {
  104. item := InternalType{Kind: kind,
  105. APIVersion: apiversion,
  106. Name: name}
  107. return &item
  108. }
  109. // InternalNamespacedType schema for internal namespaced types
  110. // +k8s:deepcopy-gen=true
  111. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
  112. type InternalNamespacedType struct {
  113. Kind string
  114. APIVersion string
  115. Name string
  116. Namespace string
  117. }
  118. // ExternalNamespacedType schema for external namespaced types
  119. // +k8s:deepcopy-gen=true
  120. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
  121. type ExternalNamespacedType struct {
  122. Kind string `json:"kind"`
  123. APIVersion string `json:"apiVersion"`
  124. Name string `json:"name"`
  125. Namespace string `json:"namespace"`
  126. }
  127. // ExternalNamespacedType2 schema for external namespaced types
  128. // +k8s:deepcopy-gen=true
  129. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
  130. type ExternalNamespacedType2 struct {
  131. Kind string `json:"kind"`
  132. APIVersion string `json:"apiVersion"`
  133. Name string `json:"name"`
  134. Namespace string `json:"namespace"`
  135. }
  136. // GetObjectKind returns the ObjectKind schema
  137. func (obj *InternalNamespacedType) GetObjectKind() schema.ObjectKind { return obj }
  138. // SetGroupVersionKind sets the API group and kind from schema
  139. func (obj *InternalNamespacedType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
  140. obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
  141. }
  142. // GroupVersionKind returns the GroupVersionKind schema
  143. func (obj *InternalNamespacedType) GroupVersionKind() schema.GroupVersionKind {
  144. return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
  145. }
  146. // GetObjectKind returns the ObjectKind schema
  147. func (obj *ExternalNamespacedType) GetObjectKind() schema.ObjectKind { return obj }
  148. // SetGroupVersionKind sets the API version and kind from schema
  149. func (obj *ExternalNamespacedType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
  150. obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
  151. }
  152. // GroupVersionKind returns the GroupVersionKind schema
  153. func (obj *ExternalNamespacedType) GroupVersionKind() schema.GroupVersionKind {
  154. return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
  155. }
  156. // GetObjectKind returns the ObjectKind schema
  157. func (obj *ExternalNamespacedType2) GetObjectKind() schema.ObjectKind { return obj }
  158. // SetGroupVersionKind sets the API version and kind from schema
  159. func (obj *ExternalNamespacedType2) SetGroupVersionKind(gvk schema.GroupVersionKind) {
  160. obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
  161. }
  162. // GroupVersionKind returns the GroupVersionKind schema
  163. func (obj *ExternalNamespacedType2) GroupVersionKind() schema.GroupVersionKind {
  164. return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
  165. }
  166. // NewInternalNamespacedType returns an initialized instance of InternalNamespacedType
  167. func NewInternalNamespacedType(kind, apiversion, name, namespace string) *InternalNamespacedType {
  168. item := InternalNamespacedType{Kind: kind,
  169. APIVersion: apiversion,
  170. Name: name,
  171. Namespace: namespace}
  172. return &item
  173. }
  174. var errInvalidVersion = errors.New("not a version")
  175. // ValidVersion of API
  176. var ValidVersion = "v1"
  177. // InternalGV is the internal group version object
  178. var InternalGV = schema.GroupVersion{Group: "apitest", Version: runtime.APIVersionInternal}
  179. // UnlikelyGV is a group version object for unrecognised version
  180. var UnlikelyGV = schema.GroupVersion{Group: "apitest", Version: "unlikelyversion"}
  181. // ValidVersionGV is the valid group version object
  182. var ValidVersionGV = schema.GroupVersion{Group: "apitest", Version: ValidVersion}
  183. // NewExternalScheme returns required objects for ExternalScheme
  184. func NewExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
  185. scheme := runtime.NewScheme()
  186. mapper, codec := AddToScheme(scheme)
  187. return scheme, mapper, codec
  188. }
  189. // AddToScheme adds required objects into scheme
  190. func AddToScheme(scheme *runtime.Scheme) (meta.RESTMapper, runtime.Codec) {
  191. scheme.AddKnownTypeWithName(InternalGV.WithKind("Type"), &InternalType{})
  192. scheme.AddKnownTypeWithName(UnlikelyGV.WithKind("Type"), &ExternalType{})
  193. //This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
  194. scheme.AddKnownTypeWithName(ValidVersionGV.WithKind("Type"), &ExternalType2{})
  195. scheme.AddKnownTypeWithName(InternalGV.WithKind("NamespacedType"), &InternalNamespacedType{})
  196. scheme.AddKnownTypeWithName(UnlikelyGV.WithKind("NamespacedType"), &ExternalNamespacedType{})
  197. //This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
  198. scheme.AddKnownTypeWithName(ValidVersionGV.WithKind("NamespacedType"), &ExternalNamespacedType2{})
  199. codecs := serializer.NewCodecFactory(scheme)
  200. codec := codecs.LegacyCodec(UnlikelyGV)
  201. mapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{UnlikelyGV, ValidVersionGV})
  202. for _, gv := range []schema.GroupVersion{UnlikelyGV, ValidVersionGV} {
  203. for kind := range scheme.KnownTypes(gv) {
  204. gvk := gv.WithKind(kind)
  205. scope := meta.RESTScopeNamespace
  206. mapper.Add(gvk, scope)
  207. }
  208. }
  209. return mapper, codec
  210. }
  211. type fakeCachedDiscoveryClient struct {
  212. discovery.DiscoveryInterface
  213. }
  214. func (d *fakeCachedDiscoveryClient) Fresh() bool {
  215. return true
  216. }
  217. func (d *fakeCachedDiscoveryClient) Invalidate() {
  218. }
  219. // Deprecated: use ServerGroupsAndResources instead.
  220. func (d *fakeCachedDiscoveryClient) ServerResources() ([]*metav1.APIResourceList, error) {
  221. return []*metav1.APIResourceList{}, nil
  222. }
  223. func (d *fakeCachedDiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
  224. return []*metav1.APIGroup{}, []*metav1.APIResourceList{}, nil
  225. }
  226. // TestFactory extends cmdutil.Factory
  227. type TestFactory struct {
  228. cmdutil.Factory
  229. kubeConfigFlags *genericclioptions.TestConfigFlags
  230. Client kubectl.RESTClient
  231. ScaleGetter scaleclient.ScalesGetter
  232. UnstructuredClient kubectl.RESTClient
  233. ClientConfigVal *restclient.Config
  234. FakeDynamicClient *fakedynamic.FakeDynamicClient
  235. tempConfigFile *os.File
  236. UnstructuredClientForMappingFunc resource.FakeClientFunc
  237. OpenAPISchemaFunc func() (openapi.Resources, error)
  238. }
  239. // NewTestFactory returns an initialized TestFactory instance
  240. func NewTestFactory() *TestFactory {
  241. // specify an optionalClientConfig to explicitly use in testing
  242. // to avoid polluting an existing user config.
  243. tmpFile, err := ioutil.TempFile("", "cmdtests_temp")
  244. if err != nil {
  245. panic(fmt.Sprintf("unable to create a fake client config: %v", err))
  246. }
  247. loadingRules := &clientcmd.ClientConfigLoadingRules{
  248. Precedence: []string{tmpFile.Name()},
  249. MigrationRules: map[string]string{},
  250. }
  251. overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"}}
  252. fallbackReader := bytes.NewBuffer([]byte{})
  253. clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, fallbackReader)
  254. configFlags := genericclioptions.NewTestConfigFlags().
  255. WithClientConfig(clientConfig).
  256. WithRESTMapper(testRESTMapper())
  257. restConfig, err := clientConfig.ClientConfig()
  258. if err != nil {
  259. panic(fmt.Sprintf("unable to create a fake restclient config: %v", err))
  260. }
  261. return &TestFactory{
  262. Factory: cmdutil.NewFactory(configFlags),
  263. kubeConfigFlags: configFlags,
  264. FakeDynamicClient: fakedynamic.NewSimpleDynamicClient(scheme.Scheme),
  265. tempConfigFile: tmpFile,
  266. ClientConfigVal: restConfig,
  267. }
  268. }
  269. // WithNamespace is used to mention namespace reactively
  270. func (f *TestFactory) WithNamespace(ns string) *TestFactory {
  271. f.kubeConfigFlags.WithNamespace(ns)
  272. return f
  273. }
  274. // Cleanup cleans up TestFactory temp config file
  275. func (f *TestFactory) Cleanup() {
  276. if f.tempConfigFile == nil {
  277. return
  278. }
  279. os.Remove(f.tempConfigFile.Name())
  280. }
  281. // ToRESTConfig is used to get ClientConfigVal from a TestFactory
  282. func (f *TestFactory) ToRESTConfig() (*restclient.Config, error) {
  283. return f.ClientConfigVal, nil
  284. }
  285. // ClientForMapping is used to Client from a TestFactory
  286. func (f *TestFactory) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
  287. return f.Client, nil
  288. }
  289. // UnstructuredClientForMapping is used to get UnstructuredClient from a TestFactory
  290. func (f *TestFactory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
  291. if f.UnstructuredClientForMappingFunc != nil {
  292. return f.UnstructuredClientForMappingFunc(mapping.GroupVersionKind.GroupVersion())
  293. }
  294. return f.UnstructuredClient, nil
  295. }
  296. // Validator returns a validation schema
  297. func (f *TestFactory) Validator(validate bool) (validation.Schema, error) {
  298. return validation.NullSchema{}, nil
  299. }
  300. // OpenAPISchema returns openapi resources
  301. func (f *TestFactory) OpenAPISchema() (openapi.Resources, error) {
  302. if f.OpenAPISchemaFunc != nil {
  303. return f.OpenAPISchemaFunc()
  304. }
  305. return openapitesting.EmptyResources{}, nil
  306. }
  307. // NewBuilder returns an initialized resource.Builder instance
  308. func (f *TestFactory) NewBuilder() *resource.Builder {
  309. return resource.NewFakeBuilder(
  310. func(version schema.GroupVersion) (resource.RESTClient, error) {
  311. if f.UnstructuredClientForMappingFunc != nil {
  312. return f.UnstructuredClientForMappingFunc(version)
  313. }
  314. if f.UnstructuredClient != nil {
  315. return f.UnstructuredClient, nil
  316. }
  317. return f.Client, nil
  318. },
  319. f.ToRESTMapper,
  320. func() (restmapper.CategoryExpander, error) {
  321. return resource.FakeCategoryExpander, nil
  322. },
  323. )
  324. }
  325. // KubernetesClientSet initializes and returns the Clientset using TestFactory
  326. func (f *TestFactory) KubernetesClientSet() (*kubernetes.Clientset, error) {
  327. fakeClient := f.Client.(*fake.RESTClient)
  328. clientset := kubernetes.NewForConfigOrDie(f.ClientConfigVal)
  329. clientset.CoreV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  330. clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  331. clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  332. clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  333. clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  334. clientset.AutoscalingV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  335. clientset.AutoscalingV2beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  336. clientset.BatchV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  337. clientset.BatchV2alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  338. clientset.CertificatesV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  339. clientset.ExtensionsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  340. clientset.RbacV1alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  341. clientset.RbacV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  342. clientset.StorageV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  343. clientset.StorageV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  344. clientset.AppsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  345. clientset.AppsV1beta2().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  346. clientset.AppsV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  347. clientset.PolicyV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  348. clientset.DiscoveryClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  349. return clientset, nil
  350. }
  351. // DynamicClient returns a dynamic client from TestFactory
  352. func (f *TestFactory) DynamicClient() (dynamic.Interface, error) {
  353. if f.FakeDynamicClient != nil {
  354. return f.FakeDynamicClient, nil
  355. }
  356. return f.Factory.DynamicClient()
  357. }
  358. // RESTClient returns a REST client from TestFactory
  359. func (f *TestFactory) RESTClient() (*restclient.RESTClient, error) {
  360. // Swap out the HTTP client out of the client with the fake's version.
  361. fakeClient := f.Client.(*fake.RESTClient)
  362. restClient, err := restclient.RESTClientFor(f.ClientConfigVal)
  363. if err != nil {
  364. panic(err)
  365. }
  366. restClient.Client = fakeClient.Client
  367. return restClient, nil
  368. }
  369. // DiscoveryClient returns a discovery client from TestFactory
  370. func (f *TestFactory) DiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
  371. fakeClient := f.Client.(*fake.RESTClient)
  372. cacheDir := filepath.Join("", ".kube", "cache", "discovery")
  373. cachedClient, err := diskcached.NewCachedDiscoveryClientForConfig(f.ClientConfigVal, cacheDir, "", time.Duration(10*time.Minute))
  374. if err != nil {
  375. return nil, err
  376. }
  377. cachedClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
  378. return cachedClient, nil
  379. }
  380. func testRESTMapper() meta.RESTMapper {
  381. groupResources := testDynamicResources()
  382. mapper := restmapper.NewDiscoveryRESTMapper(groupResources)
  383. // for backwards compatibility with existing tests, allow rest mappings from the scheme to show up
  384. // TODO: make this opt-in?
  385. mapper = meta.FirstHitRESTMapper{
  386. MultiRESTMapper: meta.MultiRESTMapper{
  387. mapper,
  388. testrestmapper.TestOnlyStaticRESTMapper(scheme.Scheme),
  389. },
  390. }
  391. fakeDs := &fakeCachedDiscoveryClient{}
  392. expander := restmapper.NewShortcutExpander(mapper, fakeDs)
  393. return expander
  394. }
  395. // ScaleClient returns the ScalesGetter from a TestFactory
  396. func (f *TestFactory) ScaleClient() (scaleclient.ScalesGetter, error) {
  397. return f.ScaleGetter, nil
  398. }
  399. func testDynamicResources() []*restmapper.APIGroupResources {
  400. return []*restmapper.APIGroupResources{
  401. {
  402. Group: metav1.APIGroup{
  403. Versions: []metav1.GroupVersionForDiscovery{
  404. {Version: "v1"},
  405. },
  406. PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
  407. },
  408. VersionedResources: map[string][]metav1.APIResource{
  409. "v1": {
  410. {Name: "pods", Namespaced: true, Kind: "Pod"},
  411. {Name: "services", Namespaced: true, Kind: "Service"},
  412. {Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
  413. {Name: "componentstatuses", Namespaced: false, Kind: "ComponentStatus"},
  414. {Name: "nodes", Namespaced: false, Kind: "Node"},
  415. {Name: "secrets", Namespaced: true, Kind: "Secret"},
  416. {Name: "configmaps", Namespaced: true, Kind: "ConfigMap"},
  417. {Name: "namespacedtype", Namespaced: true, Kind: "NamespacedType"},
  418. {Name: "namespaces", Namespaced: false, Kind: "Namespace"},
  419. {Name: "resourcequotas", Namespaced: true, Kind: "ResourceQuota"},
  420. },
  421. },
  422. },
  423. {
  424. Group: metav1.APIGroup{
  425. Name: "extensions",
  426. Versions: []metav1.GroupVersionForDiscovery{
  427. {Version: "v1beta1"},
  428. },
  429. PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1beta1"},
  430. },
  431. VersionedResources: map[string][]metav1.APIResource{
  432. "v1beta1": {
  433. {Name: "deployments", Namespaced: true, Kind: "Deployment"},
  434. {Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
  435. },
  436. },
  437. },
  438. {
  439. Group: metav1.APIGroup{
  440. Name: "apps",
  441. Versions: []metav1.GroupVersionForDiscovery{
  442. {Version: "v1beta1"},
  443. {Version: "v1beta2"},
  444. {Version: "v1"},
  445. },
  446. PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
  447. },
  448. VersionedResources: map[string][]metav1.APIResource{
  449. "v1beta1": {
  450. {Name: "deployments", Namespaced: true, Kind: "Deployment"},
  451. {Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
  452. },
  453. "v1beta2": {
  454. {Name: "deployments", Namespaced: true, Kind: "Deployment"},
  455. },
  456. "v1": {
  457. {Name: "deployments", Namespaced: true, Kind: "Deployment"},
  458. {Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
  459. },
  460. },
  461. },
  462. {
  463. Group: metav1.APIGroup{
  464. Name: "batch",
  465. Versions: []metav1.GroupVersionForDiscovery{
  466. {Version: "v1beta1"},
  467. {Version: "v1"},
  468. },
  469. PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
  470. },
  471. VersionedResources: map[string][]metav1.APIResource{
  472. "v1beta1": {
  473. {Name: "cronjobs", Namespaced: true, Kind: "CronJob"},
  474. },
  475. "v1": {
  476. {Name: "jobs", Namespaced: true, Kind: "Job"},
  477. },
  478. },
  479. },
  480. {
  481. Group: metav1.APIGroup{
  482. Name: "autoscaling",
  483. Versions: []metav1.GroupVersionForDiscovery{
  484. {Version: "v1"},
  485. {Version: "v2beta1"},
  486. },
  487. PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v2beta1"},
  488. },
  489. VersionedResources: map[string][]metav1.APIResource{
  490. "v1": {
  491. {Name: "horizontalpodautoscalers", Namespaced: true, Kind: "HorizontalPodAutoscaler"},
  492. },
  493. "v2beta1": {
  494. {Name: "horizontalpodautoscalers", Namespaced: true, Kind: "HorizontalPodAutoscaler"},
  495. },
  496. },
  497. },
  498. {
  499. Group: metav1.APIGroup{
  500. Name: "storage.k8s.io",
  501. Versions: []metav1.GroupVersionForDiscovery{
  502. {Version: "v1beta1"},
  503. {Version: "v0"},
  504. },
  505. PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1beta1"},
  506. },
  507. VersionedResources: map[string][]metav1.APIResource{
  508. "v1beta1": {
  509. {Name: "storageclasses", Namespaced: false, Kind: "StorageClass"},
  510. },
  511. // bogus version of a known group/version/resource to make sure kubectl falls back to generic object mode
  512. "v0": {
  513. {Name: "storageclasses", Namespaced: false, Kind: "StorageClass"},
  514. },
  515. },
  516. },
  517. {
  518. Group: metav1.APIGroup{
  519. Name: "rbac.authorization.k8s.io",
  520. Versions: []metav1.GroupVersionForDiscovery{
  521. {Version: "v1beta1"},
  522. {Version: "v1"},
  523. },
  524. PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
  525. },
  526. VersionedResources: map[string][]metav1.APIResource{
  527. "v1": {
  528. {Name: "clusterroles", Namespaced: false, Kind: "ClusterRole"},
  529. },
  530. "v1beta1": {
  531. {Name: "clusterrolebindings", Namespaced: false, Kind: "ClusterRoleBinding"},
  532. },
  533. },
  534. },
  535. {
  536. Group: metav1.APIGroup{
  537. Name: "company.com",
  538. Versions: []metav1.GroupVersionForDiscovery{
  539. {Version: "v1"},
  540. },
  541. PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
  542. },
  543. VersionedResources: map[string][]metav1.APIResource{
  544. "v1": {
  545. {Name: "bars", Namespaced: true, Kind: "Bar"},
  546. },
  547. },
  548. },
  549. {
  550. Group: metav1.APIGroup{
  551. Name: "unit-test.test.com",
  552. Versions: []metav1.GroupVersionForDiscovery{
  553. {GroupVersion: "unit-test.test.com/v1", Version: "v1"},
  554. },
  555. PreferredVersion: metav1.GroupVersionForDiscovery{
  556. GroupVersion: "unit-test.test.com/v1",
  557. Version: "v1"},
  558. },
  559. VersionedResources: map[string][]metav1.APIResource{
  560. "v1": {
  561. {Name: "widgets", Namespaced: true, Kind: "Widget"},
  562. },
  563. },
  564. },
  565. {
  566. Group: metav1.APIGroup{
  567. Name: "apitest",
  568. Versions: []metav1.GroupVersionForDiscovery{
  569. {GroupVersion: "apitest/unlikelyversion", Version: "unlikelyversion"},
  570. },
  571. PreferredVersion: metav1.GroupVersionForDiscovery{
  572. GroupVersion: "apitest/unlikelyversion",
  573. Version: "unlikelyversion"},
  574. },
  575. VersionedResources: map[string][]metav1.APIResource{
  576. "unlikelyversion": {
  577. {Name: "types", SingularName: "type", Namespaced: false, Kind: "Type"},
  578. },
  579. },
  580. },
  581. }
  582. }