欢迎关注我的公众号:

kubectl源码分析之diff_golang

 目前刚开始写一个月,一共写了18篇原创文章,文章目录如下:

​istio多集群探秘,部署了50次多集群后我得出的结论​

​istio多集群链路追踪,附实操视频​

​istio防故障利器,你知道几个,istio新手不要读,太难!​

​istio业务权限控制,原来可以这么玩​

​istio实现非侵入压缩,微服务之间如何实现压缩​

​不懂envoyfilter也敢说精通istio系列-http-rbac-不要只会用AuthorizationPolicy配置权限​

​不懂envoyfilter也敢说精通istio系列-02-http-corsFilter-不要只会vs​

​不懂envoyfilter也敢说精通istio系列-03-http-csrf filter-再也不用再代码里写csrf逻辑了​

​不懂envoyfilter也敢说精通istio系列http-jwt_authn-不要只会RequestAuthorization​

​不懂envoyfilter也敢说精通istio系列-05-fault-filter-故障注入不止是vs​

​不懂envoyfilter也敢说精通istio系列-06-http-match-配置路由不只是vs​

​不懂envoyfilter也敢说精通istio系列-07-负载均衡配置不止是dr​

​不懂envoyfilter也敢说精通istio系列-08-连接池和断路器​

​不懂envoyfilter也敢说精通istio系列-09-http-route filter​

​不懂envoyfilter也敢说精通istio系列-network filter-redis proxy​

​不懂envoyfilter也敢说精通istio系列-network filter-HttpConnectionManager​

​不懂envoyfilter也敢说精通istio系列-ratelimit-istio ratelimit完全手册​

 

————————————————

type DiffOptions struct {//diff结构体
FilenameOptions resource.FilenameOptions

ServerSideApply bool
ForceConflicts bool

OpenAPISchema openapi.Resources
DiscoveryClient discovery.DiscoveryInterface
DynamicClient dynamic.Interface
DryRunVerifier *apply.DryRunVerifier
CmdNamespace string
EnforceNamespace bool
Builder *resource.Builder
Diff *DiffProgram
}
func NewDiffOptions(ioStreams genericclioptions.IOStreams) *DiffOptions {
return &DiffOptions{//创建diff结构体
Diff: &DiffProgram{
Exec: exec.New(),
IOStreams: ioStreams,
},
}
}
//创建diff命令
func NewCmdDiff(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
options := NewDiffOptions(streams)//初始化结构体
cmd := &cobra.Command{//创建cobra命令
Use: "diff -f FILENAME",
DisableFlagsInUseLine: true,
Short: i18n.T("Diff live version against would-be applied version"),
Long: diffLong,
Example: diffExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd))//准备方法
cmdutil.CheckErr(validateArgs(cmd, args))//校验
cmdutil.CheckErr(options.Run())//运行
},
}

usage := "contains the configuration to diff"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)//文件选项
cmdutil.AddServerSideApplyFlags(cmd)//serverside选项

