client-go dynamic包

1. 概述

用于动态生成informer/lister/client等

2. 类图

在这里插入图片描述

3. 具体代码解析

3.1 interface.go

// ResourceInterface的工厂模式,方法Resource可以获取对应gvr的ResourceInterface接口
type Interface interface {
	Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface
}

// 操作对应obj的接口
type ResourceInterface interface {
	Create(ctx context.Context, obj *unstructured.Unstructured, options metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error)
	Update(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error)
	UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, options metav1.UpdateOptions) (*unstructured.Unstructured, error)
	Delete(ctx context.Context, name string, options metav1.DeleteOptions, subresources ...string) error
	DeleteCollection(ctx context.Context, options metav1.DeleteOptions, listOptions metav1.ListOptions) error
	Get(ctx context.Context, name string, options metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error)
	List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error)
	Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error)
	Patch(ctx context.Context, name string, pt types.PatchType, data []byte, options metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error)
	Apply(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error)
	ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error)
}

// 限定了namespace的ResourceInterface
type NamespaceableResourceInterface interface {
	Namespace(string) ResourceInterface
	ResourceInterface
}

3.2 simple.go

相关函数:


// ConfigFor 返回提供的配置的副本,并设置了适当的动态客户端默认值。
func ConfigFor(inConfig *rest.Config) *rest.Config {
	config := rest.CopyConfig(inConfig)
	config.AcceptContentTypes = "application/json"
	config.ContentType = "application/json"
	config.NegotiatedSerializer = basicNegotiatedSerializer{} // this gets used for discovery and error handling types
	if config.UserAgent == "" {
		config.UserAgent = rest.DefaultKubernetesUserAgent()
	}
	return config
}

// // NewForConfig 创建新的动态客户端或返回错误。
func New(c rest.Interface) *DynamicClient {
	return &DynamicClient{client: c}
}

//NewForConfigOrDie为给定的配置创建一个新的DynamicClient,如果配置中有错误,就会panic。
func NewForConfigOrDie(c *rest.Config) *DynamicClient {
	ret, err := NewForConfig(c)
	if err != nil {
		panic(err)
	}
	return ret
}

// NewForConfig创建新的动态客户端或返回错误。
// NewForConfig相当于NewForConfigAndClient(c,httpClient),其中httpClient是使用rest.HTTPClientFor(c)生成的。
func NewForConfig(inConfig *rest.Config) (*DynamicClient, error) {
	config := ConfigFor(inConfig)

	httpClient, err := rest.HTTPClientFor(config)
	if err != nil {
		return nil, err
	}
	return NewForConfigAndClient(config, httpClient)
}

// NewForConfigAndClient为给定的配置和http客户端创建一个新的动态客户
func NewForConfigAndClient(inConfig *rest.Config, h *http.Client) (*DynamicClient, error) {
	config := ConfigFor(inConfig)
	// for serializing the options
	config.GroupVersion = &schema.GroupVersion{}
	config.APIPath = "/if-you-see-this-search-for-the-break"

	restClient, err := rest.RESTClientForConfigAndClient(config, h)
	if err != nil {
		return nil, err
	}
	return &DynamicClient{client: restClient}, nil
}

dynamicResourceClient结构体定义及其方法分析:

// DynamicClient定义,实质上就是rest接口
type DynamicClient struct {
	client rest.Interface
}

var _ Interface = &DynamicClient{}

type dynamicResourceClient struct {
	client    *DynamicClient
	namespace string
	resource  schema.GroupVersionResource
}


