...

Source file src/kubevirt.io/client-go/kubecli/kubecli.go

Documentation: kubevirt.io/client-go/kubecli

     1  /*
     2   * This file is part of the KubeVirt project
     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   * Copyright 2018 Red Hat, Inc.
    17   * Copyright 2018 The Kubernetes Authors.
    18   *
    19   */
    20  
    21  package kubecli
    22  
    23  import (
    24  	"flag"
    25  	"os"
    26  	"sync"
    27  
    28  	routev1 "github.com/openshift/client-go/route/clientset/versioned/typed/route/v1"
    29  
    30  	clonev1alpha1 "kubevirt.io/client-go/generated/kubevirt/clientset/versioned/typed/clone/v1alpha1"
    31  
    32  	secv1 "github.com/openshift/client-go/security/clientset/versioned/typed/security/v1"
    33  	"github.com/spf13/pflag"
    34  	extclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
    35  	"k8s.io/apimachinery/pkg/runtime"
    36  	"k8s.io/apimachinery/pkg/runtime/schema"
    37  	"k8s.io/apimachinery/pkg/runtime/serializer"
    38  	"k8s.io/client-go/discovery"
    39  	"k8s.io/client-go/dynamic"
    40  	"k8s.io/client-go/kubernetes"
    41  	"k8s.io/client-go/kubernetes/scheme"
    42  	"k8s.io/client-go/rest"
    43  	restclient "k8s.io/client-go/rest"
    44  	"k8s.io/client-go/tools/clientcmd"
    45  
    46  	"kubevirt.io/api/core"
    47  	v1 "kubevirt.io/api/core/v1"
    48  	cdiclient "kubevirt.io/client-go/generated/containerized-data-importer/clientset/versioned"
    49  	k8ssnapshotclient "kubevirt.io/client-go/generated/external-snapshotter/clientset/versioned"
    50  	generatedclient "kubevirt.io/client-go/generated/kubevirt/clientset/versioned"
    51  	migrationsv1 "kubevirt.io/client-go/generated/kubevirt/clientset/versioned/typed/migrations/v1alpha1"
    52  	networkclient "kubevirt.io/client-go/generated/network-attachment-definition-client/clientset/versioned"
    53  	promclient "kubevirt.io/client-go/generated/prometheus-operator/clientset/versioned"
    54  )
    55  
    56  var (
    57  	kubeconfig string
    58  	master     string
    59  )
    60  
    61  var (
    62  	SchemeBuilder  runtime.SchemeBuilder
    63  	Scheme         *runtime.Scheme
    64  	Codecs         serializer.CodecFactory
    65  	ParameterCodec runtime.ParameterCodec
    66  )
    67  
    68  func init() {
    69  	// This allows consumers of the KubeVirt client go package to
    70  	// customize what version the client uses. Without specifying a
    71  	// version, all versions are registered. While this techincally
    72  	// file to register all versions, so k8s ecosystem libraries
    73  	// do not work well with this. By explicitly setting the env var,
    74  	// consumers of our client go can avoid these scenarios by only
    75  	// registering a single version
    76  	registerVersion := os.Getenv(v1.KubeVirtClientGoSchemeRegistrationVersionEnvVar)
    77  	if registerVersion != "" {
    78  		SchemeBuilder = runtime.NewSchemeBuilder(v1.AddKnownTypesGenerator([]schema.GroupVersion{schema.GroupVersion{Group: core.GroupName, Version: registerVersion}}))
    79  	} else {
    80  		SchemeBuilder = runtime.NewSchemeBuilder(v1.AddKnownTypesGenerator(v1.GroupVersions))
    81  	}
    82  	Scheme = runtime.NewScheme()
    83  	AddToScheme := SchemeBuilder.AddToScheme
    84  	Codecs = serializer.NewCodecFactory(Scheme)
    85  	ParameterCodec = runtime.NewParameterCodec(Scheme)
    86  	AddToScheme(Scheme)
    87  	AddToScheme(scheme.Scheme)
    88  }
    89  
    90  type RestConfigHookFunc func(*rest.Config)
    91  
    92  var restConfigHooks []RestConfigHookFunc
    93  var restConfigHooksLock sync.Mutex
    94  
    95  var virtclient KubevirtClient
    96  var once sync.Once
    97  
    98  // Init adds the default `kubeconfig` and `master` flags. It is not added by default to allow integration into
    99  // the different controller generators which normally add these flags too.
   100  func Init() {
   101  	if flag.CommandLine.Lookup("kubeconfig") == nil {
   102  		flag.StringVar(&kubeconfig, "kubeconfig", "", "absolute path to the kubeconfig file")
   103  	}
   104  	if flag.CommandLine.Lookup("master") == nil {
   105  		flag.StringVar(&master, "master", "", "master url")
   106  	}
   107  }
   108  
   109  func RegisterRestConfigHook(fn RestConfigHookFunc) {
   110  	restConfigHooksLock.Lock()
   111  	defer restConfigHooksLock.Unlock()
   112  
   113  	restConfigHooks = append(restConfigHooks, fn)
   114  }
   115  
   116  func executeRestConfigHooks(config *rest.Config) {
   117  	restConfigHooksLock.Lock()
   118  	defer restConfigHooksLock.Unlock()
   119  
   120  	for _, hookFn := range restConfigHooks {
   121  		hookFn(config)
   122  	}
   123  }
   124  
   125  func FlagSet() *flag.FlagSet {
   126  	set := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
   127  	set.StringVar(&kubeconfig, "kubeconfig", "", "absolute path to the kubeconfig file")
   128  	set.StringVar(&master, "master", "", "master url")
   129  	return set
   130  }
   131  
   132  func GetKubevirtSubresourceClientFromFlags(master string, kubeconfig string) (KubevirtClient, error) {
   133  	config, err := clientcmd.BuildConfigFromFlags(master, kubeconfig)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	config.GroupVersion = &v1.SubresourceStorageGroupVersion
   139  	config.NegotiatedSerializer = serializer.WithoutConversionCodecFactory{CodecFactory: Codecs}
   140  	config.APIPath = "/apis"
   141  	config.ContentType = runtime.ContentTypeJSON
   142  
   143  	restClient, err := rest.RESTClientFor(config)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	coreClient, err := kubernetes.NewForConfig(config)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  
   153  	generatedKubeVirtClient, err := generatedclient.NewForConfig(config)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	cdiClient, err := cdiclient.NewForConfig(config)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	networkClient, err := networkclient.NewForConfig(config)
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	extensionsClient, err := extclient.NewForConfig(config)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	secClient, err := secv1.NewForConfig(config)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  
   178  	routeClient, err := routev1.NewForConfig(config)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  
   183  	discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  
   188  	prometheusClient, err := promclient.NewForConfig(config)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	snapshotClient, err := k8ssnapshotclient.NewForConfig(config)
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	dynamicClient, err := dynamic.NewForConfig(config)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	migrationsClient, err := migrationsv1.NewForConfig(config)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	cloneClient, err := clonev1alpha1.NewForConfig(config)
   209  	if err != nil {
   210  		return nil, err
   211  	}
   212  
   213  	return &kubevirt{
   214  		master,
   215  		kubeconfig,
   216  		restClient,
   217  		config,
   218  		generatedKubeVirtClient,
   219  		cdiClient,
   220  		networkClient,
   221  		extensionsClient,
   222  		secClient,
   223  		routeClient,
   224  		discoveryClient,
   225  		prometheusClient,
   226  		snapshotClient,
   227  		dynamicClient,
   228  		migrationsClient,
   229  		cloneClient,
   230  		coreClient,
   231  	}, nil
   232  }
   233  
   234  // DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
   235  //
   236  //  1. Use the kubeconfig builder.  The number of merges and overrides here gets a little crazy.  Stay with me.
   237  //
   238  //  1. Merge the kubeconfig itself.  This is done with the following hierarchy rules:
   239  //
   240  //  1. CommandLineLocation - this parsed from the command line, so it must be late bound.  If you specify this,
   241  //     then no other kubeconfig files are merged.  This file must exist.
   242  //
   243  //  2. If $KUBECONFIG is set, then it is treated as a list of files that should be merged.
   244  //
   245  //  3. HomeDirectoryLocation
   246  //     Empty filenames are ignored.  Files with non-deserializable content produced errors.
   247  //     The first file to set a particular value or map key wins and the value or map key is never changed.
   248  //     This means that the first file to set CurrentContext will have its context preserved.  It also means
   249  //     that if two files specify a "red-user", only values from the first file's red-user are used.  Even
   250  //     non-conflicting entries from the second file's "red-user" are discarded.
   251  //
   252  //  2. Determine the context to use based on the first hit in this chain
   253  //
   254  //  1. command line argument - again, parsed from the command line, so it must be late bound
   255  //
   256  //  2. CurrentContext from the merged kubeconfig file
   257  //
   258  //  3. Empty is allowed at this stage
   259  //
   260  //  3. Determine the cluster info and auth info to use.  At this point, we may or may not have a context.  They
   261  //     are built based on the first hit in this chain.  (run it twice, once for auth, once for cluster)
   262  //
   263  //  1. command line argument
   264  //
   265  //  2. If context is present, then use the context value
   266  //
   267  //  3. Empty is allowed
   268  //
   269  //  4. Determine the actual cluster info to use.  At this point, we may or may not have a cluster info.  Build
   270  //     each piece of the cluster info based on the chain:
   271  //
   272  //  1. command line argument
   273  //
   274  //  2. If cluster info is present and a value for the attribute is present, use it.
   275  //
   276  //  3. If you don't have a server location, bail.
   277  //
   278  //  5. Auth info is build using the same rules as cluster info, EXCEPT that you can only have one authentication
   279  //     technique per auth info.  The following conditions result in an error:
   280  //
   281  //  1. If there are two conflicting techniques specified from the command line, fail.
   282  //
   283  //  2. If the command line does not specify one, and the auth info has conflicting techniques, fail.
   284  //
   285  //  3. If the command line specifies one and the auth info specifies another, honor the command line technique.
   286  //
   287  //  2. Use default values and potentially prompt for auth information
   288  //
   289  //     However, if it appears that we're running in a kubernetes cluster
   290  //     container environment, then run with the auth info kubernetes mounted for
   291  //     us. Specifically:
   292  //     The env vars KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT are
   293  //     set, and the file /var/run/secrets/kubernetes.io/serviceaccount/token
   294  //     exists and is not a directory.
   295  //
   296  // Initially copied from https://github.com/kubernetes/kubernetes/blob/09f321c80bfc9bca63a5530b56d7a1a3ba80ba9b/pkg/kubectl/cmd/util/factory_client_access.go#L174
   297  func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
   298  	loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
   299  	// use the standard defaults for this client command
   300  	// DEPRECATED: remove and replace with something more accurate
   301  	loadingRules.DefaultClientConfig = &clientcmd.DefaultClientConfig
   302  
   303  	flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
   304  
   305  	overrides := &clientcmd.ConfigOverrides{ClusterDefaults: clientcmd.ClusterDefaults}
   306  
   307  	flagNames := clientcmd.RecommendedConfigOverrideFlags("")
   308  	// short flagnames are disabled by default.  These are here for compatibility with existing scripts
   309  	flagNames.ClusterOverrideFlags.APIServer.ShortName = "s"
   310  
   311  	clientcmd.BindOverrideFlags(overrides, flags, flagNames)
   312  	clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
   313  
   314  	return clientConfig
   315  }
   316  
   317  // this function is defined as a closure so iut could be overwritten by unit tests
   318  var GetKubevirtClientFromClientConfig = func(cmdConfig clientcmd.ClientConfig) (KubevirtClient, error) {
   319  	config, err := cmdConfig.ClientConfig()
   320  	if err != nil {
   321  		return nil, err
   322  	}
   323  	return GetKubevirtClientFromRESTConfig(config)
   324  
   325  }
   326  
   327  func GetKubevirtClientFromRESTConfig(config *rest.Config) (KubevirtClient, error) {
   328  	shallowCopy := *config
   329  	shallowCopy.GroupVersion = &v1.StorageGroupVersion
   330  	shallowCopy.NegotiatedSerializer = serializer.WithoutConversionCodecFactory{CodecFactory: Codecs}
   331  	shallowCopy.APIPath = "/apis"
   332  	shallowCopy.ContentType = runtime.ContentTypeJSON
   333  	if config.UserAgent == "" {
   334  		config.UserAgent = restclient.DefaultKubernetesUserAgent()
   335  	}
   336  
   337  	executeRestConfigHooks(&shallowCopy)
   338  
   339  	restClient, err := rest.RESTClientFor(&shallowCopy)
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  
   344  	coreClient, err := kubernetes.NewForConfig(&shallowCopy)
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  
   349  	generatedKubeVirtClient, err := generatedclient.NewForConfig(&shallowCopy)
   350  	if err != nil {
   351  		return nil, err
   352  	}
   353  
   354  	cdiClient, err := cdiclient.NewForConfig(&shallowCopy)
   355  	if err != nil {
   356  		return nil, err
   357  	}
   358  
   359  	networkClient, err := networkclient.NewForConfig(&shallowCopy)
   360  	if err != nil {
   361  		return nil, err
   362  	}
   363  
   364  	extensionsClient, err := extclient.NewForConfig(&shallowCopy)
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  
   369  	secClient, err := secv1.NewForConfig(&shallowCopy)
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  
   374  	routeClient, err := routev1.NewForConfig(&shallowCopy)
   375  	if err != nil {
   376  		return nil, err
   377  	}
   378  
   379  	discoveryClient, err := discovery.NewDiscoveryClientForConfig(&shallowCopy)
   380  	if err != nil {
   381  		return nil, err
   382  	}
   383  
   384  	prometheusClient, err := promclient.NewForConfig(&shallowCopy)
   385  	if err != nil {
   386  		return nil, err
   387  	}
   388  
   389  	snapshotClient, err := k8ssnapshotclient.NewForConfig(&shallowCopy)
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  
   394  	dynamicClient, err := dynamic.NewForConfig(&shallowCopy)
   395  	if err != nil {
   396  		return nil, err
   397  	}
   398  
   399  	migrationsClient, err := migrationsv1.NewForConfig(&shallowCopy)
   400  	if err != nil {
   401  		return nil, err
   402  	}
   403  
   404  	cloneClient, err := clonev1alpha1.NewForConfig(&shallowCopy)
   405  	if err != nil {
   406  		return nil, err
   407  	}
   408  
   409  	return &kubevirt{
   410  		master,
   411  		kubeconfig,
   412  		restClient,
   413  		&shallowCopy,
   414  		generatedKubeVirtClient,
   415  		cdiClient,
   416  		networkClient,
   417  		extensionsClient,
   418  		secClient,
   419  		routeClient,
   420  		discoveryClient,
   421  		prometheusClient,
   422  		snapshotClient,
   423  		dynamicClient,
   424  		migrationsClient,
   425  		cloneClient,
   426  		coreClient,
   427  	}, nil
   428  }
   429  
   430  func GetKubevirtClientFromFlags(master string, kubeconfig string) (KubevirtClient, error) {
   431  	config, err := clientcmd.BuildConfigFromFlags(master, kubeconfig)
   432  	if err != nil {
   433  		return nil, err
   434  	}
   435  	return GetKubevirtClientFromRESTConfig(config)
   436  }
   437  
   438  func GetKubevirtClient() (KubevirtClient, error) {
   439  	var err error
   440  	once.Do(func() {
   441  		virtclient, err = GetKubevirtClientFromFlags(master, kubeconfig)
   442  	})
   443  	return virtclient, err
   444  }
   445  
   446  func GetKubevirtSubresourceClient() (KubevirtClient, error) {
   447  	return GetKubevirtSubresourceClientFromFlags(master, kubeconfig)
   448  }
   449  
   450  // Deprecated: Use GetKubevirtClientConfig instead
   451  func GetConfig() (*restclient.Config, error) {
   452  	return clientcmd.BuildConfigFromFlags(master, kubeconfig)
   453  }
   454  
   455  func GetKubevirtClientConfig() (*rest.Config, error) {
   456  	return GetConfig()
   457  }
   458  

View as plain text