return cmd
}
//准备
func (o *DiffOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
var err error

err = o.FilenameOptions.RequireFilenameOrKustomize()//文件选项是必须的
if err != nil {
return err
}

o.ServerSideApply = cmdutil.GetServerSideApplyFlag(cmd)//获取server-side选项
o.ForceConflicts = cmdutil.GetForceConflictsFlag(cmd)//获取force-conflicts选项
if o.ForceConflicts && !o.ServerSideApply {//如果指定force-conflicts选项则server-side选项为必须
return fmt.Errorf("--force-conflicts only works with --server-side")
}

if !o.ServerSideApply {//如果没有设置server-side选项
o.OpenAPISchema, err = f.OpenAPISchema()//获取OpenAPISchema
if err != nil {
return err
}
}

o.DiscoveryClient, err = f.ToDiscoveryClient()//设置DiscoveryClient
if err != nil {
return err
}

o.DynamicClient, err = f.DynamicClient()//设置DynamicClient
if err != nil {
return err
}

o.DryRunVerifier = &apply.DryRunVerifier{//设置干跑校验器
Finder: cmdutil.NewCRDFinder(cmdutil.CRDFromDynamic(o.DynamicClient)),
OpenAPIGetter: o.DiscoveryClient,
}

o.CmdNamespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()//设置namespace和enforceNamespace
if err != nil {
return err
}

o.Builder = f.NewBuilder()//设置builder
return nil
}
//校验
func validateArgs(cmd *cobra.Command, args []string) error {
if len(args) != 0 {//参数必须是0个
return cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
}
return nil
}
//运行
func (o *DiffOptions) Run() error {
differ, err := NewDiffer("LIVE", "MERGED")//构造differ
if err != nil {
return err
}
defer differ.TearDown()//清理diff

printer := Printer{}//构造printer

r := o.Builder.
Unstructured().
NamespaceParam(o.CmdNamespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
Flatten().
Do()//构造result对象
if err := r.Err(); err != nil {//result有错误返回
return err
}

err = r.Visit(func(info *resource.Info, err error) error {//visit result
if err != nil {
return err
}

if err := o.DryRunVerifier.HasSupport(info.Mapping.GroupVersionKind); err != nil {//校验是否支持干跑
return err
}

local := info.Object.DeepCopyObject()//拷贝info.Object
for i := 1; i <= maxRetries; i++ {//最大尝试次数maxRetries,循环
if err = info.Get(); err != nil {//获取info
if !errors.IsNotFound(err) {//如果是非找到错误,返回
return err
}
info.Object = nil//如果info没找到,info.Object设为空
}

force := i == maxRetries//如果尝试次数到达最大重试次数,则输出告警
if force {
klog.Warningf(
"Object (%v: %v) keeps changing, diffing without lock",
info.Object.GetObjectKind().GroupVersionKind(),
info.Name,
)
}
obj := InfoObject{//构造InfoObject对象
LocalObj: local,
Info: info,
Encoder: scheme.DefaultJSONEncoder(),
OpenAPI: o.OpenAPISchema,
Force: force,
ServerSideApply: o.ServerSideApply,
ForceConflicts: o.ForceConflicts,
}

err = differ.Diff(obj, printer)//准备diff
if !isConflict(err) {//入股欧式非冲突错误,跳出循环
break
}
}
return err
})
if err != nil {
return err
}

return differ.Run(o.Diff)//运行diff
}
type Differ struct {//differ 结构体
From *DiffVersion
To *DiffVersion
}
func NewDiffer(from, to string) (*Differ, error) {//创建differ
differ := Differ{}//构造differ结构体
var err error
differ.From, err = NewDiffVersion(from)//创建from
if err != nil {
return nil, err
}
differ.To, err = NewDiffVersion(to)//创建to
if err != nil {
differ.From.Dir.Delete()//删除from
return nil, err
}

return &differ, nil//返回
}
//准备diff
func (d *Differ) Diff(obj Object, printer Printer) error {
if err := d.From.Print(obj, printer); err != nil {//把yaml文件输出到from
return err
}
if err := d.To.Print(obj, printer); err != nil {//把yaml文件输出到to
return err
}
return nil
}

// Run runs the diff program against both directories.
func (d *Differ) Run(diff *DiffProgram) error {//运行diff
return diff.Run(d.From.Dir.Name, d.To.Dir.Name)
}

// TearDown removes both temporary directories recursively.
func (d *Differ) TearDown() {//清理diff
d.From.Dir.Delete() // Ignore error删除from目录
d.To.Dir.Delete() // Ignore error删除to目录
}
type DiffVersion struct {//diffVersion结构体
Dir *Directory
Name string
}

// NewDiffVersion creates a new DiffVersion with the named version.
func NewDiffVersion(name string) (*DiffVersion, error) {//DiffVersion
dir, err := CreateDirectory(name)//创建目录
if err != nil {
return nil, err
}
return &DiffVersion{//构造diffVersion
Dir: dir,
Name: name,
}, nil
}

func (v *DiffVersion) getObject(obj Object) (runtime.Object, error) {//获取对象
switch v.Name {
case "LIVE"://获取live对象
return obj.Live(), nil
case "MERGED"://获取merged对象
return obj.Merged()
}
return nil, fmt.Errorf("Unknown version: %v", v.Name)
}

// Print prints the object using the printer into a new file in the directory.
func (v *DiffVersion) Print(obj Object, printer Printer) error {//打印对象到文件
vobj, err := v.getObject(obj)//获取对象
if err != nil {
return err
}
f, err := v.Dir.NewFile(obj.Name())//创建文件
if err != nil {
return err
}
defer f.Close()
return printer.Print(vobj, f)//输出对象到文件
}
type Printer struct{}//printer结构体

// Print the object inside the writer w.
func (p *Printer) Print(obj runtime.Object, w io.Writer) error {//打印对象到文件
if obj == nil {
return nil
}
data, err := yaml.Marshal(obj)//对象转yaml
if err != nil {
return err
}
_, err = w.Write(data)//输出到文件
return err

}
type DiffProgram struct {//diffProgram结构体
Exec exec.Interface
genericclioptions.IOStreams
}

func (d *DiffProgram) getCommand(args ...string) exec.Cmd {//获取命令
diff := ""
if envDiff := os.Getenv("KUBECTL_EXTERNAL_DIFF"); envDiff != "" {//获取环境变量
diff = envDiff
} else {//环境变量不存在则用diff
diff = "diff"
args = append([]string{"-u", "-N"}, args...)//设置参数
}

cmd := d.Exec.Command(diff, args...)//获取cmd
cmd.SetStdout(d.Out)
cmd.SetStderr(d.ErrOut)

return cmd//返回cmd
}

// Run runs the detected diff program. `from` and `to` are the directory to diff.
func (d *DiffProgram) Run(from, to string) error {//运行diffProgram
return d.getCommand(from, to).Run()
}
type Directory struct {//directory结构体
Name string
}

// CreateDirectory does create the actual disk directory, and return a
// new representation of it.
func CreateDirectory(prefix string) (*Directory, error) {//创建临时目录
name, err := ioutil.TempDir("", prefix+"-")//创建临时目录
if err != nil {
return nil, err
}

return &Directory{//返回目录
Name: name,
}, nil
}

// NewFile creates a new file in the directory.
func (d *Directory) NewFile(name string) (*os.File, error) {//在目录下创建文件
return os.OpenFile(filepath.Join(d.Name, name), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0700)
}

// Delete removes the directory recursively.
func (d *Directory) Delete() error {//删除目录
return os.RemoveAll(d.Name)
}
type Object interface {//Object接口
Live() runtime.Object
Merged() (runtime.Object, error)

Name() string
}

// InfoObject is an implementation of the Object interface. It gets all
// the information from the Info object.
type InfoObject struct {//InfoObject结构体
LocalObj runtime.Object
Info *resource.Info
Encoder runtime.Encoder
OpenAPI openapi.Resources
Force bool
ServerSideApply bool
ForceConflicts bool
}

var _ Object = &InfoObject{}

// Returns the live version of the object
获取live对象
func (obj InfoObject) Live() runtime.Object {
return obj.Info.Object
}

// Returns the "merged" object, as it would look like if applied or
// created.
//获取merged对象
func (obj InfoObject) Merged() (runtime.Object, error) {
if obj.ServerSideApply {//如果指定server-side
data, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj.LocalObj)//把localObj编码为json
if err != nil {
return nil, err
}
options := metav1.PatchOptions{//设置patch选项
Force: &obj.ForceConflicts,
DryRun: []string{metav1.DryRunAll},
}
return resource.NewHelper(obj.Info.Client, obj.Info.Mapping).Patch(
obj.Info.Namespace,
obj.Info.Name,
types.ApplyPatchType,
data,
&options,
)//干跑patch到服务端,返回结果
}

// Build the patcher, and then apply the patch with dry-run, unless the object doesn't exist, in which case we need to create it.
if obj.Live() == nil {//如果live为空
// Dry-run create if the object doesn't exist.
return resource.NewHelper(obj.Info.Client, obj.Info.Mapping).Create(
obj.Info.Namespace,
true,
obj.LocalObj,
&metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}},
)//干跑创建到服务端,返回结果
}

