...

Source file src/k8s.io/kubectl/pkg/cmd/testing/fake.go

Documentation: k8s.io/kubectl/pkg/cmd/testing

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package testing
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"os"
    23  	"path/filepath"
    24  	"time"
    25  
    26  	"k8s.io/apimachinery/pkg/api/meta"
    27  	"k8s.io/apimachinery/pkg/api/meta/testrestmapper"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/conversion"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"k8s.io/apimachinery/pkg/runtime/schema"
    32  	"k8s.io/apimachinery/pkg/runtime/serializer"
    33  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    34  	"k8s.io/cli-runtime/pkg/genericclioptions"
    35  	"k8s.io/cli-runtime/pkg/resource"
    36  	"k8s.io/client-go/discovery"
    37  	diskcached "k8s.io/client-go/discovery/cached/disk"
    38  	"k8s.io/client-go/dynamic"
    39  	fakedynamic "k8s.io/client-go/dynamic/fake"
    40  	"k8s.io/client-go/kubernetes"
    41  	openapiclient "k8s.io/client-go/openapi"
    42  	"k8s.io/client-go/openapi/openapitest"
    43  	restclient "k8s.io/client-go/rest"
    44  	"k8s.io/client-go/rest/fake"
    45  	"k8s.io/client-go/restmapper"
    46  	scaleclient "k8s.io/client-go/scale"
    47  	"k8s.io/client-go/tools/clientcmd"
    48  	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
    49  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    50  	"k8s.io/kubectl/pkg/scheme"
    51  	"k8s.io/kubectl/pkg/util/openapi"
    52  	openapitesting "k8s.io/kubectl/pkg/util/openapi/testing"
    53  	"k8s.io/kubectl/pkg/validation"
    54  )
    55  
    56  // InternalType is the schema for internal type
    57  // +k8s:deepcopy-gen=true
    58  // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
    59  type InternalType struct {
    60  	Kind       string
    61  	APIVersion string
    62  
    63  	Name string
    64  }
    65  
    66  // ExternalType is the schema for external type
    67  // +k8s:deepcopy-gen=true
    68  // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
    69  type ExternalType struct {
    70  	Kind       string `json:"kind"`
    71  	APIVersion string `json:"apiVersion"`
    72  
    73  	Name string `json:"name"`
    74  }
    75  
    76  // ExternalType2 is another schema for external type
    77  // +k8s:deepcopy-gen=true
    78  // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
    79  type ExternalType2 struct {
    80  	Kind       string `json:"kind"`
    81  	APIVersion string `json:"apiVersion"`
    82  
    83  	Name string `json:"name"`
    84  }
    85  
    86  // GetObjectKind returns the ObjectKind schema
    87  func (obj *InternalType) GetObjectKind() schema.ObjectKind { return obj }
    88  
    89  // SetGroupVersionKind sets the version and kind
    90  func (obj *InternalType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
    91  	obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
    92  }
    93  
    94  // GroupVersionKind returns GroupVersionKind schema
    95  func (obj *InternalType) GroupVersionKind() schema.GroupVersionKind {
    96  	return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
    97  }
    98  
    99  // GetObjectKind returns the ObjectKind schema
   100  func (obj *ExternalType) GetObjectKind() schema.ObjectKind { return obj }
   101  
   102  // SetGroupVersionKind returns the GroupVersionKind schema
   103  func (obj *ExternalType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
   104  	obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
   105  }
   106  
   107  // GroupVersionKind returns the GroupVersionKind schema
   108  func (obj *ExternalType) GroupVersionKind() schema.GroupVersionKind {
   109  	return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
   110  }
   111  
   112  // GetObjectKind returns the ObjectKind schema
   113  func (obj *ExternalType2) GetObjectKind() schema.ObjectKind { return obj }
   114  
   115  // SetGroupVersionKind sets the API version and obj kind from schema
   116  func (obj *ExternalType2) SetGroupVersionKind(gvk schema.GroupVersionKind) {
   117  	obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
   118  }
   119  
   120  // GroupVersionKind returns the FromAPIVersionAndKind schema
   121  func (obj *ExternalType2) GroupVersionKind() schema.GroupVersionKind {
   122  	return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
   123  }
   124  
   125  // NewInternalType returns an initialized InternalType instance
   126  func NewInternalType(kind, apiversion, name string) *InternalType {
   127  	item := InternalType{Kind: kind,
   128  		APIVersion: apiversion,
   129  		Name:       name}
   130  	return &item
   131  }
   132  
   133  func convertInternalTypeToExternalType(in *InternalType, out *ExternalType, s conversion.Scope) error {
   134  	out.Kind = in.Kind
   135  	out.APIVersion = in.APIVersion
   136  	out.Name = in.Name
   137  	return nil
   138  }
   139  
   140  func convertInternalTypeToExternalType2(in *InternalType, out *ExternalType2, s conversion.Scope) error {
   141  	out.Kind = in.Kind
   142  	out.APIVersion = in.APIVersion
   143  	out.Name = in.Name
   144  	return nil
   145  }
   146  
   147  func convertExternalTypeToInternalType(in *ExternalType, out *InternalType, s conversion.Scope) error {
   148  	out.Kind = in.Kind
   149  	out.APIVersion = in.APIVersion
   150  	out.Name = in.Name
   151  	return nil
   152  }
   153  
   154  func convertExternalType2ToInternalType(in *ExternalType2, out *InternalType, s conversion.Scope) error {
   155  	out.Kind = in.Kind
   156  	out.APIVersion = in.APIVersion
   157  	out.Name = in.Name
   158  	return nil
   159  }
   160  
   161  // InternalNamespacedType schema for internal namespaced types
   162  // +k8s:deepcopy-gen=true
   163  // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
   164  type InternalNamespacedType struct {
   165  	Kind       string
   166  	APIVersion string
   167  
   168  	Name      string
   169  	Namespace string
   170  }
   171  
   172  // ExternalNamespacedType schema for external namespaced types
   173  // +k8s:deepcopy-gen=true
   174  // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
   175  type ExternalNamespacedType struct {
   176  	Kind       string `json:"kind"`
   177  	APIVersion string `json:"apiVersion"`
   178  
   179  	Name      string `json:"name"`
   180  	Namespace string `json:"namespace"`
   181  }
   182  
   183  // ExternalNamespacedType2 schema for external namespaced types
   184  // +k8s:deepcopy-gen=true
   185  // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
   186  type ExternalNamespacedType2 struct {
   187  	Kind       string `json:"kind"`
   188  	APIVersion string `json:"apiVersion"`
   189  
   190  	Name      string `json:"name"`
   191  	Namespace string `json:"namespace"`
   192  }
   193  
   194  // GetObjectKind returns the ObjectKind schema
   195  func (obj *InternalNamespacedType) GetObjectKind() schema.ObjectKind { return obj }
   196  
   197  // SetGroupVersionKind sets the API group and kind from schema
   198  func (obj *InternalNamespacedType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
   199  	obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
   200  }
   201  
   202  // GroupVersionKind returns the GroupVersionKind schema
   203  func (obj *InternalNamespacedType) GroupVersionKind() schema.GroupVersionKind {
   204  	return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
   205  }
   206  
   207  // GetObjectKind returns the ObjectKind schema
   208  func (obj *ExternalNamespacedType) GetObjectKind() schema.ObjectKind { return obj }
   209  
   210  // SetGroupVersionKind sets the API version and kind from schema
   211  func (obj *ExternalNamespacedType) SetGroupVersionKind(gvk schema.GroupVersionKind) {
   212  	obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
   213  }
   214  
   215  // GroupVersionKind returns the GroupVersionKind schema
   216  func (obj *ExternalNamespacedType) GroupVersionKind() schema.GroupVersionKind {
   217  	return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
   218  }
   219  
   220  // GetObjectKind returns the ObjectKind schema
   221  func (obj *ExternalNamespacedType2) GetObjectKind() schema.ObjectKind { return obj }
   222  
   223  // SetGroupVersionKind sets the API version and kind from schema
   224  func (obj *ExternalNamespacedType2) SetGroupVersionKind(gvk schema.GroupVersionKind) {
   225  	obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
   226  }
   227  
   228  // GroupVersionKind returns the GroupVersionKind schema
   229  func (obj *ExternalNamespacedType2) GroupVersionKind() schema.GroupVersionKind {
   230  	return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
   231  }
   232  
   233  // NewInternalNamespacedType returns an initialized instance of InternalNamespacedType
   234  func NewInternalNamespacedType(kind, apiversion, name, namespace string) *InternalNamespacedType {
   235  	item := InternalNamespacedType{Kind: kind,
   236  		APIVersion: apiversion,
   237  		Name:       name,
   238  		Namespace:  namespace}
   239  	return &item
   240  }
   241  
   242  func convertInternalNamespacedTypeToExternalNamespacedType(in *InternalNamespacedType, out *ExternalNamespacedType, s conversion.Scope) error {
   243  	out.Kind = in.Kind
   244  	out.APIVersion = in.APIVersion
   245  	out.Name = in.Name
   246  	out.Namespace = in.Namespace
   247  	return nil
   248  }
   249  
   250  func convertInternalNamespacedTypeToExternalNamespacedType2(in *InternalNamespacedType, out *ExternalNamespacedType2, s conversion.Scope) error {
   251  	out.Kind = in.Kind
   252  	out.APIVersion = in.APIVersion
   253  	out.Name = in.Name
   254  	out.Namespace = in.Namespace
   255  	return nil
   256  }
   257  
   258  func convertExternalNamespacedTypeToInternalNamespacedType(in *ExternalNamespacedType, out *InternalNamespacedType, s conversion.Scope) error {
   259  	out.Kind = in.Kind
   260  	out.APIVersion = in.APIVersion
   261  	out.Name = in.Name
   262  	out.Namespace = in.Namespace
   263  	return nil
   264  }
   265  
   266  func convertExternalNamespacedType2ToInternalNamespacedType(in *ExternalNamespacedType2, out *InternalNamespacedType, s conversion.Scope) error {
   267  	out.Kind = in.Kind
   268  	out.APIVersion = in.APIVersion
   269  	out.Name = in.Name
   270  	out.Namespace = in.Namespace
   271  	return nil
   272  }
   273  
   274  // ValidVersion of API
   275  var ValidVersion = "v1"
   276  
   277  // InternalGV is the internal group version object
   278  var InternalGV = schema.GroupVersion{Group: "apitest", Version: runtime.APIVersionInternal}
   279  
   280  // UnlikelyGV is a group version object for unrecognised version
   281  var UnlikelyGV = schema.GroupVersion{Group: "apitest", Version: "unlikelyversion"}
   282  
   283  // ValidVersionGV is the valid group version object
   284  var ValidVersionGV = schema.GroupVersion{Group: "apitest", Version: ValidVersion}
   285  
   286  // NewExternalScheme returns required objects for ExternalScheme
   287  func NewExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
   288  	scheme := runtime.NewScheme()
   289  	mapper, codec := AddToScheme(scheme)
   290  	return scheme, mapper, codec
   291  }
   292  
   293  func registerConversions(s *runtime.Scheme) error {
   294  	if err := s.AddConversionFunc((*InternalType)(nil), (*ExternalType)(nil), func(a, b interface{}, scope conversion.Scope) error {
   295  		return convertInternalTypeToExternalType(a.(*InternalType), b.(*ExternalType), scope)
   296  	}); err != nil {
   297  		return err
   298  	}
   299  	if err := s.AddConversionFunc((*InternalType)(nil), (*ExternalType2)(nil), func(a, b interface{}, scope conversion.Scope) error {
   300  		return convertInternalTypeToExternalType2(a.(*InternalType), b.(*ExternalType2), scope)
   301  	}); err != nil {
   302  		return err
   303  	}
   304  	if err := s.AddConversionFunc((*ExternalType)(nil), (*InternalType)(nil), func(a, b interface{}, scope conversion.Scope) error {
   305  		return convertExternalTypeToInternalType(a.(*ExternalType), b.(*InternalType), scope)
   306  	}); err != nil {
   307  		return err
   308  	}
   309  	if err := s.AddConversionFunc((*ExternalType2)(nil), (*InternalType)(nil), func(a, b interface{}, scope conversion.Scope) error {
   310  		return convertExternalType2ToInternalType(a.(*ExternalType2), b.(*InternalType), scope)
   311  	}); err != nil {
   312  		return err
   313  	}
   314  	if err := s.AddConversionFunc((*InternalNamespacedType)(nil), (*ExternalNamespacedType)(nil), func(a, b interface{}, scope conversion.Scope) error {
   315  		return convertInternalNamespacedTypeToExternalNamespacedType(a.(*InternalNamespacedType), b.(*ExternalNamespacedType), scope)
   316  	}); err != nil {
   317  		return err
   318  	}
   319  	if err := s.AddConversionFunc((*InternalNamespacedType)(nil), (*ExternalNamespacedType2)(nil), func(a, b interface{}, scope conversion.Scope) error {
   320  		return convertInternalNamespacedTypeToExternalNamespacedType2(a.(*InternalNamespacedType), b.(*ExternalNamespacedType2), scope)
   321  	}); err != nil {
   322  		return err
   323  	}
   324  	if err := s.AddConversionFunc((*ExternalNamespacedType)(nil), (*InternalNamespacedType)(nil), func(a, b interface{}, scope conversion.Scope) error {
   325  		return convertExternalNamespacedTypeToInternalNamespacedType(a.(*ExternalNamespacedType), b.(*InternalNamespacedType), scope)
   326  	}); err != nil {
   327  		return err
   328  	}
   329  	if err := s.AddConversionFunc((*ExternalNamespacedType2)(nil), (*InternalNamespacedType)(nil), func(a, b interface{}, scope conversion.Scope) error {
   330  		return convertExternalNamespacedType2ToInternalNamespacedType(a.(*ExternalNamespacedType2), b.(*InternalNamespacedType), scope)
   331  	}); err != nil {
   332  		return err
   333  	}
   334  	return nil
   335  }
   336  
   337  // AddToScheme adds required objects into scheme
   338  func AddToScheme(scheme *runtime.Scheme) (meta.RESTMapper, runtime.Codec) {
   339  	scheme.AddKnownTypeWithName(InternalGV.WithKind("Type"), &InternalType{})
   340  	scheme.AddKnownTypeWithName(UnlikelyGV.WithKind("Type"), &ExternalType{})
   341  	// This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
   342  	scheme.AddKnownTypeWithName(ValidVersionGV.WithKind("Type"), &ExternalType2{})
   343  
   344  	scheme.AddKnownTypeWithName(InternalGV.WithKind("NamespacedType"), &InternalNamespacedType{})
   345  	scheme.AddKnownTypeWithName(UnlikelyGV.WithKind("NamespacedType"), &ExternalNamespacedType{})
   346  	// This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
   347  	scheme.AddKnownTypeWithName(ValidVersionGV.WithKind("NamespacedType"), &ExternalNamespacedType2{})
   348  
   349  	utilruntime.Must(registerConversions(scheme))
   350  
   351  	codecs := serializer.NewCodecFactory(scheme)
   352  	codec := codecs.LegacyCodec(UnlikelyGV)
   353  	mapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{UnlikelyGV, ValidVersionGV})
   354  	for _, gv := range []schema.GroupVersion{UnlikelyGV, ValidVersionGV} {
   355  		for kind := range scheme.KnownTypes(gv) {
   356  			gvk := gv.WithKind(kind)
   357  
   358  			scope := meta.RESTScopeNamespace
   359  			mapper.Add(gvk, scope)
   360  		}
   361  	}
   362  
   363  	return mapper, codec
   364  }
   365  
   366  type FakeCachedDiscoveryClient struct {
   367  	discovery.DiscoveryInterface
   368  	Groups             []*metav1.APIGroup
   369  	Resources          []*metav1.APIResourceList
   370  	PreferredResources []*metav1.APIResourceList
   371  	Invalidations      int
   372  }
   373  
   374  func NewFakeCachedDiscoveryClient() *FakeCachedDiscoveryClient {
   375  	return &FakeCachedDiscoveryClient{
   376  		Groups:             []*metav1.APIGroup{},
   377  		Resources:          []*metav1.APIResourceList{},
   378  		PreferredResources: []*metav1.APIResourceList{},
   379  		Invalidations:      0,
   380  	}
   381  }
   382  
   383  func (d *FakeCachedDiscoveryClient) Fresh() bool {
   384  	return true
   385  }
   386  
   387  func (d *FakeCachedDiscoveryClient) Invalidate() {
   388  	d.Invalidations++
   389  }
   390  
   391  func (d *FakeCachedDiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
   392  	return d.Groups, d.Resources, nil
   393  }
   394  
   395  func (d *FakeCachedDiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) {
   396  	groupList := &metav1.APIGroupList{Groups: []metav1.APIGroup{}}
   397  	for _, g := range d.Groups {
   398  		groupList.Groups = append(groupList.Groups, *g)
   399  	}
   400  	return groupList, nil
   401  }
   402  
   403  func (d *FakeCachedDiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) {
   404  	return d.PreferredResources, nil
   405  }
   406  
   407  // TestFactory extends cmdutil.Factory
   408  type TestFactory struct {
   409  	cmdutil.Factory
   410  
   411  	kubeConfigFlags *genericclioptions.TestConfigFlags
   412  
   413  	Client             RESTClient
   414  	ScaleGetter        scaleclient.ScalesGetter
   415  	UnstructuredClient RESTClient
   416  	ClientConfigVal    *restclient.Config
   417  	FakeDynamicClient  *fakedynamic.FakeDynamicClient
   418  
   419  	tempConfigFile *os.File
   420  
   421  	UnstructuredClientForMappingFunc resource.FakeClientFunc
   422  	OpenAPISchemaFunc                func() (openapi.Resources, error)
   423  	OpenAPIV3ClientFunc              func() (openapiclient.Client, error)
   424  }
   425  
   426  // NewTestFactory returns an initialized TestFactory instance
   427  func NewTestFactory() *TestFactory {
   428  	// specify an optionalClientConfig to explicitly use in testing
   429  	// to avoid polluting an existing user config.
   430  	tmpFile, err := os.CreateTemp(os.TempDir(), "cmdtests_temp")
   431  	if err != nil {
   432  		panic(fmt.Sprintf("unable to create a fake client config: %v", err))
   433  	}
   434  
   435  	loadingRules := &clientcmd.ClientConfigLoadingRules{
   436  		Precedence:     []string{tmpFile.Name()},
   437  		MigrationRules: map[string]string{},
   438  	}
   439  
   440  	overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmdapi.Cluster{Server: "http://localhost:8080"}}
   441  	fallbackReader := bytes.NewBuffer([]byte{})
   442  	clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, fallbackReader)
   443  
   444  	configFlags := genericclioptions.NewTestConfigFlags().
   445  		WithClientConfig(clientConfig).
   446  		WithRESTMapper(testRESTMapper())
   447  
   448  	restConfig, err := clientConfig.ClientConfig()
   449  	if err != nil {
   450  		panic(fmt.Sprintf("unable to create a fake restclient config: %v", err))
   451  	}
   452  
   453  	return &TestFactory{
   454  		Factory:           cmdutil.NewFactory(configFlags),
   455  		kubeConfigFlags:   configFlags,
   456  		FakeDynamicClient: fakedynamic.NewSimpleDynamicClient(scheme.Scheme),
   457  		tempConfigFile:    tmpFile,
   458  
   459  		ClientConfigVal: restConfig,
   460  	}
   461  }
   462  
   463  // WithNamespace is used to mention namespace reactively
   464  func (f *TestFactory) WithNamespace(ns string) *TestFactory {
   465  	f.kubeConfigFlags.WithNamespace(ns)
   466  	return f
   467  }
   468  
   469  // WithClientConfig sets the client config to use
   470  func (f *TestFactory) WithClientConfig(clientConfig clientcmd.ClientConfig) *TestFactory {
   471  	f.kubeConfigFlags.WithClientConfig(clientConfig)
   472  	return f
   473  }
   474  
   475  func (f *TestFactory) WithDiscoveryClient(discoveryClient discovery.CachedDiscoveryInterface) *TestFactory {
   476  	f.kubeConfigFlags.WithDiscoveryClient(discoveryClient)
   477  	return f
   478  }
   479  
   480  // Cleanup cleans up TestFactory temp config file
   481  func (f *TestFactory) Cleanup() {
   482  	if f.tempConfigFile == nil {
   483  		return
   484  	}
   485  
   486  	f.tempConfigFile.Close()
   487  	os.Remove(f.tempConfigFile.Name())
   488  }
   489  
   490  // ToRESTConfig is used to get ClientConfigVal from a TestFactory
   491  func (f *TestFactory) ToRESTConfig() (*restclient.Config, error) {
   492  	return f.ClientConfigVal, nil
   493  }
   494  
   495  // ClientForMapping is used to Client from a TestFactory
   496  func (f *TestFactory) ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
   497  	return f.Client, nil
   498  }
   499  
   500  // PathOptions returns a new PathOptions with a temp file
   501  func (f *TestFactory) PathOptions() *clientcmd.PathOptions {
   502  	pathOptions := clientcmd.NewDefaultPathOptions()
   503  	pathOptions.GlobalFile = f.tempConfigFile.Name()
   504  	pathOptions.EnvVar = ""
   505  	return pathOptions
   506  }
   507  
   508  // PathOptionsWithConfig writes a config to a temp file and returns PathOptions
   509  func (f *TestFactory) PathOptionsWithConfig(config clientcmdapi.Config) (*clientcmd.PathOptions, error) {
   510  	pathOptions := f.PathOptions()
   511  	err := clientcmd.WriteToFile(config, pathOptions.GlobalFile)
   512  	if err != nil {
   513  		return nil, err
   514  	}
   515  
   516  	return pathOptions, nil
   517  }
   518  
   519  // UnstructuredClientForMapping is used to get UnstructuredClient from a TestFactory
   520  func (f *TestFactory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
   521  	if f.UnstructuredClientForMappingFunc != nil {
   522  		return f.UnstructuredClientForMappingFunc(mapping.GroupVersionKind.GroupVersion())
   523  	}
   524  	return f.UnstructuredClient, nil
   525  }
   526  
   527  // Validator returns a validation schema
   528  func (f *TestFactory) Validator(validateDirective string) (validation.Schema, error) {
   529  	return validation.NullSchema{}, nil
   530  }
   531  
   532  // OpenAPISchema returns openapi resources
   533  func (f *TestFactory) OpenAPISchema() (openapi.Resources, error) {
   534  	if f.OpenAPISchemaFunc != nil {
   535  		return f.OpenAPISchemaFunc()
   536  	}
   537  	return openapitesting.EmptyResources{}, nil
   538  }
   539  
   540  func (f *TestFactory) OpenAPIV3Client() (openapiclient.Client, error) {
   541  	if f.OpenAPIV3ClientFunc != nil {
   542  		return f.OpenAPIV3ClientFunc()
   543  	}
   544  	return openapitest.NewFakeClient(), nil
   545  }
   546  
   547  // NewBuilder returns an initialized resource.Builder instance
   548  func (f *TestFactory) NewBuilder() *resource.Builder {
   549  	return resource.NewFakeBuilder(
   550  		func(version schema.GroupVersion) (resource.RESTClient, error) {
   551  			if f.UnstructuredClientForMappingFunc != nil {
   552  				return f.UnstructuredClientForMappingFunc(version)
   553  			}
   554  			if f.UnstructuredClient != nil {
   555  				return f.UnstructuredClient, nil
   556  			}
   557  			return f.Client, nil
   558  		},
   559  		f.ToRESTMapper,
   560  		func() (restmapper.CategoryExpander, error) {
   561  			return resource.FakeCategoryExpander, nil
   562  		},
   563  	)
   564  }
   565  
   566  // KubernetesClientSet initializes and returns the Clientset using TestFactory
   567  func (f *TestFactory) KubernetesClientSet() (*kubernetes.Clientset, error) {
   568  	fakeClient := f.Client.(*fake.RESTClient)
   569  	clientset := kubernetes.NewForConfigOrDie(f.ClientConfigVal)
   570  
   571  	clientset.CoreV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   572  	clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   573  	clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   574  	clientset.AuthorizationV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   575  	clientset.AuthorizationV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   576  	clientset.AuthenticationV1alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   577  	clientset.AutoscalingV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   578  	clientset.AutoscalingV2().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   579  	clientset.BatchV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   580  	clientset.CertificatesV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   581  	clientset.CertificatesV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   582  	clientset.ExtensionsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   583  	clientset.RbacV1alpha1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   584  	clientset.RbacV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   585  	clientset.StorageV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   586  	clientset.StorageV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   587  	clientset.AppsV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   588  	clientset.AppsV1beta2().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   589  	clientset.AppsV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   590  	clientset.PolicyV1beta1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   591  	clientset.PolicyV1().RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   592  	clientset.DiscoveryClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   593  
   594  	return clientset, nil
   595  }
   596  
   597  // DynamicClient returns a dynamic client from TestFactory
   598  func (f *TestFactory) DynamicClient() (dynamic.Interface, error) {
   599  	if f.FakeDynamicClient != nil {
   600  		return f.FakeDynamicClient, nil
   601  	}
   602  	return f.Factory.DynamicClient()
   603  }
   604  
   605  // RESTClient returns a REST client from TestFactory
   606  func (f *TestFactory) RESTClient() (*restclient.RESTClient, error) {
   607  	// Swap out the HTTP client out of the client with the fake's version.
   608  	fakeClient := f.Client.(*fake.RESTClient)
   609  	restClient, err := restclient.RESTClientFor(f.ClientConfigVal)
   610  	if err != nil {
   611  		panic(err)
   612  	}
   613  	restClient.Client = fakeClient.Client
   614  	return restClient, nil
   615  }
   616  
   617  // DiscoveryClient returns a discovery client from TestFactory
   618  func (f *TestFactory) DiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
   619  	fakeClient := f.Client.(*fake.RESTClient)
   620  
   621  	cacheDir := filepath.Join("", ".kube", "cache", "discovery")
   622  	cachedClient, err := diskcached.NewCachedDiscoveryClientForConfig(f.ClientConfigVal, cacheDir, "", time.Duration(10*time.Minute))
   623  	if err != nil {
   624  		return nil, err
   625  	}
   626  	cachedClient.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client
   627  
   628  	return cachedClient, nil
   629  }
   630  
   631  func testRESTMapper() meta.RESTMapper {
   632  	groupResources := testDynamicResources()
   633  	mapper := restmapper.NewDiscoveryRESTMapper(groupResources)
   634  	// for backwards compatibility with existing tests, allow rest mappings from the scheme to show up
   635  	// TODO: make this opt-in?
   636  	mapper = meta.FirstHitRESTMapper{
   637  		MultiRESTMapper: meta.MultiRESTMapper{
   638  			mapper,
   639  			testrestmapper.TestOnlyStaticRESTMapper(scheme.Scheme),
   640  		},
   641  	}
   642  
   643  	fakeDs := NewFakeCachedDiscoveryClient()
   644  	expander := restmapper.NewShortcutExpander(mapper, fakeDs, nil)
   645  	return expander
   646  }
   647  
   648  // ScaleClient returns the ScalesGetter from a TestFactory
   649  func (f *TestFactory) ScaleClient() (scaleclient.ScalesGetter, error) {
   650  	return f.ScaleGetter, nil
   651  }
   652  
   653  func testDynamicResources() []*restmapper.APIGroupResources {
   654  	return []*restmapper.APIGroupResources{
   655  		{
   656  			Group: metav1.APIGroup{
   657  				Versions: []metav1.GroupVersionForDiscovery{
   658  					{Version: "v1"},
   659  				},
   660  				PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
   661  			},
   662  			VersionedResources: map[string][]metav1.APIResource{
   663  				"v1": {
   664  					{Name: "pods", Namespaced: true, Kind: "Pod"},
   665  					{Name: "services", Namespaced: true, Kind: "Service"},
   666  					{Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
   667  					{Name: "componentstatuses", Namespaced: false, Kind: "ComponentStatus"},
   668  					{Name: "nodes", Namespaced: false, Kind: "Node"},
   669  					{Name: "secrets", Namespaced: true, Kind: "Secret"},
   670  					{Name: "configmaps", Namespaced: true, Kind: "ConfigMap"},
   671  					{Name: "namespacedtype", Namespaced: true, Kind: "NamespacedType"},
   672  					{Name: "namespaces", Namespaced: false, Kind: "Namespace"},
   673  					{Name: "resourcequotas", Namespaced: true, Kind: "ResourceQuota"},
   674  				},
   675  			},
   676  		},
   677  		{
   678  			Group: metav1.APIGroup{
   679  				Name: "extensions",
   680  				Versions: []metav1.GroupVersionForDiscovery{
   681  					{Version: "v1beta1"},
   682  				},
   683  				PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1beta1"},
   684  			},
   685  			VersionedResources: map[string][]metav1.APIResource{
   686  				"v1beta1": {
   687  					{Name: "deployments", Namespaced: true, Kind: "Deployment"},
   688  					{Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
   689  				},
   690  			},
   691  		},
   692  		{
   693  			Group: metav1.APIGroup{
   694  				Name: "apps",
   695  				Versions: []metav1.GroupVersionForDiscovery{
   696  					{Version: "v1beta1"},
   697  					{Version: "v1beta2"},
   698  					{Version: "v1"},
   699  				},
   700  				PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
   701  			},
   702  			VersionedResources: map[string][]metav1.APIResource{
   703  				"v1beta1": {
   704  					{Name: "deployments", Namespaced: true, Kind: "Deployment"},
   705  					{Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
   706  				},
   707  				"v1beta2": {
   708  					{Name: "deployments", Namespaced: true, Kind: "Deployment"},
   709  				},
   710  				"v1": {
   711  					{Name: "deployments", Namespaced: true, Kind: "Deployment"},
   712  					{Name: "replicasets", Namespaced: true, Kind: "ReplicaSet"},
   713  				},
   714  			},
   715  		},
   716  		{
   717  			Group: metav1.APIGroup{
   718  				Name: "batch",
   719  				Versions: []metav1.GroupVersionForDiscovery{
   720  					{Version: "v1beta1"},
   721  					{Version: "v1"},
   722  				},
   723  				PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
   724  			},
   725  			VersionedResources: map[string][]metav1.APIResource{
   726  				"v1beta1": {
   727  					{Name: "cronjobs", Namespaced: true, Kind: "CronJob"},
   728  				},
   729  				"v1": {
   730  					{Name: "jobs", Namespaced: true, Kind: "Job"},
   731  				},
   732  			},
   733  		},
   734  		{
   735  			Group: metav1.APIGroup{
   736  				Name: "autoscaling",
   737  				Versions: []metav1.GroupVersionForDiscovery{
   738  					{Version: "v1"},
   739  					{Version: "v2"},
   740  				},
   741  				PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v2"},
   742  			},
   743  			VersionedResources: map[string][]metav1.APIResource{
   744  				"v1": {
   745  					{Name: "horizontalpodautoscalers", Namespaced: true, Kind: "HorizontalPodAutoscaler"},
   746  				},
   747  				"v2": {
   748  					{Name: "horizontalpodautoscalers", Namespaced: true, Kind: "HorizontalPodAutoscaler"},
   749  				},
   750  			},
   751  		},
   752  		{
   753  			Group: metav1.APIGroup{
   754  				Name: "storage.k8s.io",
   755  				Versions: []metav1.GroupVersionForDiscovery{
   756  					{Version: "v1beta1"},
   757  					{Version: "v0"},
   758  				},
   759  				PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1beta1"},
   760  			},
   761  			VersionedResources: map[string][]metav1.APIResource{
   762  				"v1beta1": {
   763  					{Name: "storageclasses", Namespaced: false, Kind: "StorageClass"},
   764  				},
   765  				// bogus version of a known group/version/resource to make sure kubectl falls back to generic object mode
   766  				"v0": {
   767  					{Name: "storageclasses", Namespaced: false, Kind: "StorageClass"},
   768  				},
   769  			},
   770  		},
   771  		{
   772  			Group: metav1.APIGroup{
   773  				Name: "rbac.authorization.k8s.io",
   774  				Versions: []metav1.GroupVersionForDiscovery{
   775  					{Version: "v1beta1"},
   776  					{Version: "v1"},
   777  				},
   778  				PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
   779  			},
   780  			VersionedResources: map[string][]metav1.APIResource{
   781  				"v1": {
   782  					{Name: "clusterroles", Namespaced: false, Kind: "ClusterRole"},
   783  				},
   784  				"v1beta1": {
   785  					{Name: "clusterrolebindings", Namespaced: false, Kind: "ClusterRoleBinding"},
   786  				},
   787  			},
   788  		},
   789  		{
   790  			Group: metav1.APIGroup{
   791  				Name: "company.com",
   792  				Versions: []metav1.GroupVersionForDiscovery{
   793  					{Version: "v1"},
   794  				},
   795  				PreferredVersion: metav1.GroupVersionForDiscovery{Version: "v1"},
   796  			},
   797  			VersionedResources: map[string][]metav1.APIResource{
   798  				"v1": {
   799  					{Name: "bars", Namespaced: true, Kind: "Bar"},
   800  					{Name: "applysets", Namespaced: false, Kind: "ApplySet"},
   801  				},
   802  			},
   803  		},
   804  		{
   805  			Group: metav1.APIGroup{
   806  				Name: "unit-test.test.com",
   807  				Versions: []metav1.GroupVersionForDiscovery{
   808  					{GroupVersion: "unit-test.test.com/v1", Version: "v1"},
   809  				},
   810  				PreferredVersion: metav1.GroupVersionForDiscovery{
   811  					GroupVersion: "unit-test.test.com/v1",
   812  					Version:      "v1"},
   813  			},
   814  			VersionedResources: map[string][]metav1.APIResource{
   815  				"v1": {
   816  					{Name: "widgets", Namespaced: true, Kind: "Widget"},
   817  				},
   818  			},
   819  		},
   820  		{
   821  			Group: metav1.APIGroup{
   822  				Name: "apitest",
   823  				Versions: []metav1.GroupVersionForDiscovery{
   824  					{GroupVersion: "apitest/unlikelyversion", Version: "unlikelyversion"},
   825  				},
   826  				PreferredVersion: metav1.GroupVersionForDiscovery{
   827  					GroupVersion: "apitest/unlikelyversion",
   828  					Version:      "unlikelyversion"},
   829  			},
   830  			VersionedResources: map[string][]metav1.APIResource{
   831  				"unlikelyversion": {
   832  					{Name: "types", SingularName: "type", Namespaced: false, Kind: "Type"},
   833  				},
   834  			},
   835  		},
   836  	}
   837  }
   838  

View as plain text