istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/cli/context.go (about)

     1  // Copyright Istio Authors
     2  //
     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  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package cli
    16  
    17  import (
    18  	corev1 "k8s.io/api/core/v1"
    19  	"k8s.io/apimachinery/pkg/runtime"
    20  	"k8s.io/client-go/rest"
    21  
    22  	"istio.io/istio/istioctl/pkg/util/handlers"
    23  	"istio.io/istio/pkg/kube"
    24  	"istio.io/istio/pkg/ptr"
    25  )
    26  
    27  type Context interface {
    28  	// CLIClient returns a client for the default revision
    29  	CLIClient() (kube.CLIClient, error)
    30  	// CLIClientWithRevision returns a client for the given revision
    31  	CLIClientWithRevision(rev string) (kube.CLIClient, error)
    32  	// InferPodInfoFromTypedResource returns the pod name and namespace for the given typed resource
    33  	InferPodInfoFromTypedResource(name, namespace string) (pod string, ns string, err error)
    34  	// InferPodsFromTypedResource returns the pod names and namespace for the given typed resource
    35  	InferPodsFromTypedResource(name, namespace string) ([]string, string, error)
    36  	// Namespace returns the namespace specified by the user
    37  	Namespace() string
    38  	// IstioNamespace returns the Istio namespace specified by the user
    39  	IstioNamespace() string
    40  	// NamespaceOrDefault returns the namespace specified by the user, or the default namespace if none was specified
    41  	NamespaceOrDefault(namespace string) string
    42  }
    43  
    44  type instance struct {
    45  	// clients are cached clients for each revision
    46  	clients map[string]kube.CLIClient
    47  	RootFlags
    48  }
    49  
    50  func newKubeClientWithRevision(kubeconfig, configContext, revision string) (kube.CLIClient, error) {
    51  	rc, err := kube.DefaultRestConfig(kubeconfig, configContext, func(config *rest.Config) {
    52  		// We are running a one-off command locally, so we don't need to worry too much about rate limiting
    53  		// Bumping this up greatly decreases install time
    54  		config.QPS = 50
    55  		config.Burst = 100
    56  	})
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	return kube.NewCLIClient(kube.NewClientConfigForRestConfig(rc), kube.WithRevision(revision))
    61  }
    62  
    63  func NewCLIContext(rootFlags *RootFlags) Context {
    64  	if rootFlags == nil {
    65  		rootFlags = &RootFlags{
    66  			kubeconfig:       ptr.Of[string](""),
    67  			configContext:    ptr.Of[string](""),
    68  			namespace:        ptr.Of[string](""),
    69  			istioNamespace:   ptr.Of[string](""),
    70  			defaultNamespace: "",
    71  		}
    72  	}
    73  	return &instance{
    74  		RootFlags: *rootFlags,
    75  	}
    76  }
    77  
    78  func (i *instance) CLIClientWithRevision(rev string) (kube.CLIClient, error) {
    79  	if i.clients == nil {
    80  		i.clients = make(map[string]kube.CLIClient)
    81  	}
    82  	if i.clients[rev] == nil {
    83  		client, err := newKubeClientWithRevision(*i.kubeconfig, *i.configContext, rev)
    84  		if err != nil {
    85  			return nil, err
    86  		}
    87  		i.clients[rev] = client
    88  	}
    89  	return i.clients[rev], nil
    90  }
    91  
    92  func (i *instance) CLIClient() (kube.CLIClient, error) {
    93  	return i.CLIClientWithRevision("")
    94  }
    95  
    96  func (i *instance) InferPodInfoFromTypedResource(name, namespace string) (pod string, ns string, err error) {
    97  	client, err := i.CLIClient()
    98  	if err != nil {
    99  		return "", "", err
   100  	}
   101  	return handlers.InferPodInfoFromTypedResource(name, i.NamespaceOrDefault(namespace), MakeKubeFactory(client))
   102  }
   103  
   104  func (i *instance) InferPodsFromTypedResource(name, namespace string) ([]string, string, error) {
   105  	client, err := i.CLIClient()
   106  	if err != nil {
   107  		return nil, "", err
   108  	}
   109  	return handlers.InferPodsFromTypedResource(name, i.NamespaceOrDefault(namespace), MakeKubeFactory(client))
   110  }
   111  
   112  func (i *instance) NamespaceOrDefault(namespace string) string {
   113  	return handleNamespace(namespace, i.DefaultNamespace())
   114  }
   115  
   116  // handleNamespace returns the defaultNamespace if the namespace is empty
   117  func handleNamespace(ns, defaultNamespace string) string {
   118  	if ns == corev1.NamespaceAll {
   119  		ns = defaultNamespace
   120  	}
   121  	return ns
   122  }
   123  
   124  type fakeInstance struct {
   125  	// clients are cached clients for each revision
   126  	clients   map[string]kube.CLIClient
   127  	rootFlags *RootFlags
   128  	results   map[string][]byte
   129  	objects   []runtime.Object
   130  	version   string
   131  }
   132  
   133  func (f *fakeInstance) CLIClientWithRevision(rev string) (kube.CLIClient, error) {
   134  	if _, ok := f.clients[rev]; !ok {
   135  		var cliclient kube.CLIClient
   136  		if f.version != "" {
   137  			cliclient = kube.NewFakeClientWithVersion(f.version, f.objects...)
   138  		} else {
   139  			cliclient = kube.NewFakeClient(f.objects...)
   140  		}
   141  		if rev != "" {
   142  			kube.SetRevisionForTest(cliclient, rev)
   143  		}
   144  		c := MockClient{
   145  			CLIClient: cliclient,
   146  			Results:   f.results,
   147  		}
   148  		f.clients[rev] = c
   149  	}
   150  	return f.clients[rev], nil
   151  }
   152  
   153  func (f *fakeInstance) CLIClient() (kube.CLIClient, error) {
   154  	return f.CLIClientWithRevision("")
   155  }
   156  
   157  func (f *fakeInstance) InferPodInfoFromTypedResource(name, namespace string) (pod string, ns string, err error) {
   158  	client, err := f.CLIClient()
   159  	if err != nil {
   160  		return "", "", err
   161  	}
   162  	return handlers.InferPodInfoFromTypedResource(name, f.NamespaceOrDefault(namespace), MakeKubeFactory(client))
   163  }
   164  
   165  func (f *fakeInstance) InferPodsFromTypedResource(name, namespace string) ([]string, string, error) {
   166  	client, err := f.CLIClient()
   167  	if err != nil {
   168  		return nil, "", err
   169  	}
   170  	return handlers.InferPodsFromTypedResource(name, f.NamespaceOrDefault(namespace), MakeKubeFactory(client))
   171  }
   172  
   173  func (f *fakeInstance) NamespaceOrDefault(namespace string) string {
   174  	return handleNamespace(namespace, f.rootFlags.defaultNamespace)
   175  }
   176  
   177  func (f *fakeInstance) Namespace() string {
   178  	return f.rootFlags.Namespace()
   179  }
   180  
   181  func (f *fakeInstance) IstioNamespace() string {
   182  	return f.rootFlags.IstioNamespace()
   183  }
   184  
   185  type NewFakeContextOption struct {
   186  	Namespace      string
   187  	IstioNamespace string
   188  	Results        map[string][]byte
   189  	// Objects are the objects to be applied to the fake client
   190  	Objects []runtime.Object
   191  	// Version is the version of the fake client
   192  	Version string
   193  }
   194  
   195  func NewFakeContext(opts *NewFakeContextOption) Context {
   196  	if opts == nil {
   197  		opts = &NewFakeContextOption{}
   198  	}
   199  	ns := opts.Namespace
   200  	ins := opts.IstioNamespace
   201  	return &fakeInstance{
   202  		clients: map[string]kube.CLIClient{},
   203  		rootFlags: &RootFlags{
   204  			kubeconfig:       ptr.Of[string](""),
   205  			configContext:    ptr.Of[string](""),
   206  			namespace:        &ns,
   207  			istioNamespace:   &ins,
   208  			defaultNamespace: "",
   209  		},
   210  		results: opts.Results,
   211  		objects: opts.Objects,
   212  		version: opts.Version,
   213  	}
   214  }