作者/吴江法
kubectl源码阅读—get命令
问题:
1.kubectl get命令输出的表头是如何动态变化的?
2.kubectl get pod命令输出的pod的状态如:CrashLoopBackOff、Completed等是如何获取的?
3.kubectl是如何转换别名的?如get po,get svc等。
首先简单介绍cobra包的Command结构体,可以在该结构体的属性中找到关键的注释:
该结构体中最重要的两个属性成员:
1.子命令集合
2.命令主要的执行逻辑
kubectl是主命令,而get、describe、create等命令则是子命令。
在构建kubectl命令时,会添加各子命令:
执行主命令的入口:
command.Execute() — >ExecuteC() 在该方法中获取命令行参数:
再根据参数查询子命令:
最终使用找到的子命令,执行子命令核心逻辑:
在该核心逻辑中会执行Command的核心逻辑:
抱着本次的问题,主要看子命令get的实现逻辑:
在get命令的实现中,有一个很重要的结构体:
该结构体负责接收和保存get子命令的参数。结合pflag包,将参数写入。
子命令的核心逻辑:
这里主要看o.Run方法:
在该方法中,通过调试,可以发现,在执行下面这行代码时,kubectl调用接口拉取了数据:
该方法代码如下:
可以发现,这里使用了访问者模式。要明白它是如何调用接口的,得先弄清楚两个问题:
1)r是如何构建出来的?
2)r.visitor是什么?
下面是r的构建代码,典型的构建者模式:
回头看参数f,类型是cmdutil.Factory,是一个抽象工厂,是在cmd.go中构建传入的:
实现类是:
追踪找到NewBuilder():
查看Do()方法:
在该方法中,对r.visitor进行赋值,这里运用了装饰器模式,核心方法是b.visitorResult(),最终发现了一层层的装饰:
底层是DecoratedVisitor—>ContinueOnErrorVisitor—>FlattenListVisitor—>EagerVisitorList—>Selector
终于在Selector中找到了接口调用:
调试拿到数据,找到了开篇时的问题1,2的答案:原来输出哪些表头是接口返回的,pod的状态也是接口返回的。
表头:
这里产生了疑问:莫非kubectl拉取数据时调用的不是普通的接口?
继续调试,最终找到了答案:
接口是一样的接口,但是加了特殊的header,as=Table。
然后新的问题就是这个请求头是怎么加上去的?要回答这个问题,就得先追踪client的构建和传递:
这个Client是哪来的?
是调用了NewSelector方法获取的。
接着找到调用者,以及client的源头:
查看b.getClient方法:
requestTransforms是不是很眼熟:
最终找到:
该方法会在发起http请求前被回调:
以上,基本理顺了开篇的两个问题。
通过调试,找到了解开第三个问题的入口:
入口:b.resourceMappings(),查看该方法,找到方法的核心点:
继续跟进:
终于找到了抽象层,关键点在于restMapper是如何传入Builder对象中的。
可以查看NewBuilder:
关键点在于restClientGetter,该参数源头,继续回推:
那么factoryImpl中的clientGetter是哪来的?
可以发现最顶层的构建是:
由于中间是一层层对clientGetter的代理,可以直接找到最底层的调用:
找到对应的代理:
对应的KindFor方法:
以及最终的执行逻辑:
最终调试:
从数据中找到了答案:
通过
e.discoveryClient.ServerGroupsAndResources()能拉取到所有k8s资源的资源的别名。
LStack产品简介
面向行业应用开发商(ISV/SI)提供混合云/边缘云场景下云原生应用开发测试、交付、运维一站式服务,帮助企业采用云原生敏捷开发交付方法论,从而提高软件开发人员效率、减少运维成本,加快数字化转型,并最终实现业务创新。