func (c *DynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableResourceInterface {
	return &dynamicResourceClient{client: c, resource: resource}
}

// 实现了NamespaceableResourceInterface接口的Namespace方法
func (c *dynamicResourceClient) Namespace(ns string) ResourceInterface {
	ret := *c
	ret.namespace = ns
	return &ret
}

func (c *dynamicResourceClient) Create(ctx context.Context, obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
	// 把Unstructured obj(内部其实是map类型)转化为字节数组
	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
	if err != nil {
		return nil, err
	}
	name := ""
	// 如果subresources不为空,会校验object的name
	if len(subresources) > 0 {
		accessor, err := meta.Accessor(obj)
		if err != nil {
			return nil, err
		}
		name = accessor.GetName()
		if len(name) == 0 {
			return nil, fmt.Errorf("name is required")
		}
	}
	if err := validateNamespaceWithOptionalName(c.namespace, name); err != nil {
		return nil, err
	}
	 // 如果name不为空,再创建时则使用该name,否则随机生成。
	 // (这里只是建立了tcp长链接和解码相应,具体的执行逻辑还是在k8s api-server中调用k8s api的etcd相关接口)
	result := c.client.client.
		Post().
		AbsPath(append(c.makeURLSegments(name), subresources...)...).
		SetHeader("Content-Type", runtime.ContentTypeJSON).
		Body(outBytes).
		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
		Do(ctx)
	if err := result.Error(); err != nil {
		return nil, err
	}
	// 返回原始结果(字节数组数据)
	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	 // 使用json编解码器  解码原始数据,得到一个Unstructured类型的数据
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

3.3 scheme.go

scheme.go 实现了kubernetes/apimachinery的序列化和编解码

var watchScheme = runtime.NewScheme(

// 供basicNegotiatedSerializer来获取gvk/类型转化/创建obj
var basicScheme = runtime.NewScheme()

// 为上面ResourceClient删除操作的Options创建删除CodecFactory(编解码器工厂),提供Scheme参数
var deleteScheme = runtime.NewScheme()

// 为上面ResourceClient各种操作创建参数CodecFactory(编解码器工厂),提供Scheme参数
var parameterScheme = runtime.NewScheme()

// 创建删除CodecFactory(编解码器工厂),编解码DeleteOptions对象
var deleteOptionsCodec = serializer.NewCodecFactory(deleteScheme)

// 创建参数CodecFactory(编解码器工厂),
// 编解码ListOptions/GetOptions/CreateOptions/UpdateOptions/PatchOptions对象,作为请求参数
var dynamicParameterCodec = runtime.NewParameterCodec(parameterScheme)

// 定义gv
var versionV1 = schema.GroupVersion{Version: "v1"}

// 添加ListOptions/GetOptions/DeleteOptions/CreateOptions/UpdateOptions/PatchOptions,初始化各个schema
func init() {
	metav1.AddToGroupVersion(watchScheme, versionV1)
	metav1.AddToGroupVersion(basicScheme, versionV1)
	metav1.AddToGroupVersion(parameterScheme, versionV1)
	metav1.AddToGroupVersion(deleteScheme, versionV1)
}

结构体及其相关方法定义:

// 实现了NegotiatedSerializer接口,根据content判断,以何种方式编解码obj
type basicNegotiatedSerializer struct{}

// 实现NegotiatedSerializer的SupportedMediaTypes方法,自定义可以处理的MediaType,并定义各种MediaType的Serializer
func (s basicNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
	return []runtime.SerializerInfo{
		{
			MediaType:        "application/json",
			MediaTypeType:    "application",
			MediaTypeSubType: "json",
			EncodesAsText:    true,
			Serializer:       json.NewSerializer(json.DefaultMetaFactory, unstructuredCreater{basicScheme}, unstructuredTyper{basicScheme}, false),
			PrettySerializer: json.NewSerializer(json.DefaultMetaFactory, unstructuredCreater{basicScheme}, unstructuredTyper{basicScheme}, true),
			StreamSerializer: &runtime.StreamSerializerInfo{
				EncodesAsText: true,
				Serializer:    json.NewSerializer(json.DefaultMetaFactory, basicScheme, basicScheme, false),
				Framer:        json.Framer,
			},
		},
	}
}

// 实现NegotiatedSerializer的EncoderForVersion方法,获取一个版本化的Encoder(编码为指定版本)
func (s basicNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
	return runtime.WithVersionEncoder{
		Version:     gv,
		Encoder:     encoder,
		ObjectTyper: unstructuredTyper{basicScheme},
	}
}

// 实现NegotiatedSerializer的DecoderToVersion方法, 获取原始的decoder
func (s basicNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
	return decoder
}
// unstructuredCreater 包装了ObjectCreater(用于New一个gvk对应的obj),
// 如果有err,那么和ObjectCreater的区别是返回了空的obj,而err为空
type unstructuredCreater struct {
	nested runtime.ObjectCreater
}

// 实现了ObjectCreater接口的New方法
func (c unstructuredCreater) New(kind schema.GroupVersionKind) (runtime.Object, error) {
	out, err := c.nested.New(kind)
	if err == nil {
		return out, nil
	}
	out = &unstructured.Unstructured{}
	out.GetObjectKind().SetGroupVersionKind(kind)
	return out, nil
}
// unstructuredTyper 包装了ObjectTyper(用于获取obj对应的gvk列表),
// 如果有err,那么和ObjectTyper的区别是会判断是否是Unstructured类型且obj对应的gvk是否为空

type unstructuredTyper struct {
	nested runtime.ObjectTyper
}

// 实现了ObjectTyper接口的ObjectKinds方法
func (t unstructuredTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
	kinds, unversioned, err := t.nested.ObjectKinds(obj)
	if err == nil {
		return kinds, unversioned, nil
	}
    // 判断obj是否是Unstructured类型且对应的gvk是否为空
	if _, ok := obj.(runtime.Unstructured); ok && !obj.GetObjectKind().GroupVersionKind().Empty() {
		return []schema.GroupVersionKind{obj.GetObjectKind().GroupVersionKind()}, false, nil
	}
	return nil, false, err
}

func (t unstructuredTyper) Recognizes(gvk schema.GroupVersionKind) bool {
	return true
}

3.4 dynamiclister包

3.4.1 interface.go

interface.go 定义了获取(从indexer(缓存)中get/list)obj的接口

// Lister 获取资源和获取NamespaceLister。
type Lister interface {
	// List 列出索引器(缓存)中的所有资源。
	List(selector labels.Selector) (ret []*unstructured.Unstructured, err error)
	// Get 从索引器(缓存)中检索具有给定名称的资源
	Get(name string) (*unstructured.Unstructured, error)
	// Namespace 根据指定namespace返回一个对象,该对象可以列出和获取给定命名空间中的资源
	Namespace(namespace string) NamespaceLister
}

// NamespaceLister 获取命名空间下的资源。类似于controller-runtime分析client包的Reader接口
type NamespaceLister interface {
	// List 列出索引器(缓存)中给定命名空间的所有资源
	List(selector labels.Selector) (ret []*unstructured.Unstructured, err error)
	// Get 从索引器(缓存)中检索给定命名空间和名称的资源。
	Get(name string) (*unstructured.Unstructured, error)
}

3.4.2 lister.go

// dynamicLister 实现了 Lister 接口。
type dynamicLister struct {
	// 索引器(缓存)
	indexer cache.Indexer
 	// 索引器对应的资源gvr,该索引器只存储该gvr对应的资源
	gvr     schema.GroupVersionResource
}

// New 返回一个新的 Lister.
func New(indexer cache.Indexer, gvr schema.GroupVersionResource) Lister {
	return &dynamicLister{indexer: indexer, gvr: gvr}
}

// List 列出索引器中的所有资源。
func (l *dynamicLister) List(selector labels.Selector) (ret []*unstructured.Unstructured, err error) {
  	// 该方法到对应包在做具体分析,用来获取符合selector对应添加的item
	err = cache.ListAll(l.indexer, selector, func(m interface{}) {
		// 符合添加的item会追加到ret
		ret = append(ret, m.(*unstructured.Unstructured))
	})
	return ret, err
}

// Get 从索引器中检索具有给定名称的资源
func (l *dynamicLister) Get(name string) (*unstructured.Unstructured, error) {

	obj, exists, err := l.indexer.GetByKey(name)
	if err != nil {
		return nil, err
	}
	if !exists {
		return nil, errors.NewNotFound(l.gvr.GroupResource(), name)
	}
	return obj.(*unstructured.Unstructured), nil
}

// Namespace 返回一个对象,该对象可以从给定的命名空间中列出和获取资源.
func (l *dynamicLister) Namespace(namespace string) NamespaceLister {
	return &dynamicNamespaceLister{indexer: l.indexer, namespace: namespace, gvr: l.gvr}
}

// dynamicNamespaceLister 实现了 NamespaceLister 接口。相比dynamicLister多了namespace属性,用来限定namespace
type dynamicNamespaceLister struct {
	indexer   cache.Indexer
	namespace string
	gvr       schema.GroupVersionResource
}

// List 列出索引器中给定命名空间的所有资源。
func (l *dynamicNamespaceLister) List(selector labels.Selector) (ret []*unstructured.Unstructured, err error) {
	err = cache.ListAllByNamespace(l.indexer, l.namespace, selector, func(m interface{}) {
		ret = append(ret, m.(*unstructured.Unstructured))
	})
	return ret, err
}

// Get 从索引器中检索给定命名空间和名称的资源。
func (l *dynamicNamespaceLister) Get(name string) (*unstructured.Unstructured, error) {
	  // 注意: 这里可以看到indexer中items的存放,当namespace不为空时,key是${namespace}/${name}
	obj, exists, err := l.indexer.GetByKey(l.namespace + "/" + name)
	if err != nil {
		return nil, err
	}
	if !exists {
		return nil, errors.NewNotFound(l.gvr.GroupResource(), name)
	}
	return obj.(*unstructured.Unstructured), nil
}

3.4.3 shim.go

// dynamicListerShim 实现了 cache.GenericLister
// 包装了Lister接口,只是List把返回slice中Unstructured对象变为object对象
type dynamicListerShim struct {
	lister Lister
}

// NewRuntimeObjectShim 为 Lister 返回一个新的shim。
// 它包装 Lister 以便它实现 cache.GenericLister 接口
 func NewRuntimeObjectShim(lister Lister) cache.GenericLister { return &dynamicListerShim{lister: lister} }
func NewRuntimeObjectShim(lister Lister) cache.GenericLister {
	return &dynamicListerShim{lister: lister}
}

// List 将返回跨命名空间的所有对象
func (s *dynamicListerShim) List(selector labels.Selector) (ret []runtime.Object, err error) {
	objs, err := s.lister.List(selector)
	if err != nil {
		return nil, err
	}

	ret = make([]runtime.Object, len(objs))
    	// 返回slice中Unstructured对象变为Object对象
	for index, obj := range objs {
		ret[index] = obj
	}
	return ret, err
}

// Get会假设name=key去尝试检索  
func (s *dynamicListerShim) Get(name string) (runtime.Object, error) {
	return s.lister.Get(name)
}

// 获取限定命名空间的Lister
func (s *dynamicListerShim) ByNamespace(namespace string) cache.GenericNamespaceLister {
	return &dynamicNamespaceListerShim{
		namespaceLister: s.lister.Namespace(namespace),
	}
}

// dynamicNamespaceListerShim 实现了 NamespaceLister 接口。
// 它包装了 NamespaceLister 以便它实现 cache.GenericNamespaceLister 接口
type dynamicNamespaceListerShim struct {
	namespaceLister NamespaceLister
}

// List 将返回此命名空间中的所有对象
func (ns *dynamicNamespaceListerShim) List(selector labels.Selector) (ret []runtime.Object, err error) {
	objs, err := ns.namespaceLister.List(selector)
	if err != nil {
		return nil, err
	}

	ret = make([]runtime.Object, len(objs))
	for index, obj := range objs {
		ret[index] = obj
	}
	return ret, err
}

// Get 将尝试按命名空间和名称检索
func (ns *dynamicNamespaceListerShim) Get(name string) (runtime.Object, error) {
	return ns.namespaceLister.Get(name)
}

3.5 dynamicinformer包

3.5.1 interface.go

接口 定义了获取Informer的方法,等待缓存同步的方法,启动所有informer的方法

// Dynamic SharedInformerFactory 为动态客户端提供对共享informer和lister的访问
type DynamicSharedInformerFactory interface {
   	 // 启动所有informer的方法
	Start(stopCh <-chan struct{})
	// 获取Informer的方法
	ForResource(gvr schema.GroupVersionResource) informers.GenericInformer
 	// 等待缓存同步的方法
	WaitForCacheSync(stopCh <-chan struct{}) map[schema.GroupVersionResource]bool
}

// TweakListOptionsFunc 定义了一个辅助函数的签名,想要为 API 提供更多的列表选项
type TweakListOptionsFunc func(*metav1.ListOptions)

3.5.2 informer.go

公共函数:

// NewDynamicSharedInformerFactory 为所有命名空间构造一个 dynamicSharedInformerFactory 的新实例。
func NewDynamicSharedInformerFactory(client dynamic.Interface, defaultResync time.Duration) DynamicSharedInformerFactory {
	return NewFilteredDynamicSharedInformerFactory(client, defaultResync, metav1.NamespaceAll, nil)
}

// NewFilteredDynamicSharedInformerFactory 构造了一个 dynamicSharedInformerFactory 的新实例。 
// 通过此工厂获得的lister将受到此处指定的相同过滤器的约束。
func NewFilteredDynamicSharedInformerFactory(client dynamic.Interface, defaultResync time.Duration, namespace string, tweakListOptions TweakListOptionsFunc) DynamicSharedInformerFactory {
	return &dynamicSharedInformerFactory{
		client:           client,
		defaultResync:    defaultResync,
		namespace:        namespace,
		informers:        map[schema.GroupVersionResource]informers.GenericInformer{},
		startedInformers: make(map[schema.GroupVersionResource]bool),
		tweakListOptions: tweakListOptions,
	}
}

// NewFilteredDynamicInformer 为动态类型构造一个新的 Informer。
func NewFilteredDynamicInformer(client dynamic.Interface, gvr schema.GroupVersionResource, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions TweakListOptionsFunc) informers.GenericInformer {
	return &dynamicInformer{
		gvr: gvr,
		informer: cache.NewSharedIndexInformer(
			&cache.ListWatch{
				ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
					if tweakListOptions != nil {
						tweakListOptions(&options)
					}
					return client.Resource(gvr).Namespace(namespace).List(context.TODO(), options)
				},
				WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
					if tweakListOptions != nil {
						tweakListOptions(&options)
					}
					return client.Resource(gvr).Namespace(namespace).Watch(context.TODO(), options)
				},
			},
			&unstructured.Unstructured{},
			resyncPeriod,
			indexers,
		),
	}
}

结构体定义私有属性和方法:

type dynamicSharedInformerFactory struct {
    	// 构建ListWatch接口使用,为后来构建reflector,执行listWatch监控api resource提供client
	client        dynamic.Interface
   	 // 同步周期,informer同步deltaFIFO中数据到listener中的chan中
	defaultResync time.Duration
    	// 命名空间
	namespace     string

	lock      sync.Mutex
	// 缓存informer到map中
	informers map[schema.GroupVersionResource]informers.GenericInformer
	// startInformers 用于跟踪哪些 Informers 已启动。这允许安全地多次调用 Start()。
	startedInformers map[schema.GroupVersionResource]bool
	tweakListOptions TweakListOptionsFunc
}

// 实现DynamicSharedInformerFactory的ForResource方法
func (f *dynamicSharedInformerFactory) ForResource(gvr schema.GroupVersionResource) informers.GenericInformer {
	f.lock.Lock()
	defer f.lock.Unlock()

	key := gvr
    	// 获取缓存map中的informer
	informer, exists := f.informers[key]
	if exists {
		return informer
	}
   	 // 不存在就创建
	informer = NewFilteredDynamicInformer(f.client, gvr, f.namespace, f.defaultResync, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
	f.informers[key] = informer

	return informer
}

// 实现SharedInformerFactory的Start方法,初始化所有请求的informers.
func (f *dynamicSharedInformerFactory) Start(stopCh <-chan struct{}) {
	f.lock.Lock()
	defer f.lock.Unlock()
	// 遍历所有informer
	for informerType, informer := range f.informers {
		// 判断该informer是否已经启动
		if !f.startedInformers[informerType] {
			 // 启动informer
			go informer.Informer().Run(stopCh)
            		// 设置对应gvr的informer已经启动
			f.startedInformers[informerType] = true
		}
	}
}

// 实现SharedInformerFactory的WaitForCacheSync方法,等待所有启动的informer的缓存同步。
 func (f *dynamicSharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[schema.GroupVersionResource]bool {
	 // 定义map,用于接收所有已经启动的informer
	informers := func() map[schema.GroupVersionResource]cache.SharedIndexInformer {
		f.lock.Lock()
		defer f.lock.Unlock()
		informers := map[schema.GroupVersionResource]cache.SharedIndexInformer{}
		for informerType, informer := range f.informers {
			if f.startedInformers[informerType] {
				informers[informerType] = informer.Informer()
			}
		}
		return informers
	}()
	// 定义map,用于接收所有同步完成的informer
	res := map[schema.GroupVersionResource]bool{}
	// 遍历已经启动的所有informer
	for informType, informer := range informers {
 		// 执行同步方法
        	// (1) 如果informer中controller为空,返回false,
       		 // (2) 如果informer.controller的queue还没有调用过Add/Update/Delete/AddIfNotPresent或者queue的initialPopulationCount != 0 (队列中还有数据),返回false
		res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced)
	}
	return res
}

// 动态Informer结构体,包装了SharedIndexInformer和gvr
type dynamicInformer struct {
	informer cache.SharedIndexInformer
	gvr      schema.GroupVersionResource
}

// 实现GenericInformer的Informer方法
func (d *dynamicInformer) Informer() cache.SharedIndexInformer {
	return d.informer
}

// 实现GenericInformer的Lister方法,使用dynamicInformer的indexer和gvr构造以Lister
func (d *dynamicInformer) Lister() cache.GenericLister {
	return dynamiclister.NewRuntimeObjectShim(dynamiclister.New(d.informer.GetIndexer(), d.gvr))
}

4. client-go dynamic代码示例

package main

import (
 	"context"
	"flag"
	"fmt"
 	apiv1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/client-go/dynamic"
  	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
	"log"
	"path/filepath"
 )

func main() {
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home,".kube", "config_local"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig","","absolute path to the kubeconfig file")
	}
	flag.Parse()

	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		log.Fatal(err)
	}

	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		log.Fatal(err)
 	}

	// dynamicClient的唯一关联方法所需的入参
	gvr := schema.GroupVersionResource{Version: "v1", Resource: "pods"}

	// 使用dynamicClient的查询列表方法,查询指定namespace下的所有pod,
	// 注意此方法返回的数据结构类型是UnstructuredList
	unstructObj, err := dynamicClient.Resource(gvr).Namespace("kube-system").List(context.TODO(),metav1.ListOptions{Limit: 100})
	if err != nil {
		log.Fatal(err)
	}

	podList := &apiv1.PodList{}
	err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.UnstructuredContent(), podList)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("namespace\t Status\t\t name")
	for _, item := range podList.Items {
		fmt.Printf("%v\t %v\t %v\n",
			item.Namespace,
			item.Status.Phase,
			item.Name,
		)
	}


}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值