var resourceVersion *string
if !obj.Force {//如果force为true,获取resourceVersion
accessor, err := meta.Accessor(obj.Info.Object)
if err != nil {
return nil, err
}
str := accessor.GetResourceVersion()
resourceVersion = &str
}

modified, err := util.GetModifiedConfiguration(obj.LocalObj, false, unstructured.UnstructuredJSONScheme)//获取yaml配置
if err != nil {
return nil, err
}

// This is using the patcher from apply, to keep the same behavior.
// We plan on replacing this with server-side apply when it becomes available.
patcher := &apply.Patcher{//创建patcher
Mapping: obj.Info.Mapping,
Helper: resource.NewHelper(obj.Info.Client, obj.Info.Mapping),
Overwrite: true,
BackOff: clockwork.NewRealClock(),
ServerDryRun: true,
OpenapiSchema: obj.OpenAPI,
ResourceVersion: resourceVersion,
}

_, result, err := patcher.Patch(obj.Info.Object, modified, obj.Info.Source, obj.Info.Namespace, obj.Info.Name, nil)//应用patch,返回结果
return result, err
}

func (obj InfoObject) Name() string {获取文件名称
group := ""
if obj.Info.Mapping.GroupVersionKind.Group != "" {设置group
group = fmt.Sprintf("%v.", obj.Info.Mapping.GroupVersionKind.Group)
}
return group + fmt.Sprintf(
"%v.%v.%v.%v",
obj.Info.Mapping.GroupVersionKind.Version,
obj.Info.Mapping.GroupVersionKind.Kind,
obj.Info.Namespace,
obj.Info.Name,
)//返回拼接的名称
}