kubectl/pkg/cmd/testing/fake.go

838 lines
29 KiB
Go

/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testing
import (
"bytes"
"fmt"
"os"
"path/filepath"
"time"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/meta/testrestmapper"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/discovery"
diskcached "k8s.io/client-go/discovery/cached/disk"
"k8s.io/client-go/dynamic"
fakedynamic "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/kubernetes"
openapiclient "k8s.io/client-go/openapi"
"k8s.io/client-go/openapi/openapitest"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/client-go/restmapper"
scaleclient "k8s.io/client-go/scale"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util/openapi"
openapitesting "k8s.io/kubectl/pkg/util/openapi/testing"
"k8s.io/kubectl/pkg/validation"
)
// InternalType is the schema for internal type
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type InternalType struct {
Kind string
APIVersion string
Name string
}
// ExternalType is the schema for external type
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ExternalType struct {
Kind string `json:"kind"`
APIVersion string `json:"apiVersion"`
Name string `json:"name"`
}
// ExternalType2 is another schema for external type
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ExternalType2 struct {
Kind string `json:"kind"`
APIVersion string `json:"apiVersion"`
Name string `json:"name"`
}
// GetObjectKind returns the ObjectKind schema
func (obj *InternalType) GetObjectKind() schema.ObjectKind { return obj }
// SetGroupVersionKind sets the version and kind
func (obj *InternalType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
// GroupVersionKind returns GroupVersionKind schema
func (obj *InternalType) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
// GetObjectKind returns the ObjectKind schema
func (obj *ExternalType) GetObjectKind() schema.ObjectKind { return obj }
// SetGroupVersionKind returns the GroupVersionKind schema
func (obj *ExternalType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
// GroupVersionKind returns the GroupVersionKind schema
func (obj *ExternalType) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
// GetObjectKind returns the ObjectKind schema
func (obj *ExternalType2) GetObjectKind() schema.ObjectKind { return obj }
// SetGroupVersionKind sets the API version and obj kind from schema
func (obj *ExternalType2) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
// GroupVersionKind returns the FromAPIVersionAndKind schema
func (obj *ExternalType2) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
// NewInternalType returns an initialized InternalType instance
func NewInternalType(kind, apiversion, name string) *InternalType {
item := InternalType{Kind: kind,
APIVersion: apiversion,
Name: name}
return &item
}
func convertInternalTypeToExternalType(in *InternalType, out *ExternalType, s conversion.Scope) error {
out.Kind = in.Kind
out.APIVersion = in.APIVersion
out.Name = in.Name
return nil
}
func convertInternalTypeToExternalType2(in *InternalType, out *ExternalType2, s conversion.Scope) error {
out.Kind = in.Kind
out.APIVersion = in.APIVersion
out.Name = in.Name
return nil
}
func convertExternalTypeToInternalType(in *ExternalType, out *InternalType, s conversion.Scope) error {
out.Kind = in.Kind
out.APIVersion = in.APIVersion
out.Name = in.Name
return nil
}
func convertExternalType2ToInternalType(in *ExternalType2, out *InternalType, s conversion.Scope) error {
out.Kind = in.Kind
out.APIVersion = in.APIVersion
out.Name = in.Name
return nil
}
// InternalNamespacedType schema for internal namespaced types
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type InternalNamespacedType struct {
Kind string
APIVersion string
Name string
Namespace string
}
// ExternalNamespacedType schema for external namespaced types
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ExternalNamespacedType struct {
Kind string `json:"kind"`
APIVersion string `json:"apiVersion"`
Name string `json:"name"`
Namespace string `json:"namespace"`
}
// ExternalNamespacedType2 schema for external namespaced types
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ExternalNamespacedType2 struct {
Kind string `json:"kind"`
APIVersion string `json:"apiVersion"`
Name string `json:"name"`
Namespace string `json:"namespace"`
}
// GetObjectKind returns the ObjectKind schema
func (obj *InternalNamespacedType) GetObjectKind() schema.ObjectKind { return obj }
// SetGroupVersionKind sets the API group and kind from schema
func (obj *InternalNamespacedType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
// GroupVersionKind returns the GroupVersionKind schema
func (obj *InternalNamespacedType) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
// GetObjectKind returns the ObjectKind schema
func (obj *ExternalNamespacedType) GetObjectKind() schema.ObjectKind { return obj }
// SetGroupVersionKind sets the API version and kind from schema
func (obj *ExternalNamespacedType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
// GroupVersionKind returns the GroupVersionKind schema
func (obj *ExternalNamespacedType) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
// GetObjectKind returns the ObjectKind schema
func (obj *ExternalNamespacedType2) GetObjectKind() schema.ObjectKind { return obj }
// SetGroupVersionKind sets the API version and kind from schema
func (obj *ExternalNamespacedType2) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
// GroupVersionKind returns the GroupVersionKind schema
func (obj *ExternalNamespacedType2) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
// NewInternalNamespacedType returns an initialized instance of InternalNamespacedType
func NewInternalNamespacedType(kind, apiversion, name, namespace string) *InternalNamespacedType {
item := InternalNamespacedType{Kind: kind,
APIVersion: apiversion,
Name: name,
Namespace: namespace}
return &item
}
func convertInternalNamespacedTypeToExternalNamespacedType(in *InternalNamespacedType, out *ExternalNamespacedType, s conversion.Scope) error {
out.Kind = in.Kind
out.APIVersion = in.APIVersion
out.Name = in.Name
out.Namespace = in.Namespace
return nil
}
func convertInternalNamespacedTypeToExternalNamespacedType2(in *InternalNamespacedType, out *ExternalNamespacedType2, s conversion.Scope) error {
out.Kind = in.Kind
out.APIVersion = in.APIVersion
out.Name = in.Name
out.Namespace = in.Namespace
return nil
}
func convertExternalNamespacedTypeToInternalNamespacedType(in *ExternalNamespacedType, out *InternalNamespacedType, s conversion.Scope) error {
out.Kind = in.Kind
out.APIVersion = in.APIVersion
out.Name = in.Name
out.Namespace = in.Namespace
return nil
}
func convertExternalNamespacedType2ToInternalNamespacedType(in *ExternalNamespacedType2, out *InternalNamespacedType, s conversion.Scope) error {
out.Kind = in.Kind
out.APIVersion = in.APIVersion
out.Name = in.Name
out.Namespace = in.Namespace
return nil
}
// ValidVersion of API
var ValidVersion = "v1"
// InternalGV is the internal group version object
var InternalGV = schema.GroupVersion{Group: "apitest", Version: runtime.APIVersionInternal}
// UnlikelyGV is a group version object for unrecognised version
var UnlikelyGV = schema.GroupVersion{Group: "apitest", Version: "unlikelyversion"}
// ValidVersionGV is the valid group version object
var ValidVersionGV = schema.GroupVersion{Group: "apitest", Version: ValidVersion}
// NewExternalScheme returns required objects for ExternalScheme
func NewExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
scheme := runtime.NewScheme()
mapper, codec := AddToScheme(scheme)
return scheme, mapper, codec
}
func registerConversions(s *runtime.Scheme) error {
if err := s.AddConversionFunc((*InternalType)(nil), (*ExternalType)(nil), func(a, b interface{}, scope conversion.Scope) error {
return convertInternalTypeToExternalType(a.(*InternalType), b.(*ExternalType), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*InternalType)(nil), (*ExternalType2)(nil), func(a, b interface{}, scope conversion.Scope) error {
return convertInternalTypeToExternalType2(a.(*InternalType), b.(*ExternalType2), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*ExternalType)(nil), (*InternalType)(nil), func(a, b interface{}, scope conversion.Scope) error {
return convertExternalTypeToInternalType(a.(*ExternalType), b.(*InternalType), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*ExternalType2)(nil), (*InternalType)(nil), func(a, b interface{}, scope conversion.Scope) error {
return convertExternalType2ToInternalType(a.(*ExternalType2), b.(*InternalType), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*InternalNamespacedType)(nil), (*ExternalNamespacedType)(nil), func(a, b interface{}, scope conversion.Scope) error {
return convertInternalNamespacedTypeToExternalNamespacedType(a.(*InternalNamespacedType), b.(*ExternalNamespacedType), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*InternalNamespacedType)(nil), (*ExternalNamespacedType2)(nil), func(a, b interface{}, scope conversion.Scope) error {
return convertInternalNamespacedTypeToExternalNamespacedType2(a.(*InternalNamespacedType), b.(*ExternalNamespacedType2), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*ExternalNamespacedType)(nil), (*InternalNamespacedType)(nil), func(a, b interface{}, scope conversion.Scope) error {
return convertExternalNamespacedTypeToInternalNamespacedType(a.(*ExternalNamespacedType), b.(*InternalNamespacedType), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*ExternalNamespacedType2)(nil), (*InternalNamespacedType)(nil), func(a, b interface{}, scope conversion.Scope) error {
return convertExternalNamespacedType2ToInternalNamespacedType(a.(*ExternalNamespacedType2), b.(*InternalNamespacedType), scope)
}); err != nil {
return err
}
return nil
}
// AddToScheme adds required objects into scheme
func AddToScheme(scheme *runtime.Scheme) (meta.RESTMapper, runtime.Codec) {
scheme.AddKnownTypeWithName(InternalGV.WithKind("Type"), &InternalType{})
scheme.AddKnownTypeWithName(UnlikelyGV.WithKind("Type"), &ExternalType{})
// This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
scheme.AddKnownTypeWithName(ValidVersionGV.WithKind("Type"), &ExternalType2{})
scheme.AddKnownTypeWithName(InternalGV.WithKind("NamespacedType"), &InternalNamespacedType{})
scheme.AddKnownTypeWithName(UnlikelyGV.WithKind("NamespacedType"), &ExternalNamespacedType{})
// This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
scheme.AddKnownTypeWithName(ValidVersionGV.WithKind("NamespacedType"), &ExternalNamespacedType2{})
utilruntime.Must(registerConversions(scheme))
codecs := serializer.NewCodecFactory(scheme)
codec := codecs.LegacyCodec(UnlikelyGV)
mapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{UnlikelyGV, ValidVersionGV})
for _, gv := range []schema.GroupVersion{UnlikelyGV, ValidVersionGV} {
for kind := range scheme.KnownTypes(gv) {
gvk := gv.WithKind(kind)
scope := meta.RESTScopeNamespace
mapper.Add(gvk, scope)
}
}
return mapper, codec
}
type FakeCachedDiscoveryClient struct {
discovery.DiscoveryInterface
Groups []*metav1.APIGroup
Resources []*metav1.APIResourceList
PreferredResources []*metav1.APIResourceList
Invalidations int
}
func NewFakeCachedDiscoveryClient() *FakeCachedDiscoveryClient {
return &FakeCachedDiscoveryClient{
Groups: []*metav1.APIGroup{},
Resources: []*metav1.APIResourceList{},
PreferredResources: []*metav1.APIResourceList{},
Invalidations: 0,
}
}
func (d *FakeCachedDiscoveryClient) Fresh() bool {
return true
}
func (d *FakeCachedDiscoveryClient) Invalidate() {
d.Invalidations++
}
func (d *FakeCachedDiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
return d.Groups, d.Resources, nil
}
func (d *FakeCachedDiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) {
groupList := &metav1.APIGroupList{Groups: []metav1.APIGroup{}}
for _, g := range d.Groups {
groupList.Groups = append(groupList.Groups, *g)
}
return groupList, nil
}
func (d *FakeCachedDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
return d.PreferredResources, nil
}
// TestFactory extends cmdutil.Factory
type TestFactory struct {
cmdutil.Factory
kubeConfigFlags *genericclioptions.TestConfigFlags
Client RESTClient
ScaleGetter scaleclient.ScalesGetter
UnstructuredClient RESTClient
ClientConfigVal *restclient.Config
FakeDynamicClient *fakedynamic.FakeDynamicClient
tempConfigFile *os.File
UnstructuredClientForMappingFunc resource.FakeClientFunc
OpenAPISchemaFunc func() (openapi.Resources, error)
OpenAPIV3ClientFunc func() (openapiclient.Client, error)
}
// NewTestFactory returns an initialized TestFactory instance
func NewTestFactory() *TestFactory {
// specify an optionalClientConfig to explicitly use in testing
// to avoid polluting an existing user config.
tmpFile, err := os.CreateTemp(os.TempDir(), "cmdtests_temp")
if err != nil {
panic(fmt.Sprintf("unable to create a fake client config: %v", err))
}
loadingRules := &clientcmd.ClientConfigLoadingRules{
Precedence: []string{tmpFile.Name()},
MigrationRules: map[string]string{},
}
overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"}}
fallbackReader := bytes.NewBuffer([]byte{})
clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, fallbackReader)
configFlags := genericclioptions.NewTestConfigFlags().
WithClientConfig(clientConfig).
WithRESTMapper(testRESTMapper())
restConfig, err := clientConfig.ClientConfig()
if err != nil {
panic(fmt.Sprintf("unable to create a fake restclient config: %v", err))
}
return &TestFactory{
Factory: cmdutil.NewFactory(configFlags),
kubeConfigFlags: configFlags,
FakeDynamicClient: fakedynamic.NewSimpleDynamicClient(scheme.Scheme),
tempConfigFile: tmpFile,
ClientConfigVal: restConfig,
}
}
// WithNamespace is used to mention namespace reactively
func (f *TestFactory) WithNamespace(ns string) *TestFactory {
f.kubeConfigFlags.WithNamespace(ns)
return f
}
// WithClientConfig sets the client config to use
func (f *TestFactory) WithClientConfig(clientConfig clientcmd.ClientConfig) *TestFactory {
f.kubeConfigFlags.WithClientConfig(clientConfig)
return f
}
func (f *TestFactory) WithDiscoveryClient(discoveryClient discovery.CachedDiscoveryInterface) *TestFactory {
f.kubeConfigFlags.WithDiscoveryClient(discoveryClient)
return f
}
// Cleanup cleans up TestFactory temp config file
func (f *TestFactory) Cleanup() {
if f.tempConfigFile == nil {
return
}
f.tempConfigFile.Close()
os.Remove(f.tempConfigFile.Name())
}
// ToRESTConfig is used to get ClientConfigVal from a TestFactory
func (f *TestFactory) ToRESTConfig() (*restclient.Config, error) {
return f.ClientConfigVal, nil
}
// ClientForMapping is used to Client from a TestFactory
func (f *TestFactory) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return f.Client, nil
}
// PathOptions returns a new PathOptions with a temp file
func (f *TestFactory) PathOptions() *clientcmd.PathOptions {
pathOptions := clientcmd.NewDefaultPathOptions()
pathOptions.GlobalFile = f.tempConfigFile.Name()
pathOptions.EnvVar = ""
return pathOptions
}
// PathOptionsWithConfig writes a config to a temp file and returns PathOptions
func (f *TestFactory) PathOptionsWithConfig(config clientcmdapi.Config) (*clientcmd.PathOptions, error) {
pathOptions := f.PathOptions()
err := clientcmd.WriteToFile(config, pathOptions.GlobalFile)
if err != nil {
return nil, err
}
return pathOptions, nil
}
// UnstructuredClientForMapping is used to get UnstructuredClient from a TestFactory
func (f *TestFactory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
if f.UnstructuredClientForMappingFunc != nil {
return f.UnstructuredClientForMappingFunc(mapping.GroupVersionKind.GroupVersion())
}
return f.UnstructuredClient, nil
}
// Validator returns a validation schema
func (f *TestFactory) Validator(validateDirective string) (validation.Schema, error) {
return validation.NullSchema{}, nil
}
// OpenAPISchema returns openapi resources
func (f *TestFactory) OpenAPISchema() (openapi.Resources, error) {
if f.OpenAPISchemaFunc != nil {
return f.OpenAPISchemaFunc()
}
return openapitesting.EmptyResources{}, nil
}
func (f *TestFactory) OpenAPIV3Client() (openapiclient.Client, error) {
if f.OpenAPIV3ClientFunc != nil {
return f.OpenAPIV3ClientFunc()
}
return openapitest.NewFakeClient(), nil
}
// NewBuilder returns an initialized resource.Builder instance
func (f *TestFactory) NewBuilder() *resource.Builder {
return resource.NewFakeBuilder(
func(version schema.GroupVersion) (resource.RESTClient, error) {
if f.UnstructuredClientForMappingFunc != nil {
return f.UnstructuredClientForMappingFunc(version)
}
if f.UnstructuredClient != nil {
return f.UnstructuredClient, nil
}
return f.Client, nil
},
f.ToRESTMapper,
func() (restmapper.CategoryExpander, error) {
return resource.FakeCategoryExpander, nil
},
)
}
// KubernetesClientSet initializes and returns the Clientset using TestFactory
func (f *TestFactory) KubernetesClientSet() (*kubernetes.Clientset, error) {
fakeClient := f.Client.(*fake.RESTClient)
clientset := kubernetes.NewForConfigOrDie(f.ClientConfigVal)
clientset.CoreV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AuthenticationV1alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AutoscalingV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AutoscalingV2().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.BatchV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.CertificatesV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.CertificatesV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.ExtensionsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.RbacV1alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.RbacV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.StorageV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.StorageV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AppsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AppsV1beta2().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.AppsV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.PolicyV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.PolicyV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
clientset.DiscoveryClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
return clientset, nil
}
// DynamicClient returns a dynamic client from TestFactory
func (f *TestFactory) DynamicClient() (dynamic.Interface, error) {
if f.FakeDynamicClient != nil {
return f.FakeDynamicClient, nil
}
return f.Factory.DynamicClient()
}
// RESTClient returns a REST client from TestFactory
func (f *TestFactory) RESTClient() (*restclient.RESTClient, error) {
// Swap out the HTTP client out of the client with the fake's version.
fakeClient := f.Client.(*fake.RESTClient)
restClient, err := restclient.RESTClientFor(f.ClientConfigVal)
if err != nil {
panic(err)
}
restClient.Client = fakeClient.Client
return restClient, nil
}
// DiscoveryClient returns a discovery client from TestFactory
func (f *TestFactory) DiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
fakeClient := f.Client.(*fake.RESTClient)
cacheDir := filepath.Join("", ".kube", "cache", "discovery")
cachedClient, err := diskcached.NewCachedDiscoveryClientForConfig(f.ClientConfigVal, cacheDir, "", time.Duration(10*time.Minute))
if err != nil {
return nil, err
}
cachedClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
return cachedClient, nil
}
func testRESTMapper() meta.RESTMapper {
groupResources := testDynamicResources()
mapper := restmapper.NewDiscoveryRESTMapper(groupResources)
// for backwards compatibility with existing tests, allow rest mappings from the scheme to show up
// TODO: make this opt-in?
mapper = meta.FirstHitRESTMapper{
MultiRESTMapper: meta.MultiRESTMapper{
mapper,
testrestmapper.TestOnlyStaticRESTMapper(scheme.Scheme),
},
}
fakeDs := NewFakeCachedDiscoveryClient()
expander := restmapper.NewShortcutExpander(mapper, fakeDs, nil)
return expander
}
// ScaleClient returns the ScalesGetter from a TestFactory
func (f *TestFactory) ScaleClient() (scaleclient.ScalesGetter, error) {
return f.ScaleGetter, nil
}
func testDynamicResources() []*restmapper.APIGroupResources {
return []*restmapper.APIGroupResources{
{
Group: metav1.APIGroup{
Versions: []metav1.GroupVersionForDiscovery{
{Version: "v1"},
},
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
},
VersionedResources: map[string][]metav1.APIResource{
"v1": {
{Name: "pods", Namespaced: true, Kind: "Pod"},
{Name: "services", Namespaced: true, Kind: "Service"},
{Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
{Name: "componentstatuses", Namespaced: false, Kind: "ComponentStatus"},
{Name: "nodes", Namespaced: false, Kind: "Node"},
{Name: "secrets", Namespaced: true, Kind: "Secret"},
{Name: "configmaps", Namespaced: true, Kind: "ConfigMap"},
{Name: "namespacedtype", Namespaced: true, Kind: "NamespacedType"},
{Name: "namespaces", Namespaced: false, Kind: "Namespace"},
{Name: "resourcequotas", Namespaced: true, Kind: "ResourceQuota"},
},
},
},
{
Group: metav1.APIGroup{
Name: "extensions",
Versions: []metav1.GroupVersionForDiscovery{
{Version: "v1beta1"},
},
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1beta1"},
},
VersionedResources: map[string][]metav1.APIResource{
"v1beta1": {
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
{Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
},
},
},
{
Group: metav1.APIGroup{
Name: "apps",
Versions: []metav1.GroupVersionForDiscovery{
{Version: "v1beta1"},
{Version: "v1beta2"},
{Version: "v1"},
},
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
},
VersionedResources: map[string][]metav1.APIResource{
"v1beta1": {
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
{Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
},
"v1beta2": {
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
},
"v1": {
{Name: "deployments", Namespaced: true, Kind: "Deployment"},
{Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
},
},
},
{
Group: metav1.APIGroup{
Name: "batch",
Versions: []metav1.GroupVersionForDiscovery{
{Version: "v1beta1"},
{Version: "v1"},
},
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
},
VersionedResources: map[string][]metav1.APIResource{
"v1beta1": {
{Name: "cronjobs", Namespaced: true, Kind: "CronJob"},
},
"v1": {
{Name: "jobs", Namespaced: true, Kind: "Job"},
},
},
},
{
Group: metav1.APIGroup{
Name: "autoscaling",
Versions: []metav1.GroupVersionForDiscovery{
{Version: "v1"},
{Version: "v2"},
},
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v2"},
},
VersionedResources: map[string][]metav1.APIResource{
"v1": {
{Name: "horizontalpodautoscalers", Namespaced: true, Kind: "HorizontalPodAutoscaler"},
},
"v2": {
{Name: "horizontalpodautoscalers", Namespaced: true, Kind: "HorizontalPodAutoscaler"},
},
},
},
{
Group: metav1.APIGroup{
Name: "storage.k8s.io",
Versions: []metav1.GroupVersionForDiscovery{
{Version: "v1beta1"},
{Version: "v0"},
},
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1beta1"},
},
VersionedResources: map[string][]metav1.APIResource{
"v1beta1": {
{Name: "storageclasses", Namespaced: false, Kind: "StorageClass"},
},
// bogus version of a known group/version/resource to make sure kubectl falls back to generic object mode
"v0": {
{Name: "storageclasses", Namespaced: false, Kind: "StorageClass"},
},
},
},
{
Group: metav1.APIGroup{
Name: "rbac.authorization.k8s.io",
Versions: []metav1.GroupVersionForDiscovery{
{Version: "v1beta1"},
{Version: "v1"},
},
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
},
VersionedResources: map[string][]metav1.APIResource{
"v1": {
{Name: "clusterroles", Namespaced: false, Kind: "ClusterRole"},
},
"v1beta1": {
{Name: "clusterrolebindings", Namespaced: false, Kind: "ClusterRoleBinding"},
},
},
},
{
Group: metav1.APIGroup{
Name: "company.com",
Versions: []metav1.GroupVersionForDiscovery{
{Version: "v1"},
},
PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
},
VersionedResources: map[string][]metav1.APIResource{
"v1": {
{Name: "bars", Namespaced: true, Kind: "Bar"},
{Name: "applysets", Namespaced: false, Kind: "ApplySet"},
},
},
},
{
Group: metav1.APIGroup{
Name: "unit-test.test.com",
Versions: []metav1.GroupVersionForDiscovery{
{GroupVersion: "unit-test.test.com/v1", Version: "v1"},
},
PreferredVersion: metav1.GroupVersionForDiscovery{
GroupVersion: "unit-test.test.com/v1",
Version: "v1"},
},
VersionedResources: map[string][]metav1.APIResource{
"v1": {
{Name: "widgets", Namespaced: true, Kind: "Widget"},
},
},
},
{
Group: metav1.APIGroup{
Name: "apitest",
Versions: []metav1.GroupVersionForDiscovery{
{GroupVersion: "apitest/unlikelyversion", Version: "unlikelyversion"},
},
PreferredVersion: metav1.GroupVersionForDiscovery{
GroupVersion: "apitest/unlikelyversion",
Version: "unlikelyversion"},
},
VersionedResources: map[string][]metav1.APIResource{
"unlikelyversion": {
{Name: "types", SingularName: "type", Namespaced: false, Kind: "Type"},
},
},
},
}
}