简介

k8s大量使用了代码生成工具,但无奈文档很少,不太容易获得直观的认识,在此先记录总结一下deepcopy_gen工具的用途和使用方法。

deepcopy_gen 这个工具从名字上来看就大概可以猜到它是用于生成深拷贝的代码。比如代码中定义了一个类型Foo,那么使用这个工具就可以生成此类型的深拷贝方法。如果有大量自定义的类型,而且在使用过程中需要进行深拷贝,这个工具可以让我们可以非常多的代码。堪称神器。

实验

通过实际的代码能更加直观的了解这个工具

安装deepcopy_gen

1
2
3
4
git clone https://github.com/kubernetes/code-generator --depth=1
cd code-generator
go build -o deepcopy-gen cmd/deepcopy-gen/main.go
mv deepcopy-gen /usr/local/bin

下载demo代码

1
2
git clone https://github.com/lt90s/deepcopy-gen-demo
cd deepcopy-gen-demo

目录结构:

1
2
3
4
5
6
7
8
├── README.md
├── boilerplate.txt
├── deepcopy-gen
├── go.mod
└── types
    ├── doc.go
    ├── generated_deepcopy.go
    └── types.go

生成代码

1
2
3
deepcopy-gen -v 2 -h boilerplate.txt --bounding-dirs . -i github.com/lt90s/deepcopy-gen-demo/types -O generated_deepcopy
mv github.com/lt90s/deepcopy-gen-demo/types/generated_deepcopy.go types/
rm -rf github.com

执行此命令后会在当前目录生成github.com/lt90s/deepcopy-gen-demo/types/generated_deepcopy.go的文件,该文件便是生成的深拷贝方法的代码文件,将此文件移入types目录下便可。

分析

命令行分析

1
deepcopy-gen -v 2 -h boilerplate.txt --bounding-dirs . -i github.com/lt90s/deepcopy-gen-demo/types -O generated_deepcopy
  1. -v 2 指定输出内容的详细程度
  2. -h boilerplate.txt指定所有生成的文件的头部声明内容
  3. --bounding-dirs .指定生成目录为当前路径】
  4. -i github.com/lt90s/deepcopy-gen-demo/types指定此package需要进行代码生成
  5. -O generated_deepcopy指定生成的文件名称为generated_deepcopy.go

生成内容分析:

打开types/generated_deepcopy.go文件可以看到。FooType类型生成了三个拷贝方法

  1. func (in *FooType) DeepCopyInto(out *FooType)
  2. func (in *FooType) DeepCopy() *FooType
  3. func (in *FooType) DeepCopyBarInterface() BarInterface

但是BazType却没有生成深拷贝方法,为啥呢?下面会进行分析。

生成规则分析

通过命令行参数-i指定需要生成代码的package后,工具会对属于此包的文件进行处理(注意并不包括此包目录下的子目录,比如types/sub下的文件就不会处理,因为它是github.com/lt90s/deepcopy-gen-demo/types/sub)。有几种方式可以控制是否生成深拷贝代码

  1. 全局开关: 在文件中加入一行注释// +k8s:deepcopy-gen=package,通常在doc.go文件中表明需要检查该package下的所有自定义类型,默认情况下生成深拷贝方法
  2. 具体开关:在类型声明上加入一行注释// +k8s:deepcopy-gen=false或者// +k8s:deepcopy-gen=true,前者表示此类型不需要生成深拷贝方法,后者表示需要生成。当全局开关打开时,只需要在不需要深拷贝方法的类型声明前加入// +k8s:deepcopy-gen=false即可,这就是为什么BazType没有生成相应的深拷贝方法。

types/types.go文件中,go语言中的基本数据类型都是支持拷贝的,但是channelfunction不支持,可以试一下将注释取消,然后再执行deepcopy-gen

interface比较特殊,要让其支持深拷贝,必须在接口中声明DeepCopyInterfaceName() InterfaceName方法,其中InterfaceName是接口类型的名称。例如:

1
2
3
4
type BarInterface interface {
	DeepCopyBarInterface() BarInterface
	String() string
}

那么问题来了。如果有很多实现了这个接口类型,它们都需要深拷贝方法,那这个DeepCopyInterfaceName方法怎么生成?deepcopy-gen提供了// +k8s:deepcopy-gen:interfaces=github.com/foo/bar/pkg.InterfaceName这个指令,将其放在实现了InterfaceName接口的类型上面,工具就会自动生成DeepCopyInterfaceName的方法。例如:

1
2
3
4
5
// +k8s:deepcopy-gen:interfaces=github.com/lt90s/deepcopy-gen-demo/types.BarInterface
type FooType struct {
	a int
  ...
}

如果实现了多个接口。那么以,分隔各个接口。

demo中没有演示一个指令// +k8s:deepcopy-gen:nonpointer-interfaces=true。可以看到。默认情况下,生成的DeepCopyInterfaceName方法的接受者是指针类型的。当你不想要指针类型的接收者时,就可以使用此指令来达到目的。

总结

当自定义类型需要进行深拷贝时候,可以使用此工具来生成深拷贝方法。可以节省大量的重复代码,解放生产力。