k8s拥有众多资源,为了统一管理这些资源,k8s实现了资源注册表,其是一个内存型的注册表,其支持注册多种资源类型,包括内部版本和
外部版本,支持多版本转换,支持资源的序列化和反序列化。
k8s的资源注册表支持两种类型资源的注册,介绍如下:
unversionedType: 无版本资源类型,目前的k8s中,大部分资源都拥有版本,只有metav1中部分类型是无版本资源类型
KnownType: 有版本资源类型,比如pod,service等都是有版本资源类型
资源注册表定义
//k8s.io/apimachinery/pkg/runtime/scheme.go:
type Scheme struct {
// versionMap allows one to figure out the go type of an object with
// the given version and name.
gvkToType map[schema.GroupVersionKind]reflect.Type// typeToGroupVersion allows one to find metadata for a given go object.
// The reflect.Type we index by should *not* be a pointer.
typeToGVK map[reflect.Type][]schema.GroupVersionKind// unversionedTypes are transformed without conversion in ConvertToVersion.
unversionedTypes map[reflect.Type]schema.GroupVersionKind// unversionedKinds are the names of kinds that can be created in the context of any group
// or version
// TODO: resolve the status of unversioned types.
unversionedKinds map[string]reflect.Type// Map from version and resource to the corresponding func to convert
// resource field labels in that version to internal version.
fieldLabelConversionFuncs map[schema.GroupVersionKind]FieldLabelConversionFunc// defaulterFuncs is an array of interfaces to be called with an object to provide defaulting
// the provided object must be a pointer.
defaulterFuncs map[reflect.Type]func(interface{})// converter stores all registered conversion functions. It also has
// default converting behavior.
converter *conversion.Converter// versionPriority is a map of groups to ordered lists of versions for those groups indicating the
// default priorities of these versions as registered in the scheme
versionPriority map[string][]string// observedVersions keeps track of the order we've seen versions during type registration
observedVersions []schema.GroupVersion// schemeName is the name of this scheme. If you don't specify a name, the stack of the NewScheme caller will be used.
// This is useful for error reporting to indicate the origin of the scheme.
schemeName string
}
gvkToType: 存储gvk到type的映射关系
typeToGVK: 存储type到gvk的映射关系,value是数组类似,说明一个type可能会对应多个gvk,比如一个type可能会有多个版本,就会对应多个gvk
unversionedTypes: 存储无版本type到gvk的映射关系
unversionedKinds: 存储king到type的映射关系
fieldLabelConversionFuncs: 存储gvk到域转换函数的映射关系
defaulterFuncs: 存储type对应的默认函数,如果用户没有指定某个字段的值,则会调用默认函数给此字段赋值
converter: 版本转换器
versionPriority: 存储group对应的所有版本,在前面的版本优先级高
observedVersions: 存储所有注册的gv信息
资源注册函数
可以使用不同的资源注册函数注册不同的资源。
1. AddKnownTypes 注册有版本资源
func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
//将gv保存到 Scheme.observedVersions
s.addObservedVersion(gv)
//遍历传进来的types
for _, obj := range types {
//通过go反射机制获取type的类型
t := reflect.TypeOf(obj)
//t必须是指针
if t.Kind() != reflect.Ptr {
panic("All types must be pointers to structs.")
}
//通过Elem获取指针指向的内容
t = t.Elem()
//t.Name()获取类型名字
//gv.WithKind根据传入的name构成gvk
//最后调用AddKnownTypeWithName将gvk存入schema
s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
}
}//这个很简单,如果版本为0或者是内部版本,则返回,已经存在则返回,
//最后将版本信息保存到数组 observedVersions
func (s *Scheme) addObservedVersion(version schema.GroupVersion) {
if len(version.Version) == 0 || version.Version == APIVersionInternal {
return
}
for _, observedVersion := range s.observedVersions {
if observedVersion == version {
return
}
}s.observedVersions = append(s.observedVersions, version)
}
2. AddKnownTypeWithName 带着名字注册有版本资源
func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
//将gv保存到 Scheme.observedVersions,如果已经存在,则返回
s.addObservedVersion(gvk.GroupVersion())
//通过go反射机制获取类型
t := reflect.TypeOf(obj)
if len(gvk.Version) == 0 {
panic(fmt.Sprintf("version is required on all types: %s %v", gvk, t))
}
//类型必须是指针
if t.Kind() != reflect.Ptr {
panic("All types must be pointers to structs.")
}
//通过Elem获取指针指向的内容
t = t.Elem()
//内容必须是struct类型
if t.Kind() != reflect.Struct {
panic("All types must be pointers to structs.")
}
//如果gvk已经存在,并且存的值和要存的值不一致,则panic
if oldT, found := s.gvkToType[gvk]; found && oldT != t {
panic(fmt.Sprintf("Double registration of different types for %v: old=%v.%v, new=%v.%v in scheme %q", gvk, oldT.PkgPath(), oldT.Name(), t.PkgPath(), t.Name(), s.schemeName))
}
//保存gvk到t的映射关系
s.gvkToType[gvk] = t
//遍历t的数组,如果对应的gvk已经存在则返回
for _, existingGvk := range s.typeToGVK[t] {
if existingGvk == gvk {
return
}
}
//如果不存在则保存
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)// if the type implements DeepCopyInto(<obj>), register a self-conversion
if m := reflect.ValueOf(obj).MethodByName("DeepCopyInto"); m.IsValid() && m.Type().NumIn() == 1 && m.Type().NumOut() == 0 && m.Type().In(0) == reflect.TypeOf(obj) {
if err := s.AddGeneratedConversionFunc(obj, obj, func(a, b interface{}, scope conversion.Scope) error {
// copy a to b
reflect.ValueOf(a).MethodByName("DeepCopyInto").Call([]reflect.Value{reflect.ValueOf(b)})
// clear TypeMeta to match legacy reflective conversion
b.(Object).GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
return nil
}); err != nil {
panic(err)
}
}
}
3. AddUnversionedTypes 注册无版本资源
func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Object) {
//将gv保存到 Scheme.observedVersions,如果已经存在,则返回
s.addObservedVersion(version)
//无版本资源也要调用AddKnownTypes进行注册
s.AddKnownTypes(version, types...)
for _, obj := range types {
t := reflect.TypeOf(obj).Elem()
gvk := version.WithKind(t.Name())
s.unversionedTypes[t] = gvk
if old, ok := s.unversionedKinds[gvk.Kind]; ok && t != old {
panic(fmt.Sprintf("%v.%v has already been registered as unversioned kind %q - kind name must be unique in scheme %q", old.PkgPath(), old.Name(), gvk, s.schemeName))
}
s.unversionedKinds[gvk.Kind] = t
}
}
版本优先级设置
每种资源可能有多种版本,如果没有指定版本得有个默认版本,默认版本就是这里设置的优先级高的版本
func (s *Scheme) SetVersionPriority(versions ...schema.GroupVersion) error {
groups := sets.String{}
order := []string{}
for _, version := range versions {
if len(version.Version) == 0 || version.Version == APIVersionInternal {
return fmt.Errorf("internal versions cannot be prioritized: %v", version)
}groups.Insert(version.Group)
order = append(order, version.Version)
}
//group只能为1,即只能为同一个group下的版本排序
if len(groups) != 1 {
return fmt.Errorf("must register versions for exactly one group: %v", strings.Join(groups.List(), ", "))
}s.versionPriority[groups.List()[0]] = order
return nil
}
代码示例
下面通过代码展示如何将资源注册到资源注册表
package mainimport (
"fmt"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)func main() {
coreGV := schema.GroupVersion{Group: "", Version: "v1"}
extensionGV := schema.GroupVersion{Group: "extensions", Version: "v1beta1"}
coreInternalGV := schema.GroupVersion{Group: "", Version: runtime.APIVersionInternal}
Unversioned := schema.GroupVersion{Group: "", Version: "v1"}
scheme := runtime.NewScheme()
scheme.AddKnownTypes(coreGV, &corev1.Pod{})
scheme.AddKnownTypes(extensionGV, &appsv1.DaemonSet{})
scheme.AddKnownTypes(coreInternalGV, &corev1.Pod{})
scheme.AddUnversionedTypes(Unversioned, &metav1.Status{})for key, value := range scheme.AllKnownTypes() {
fmt.Println("gvk:", key, "type:", value)
}
}
上述代码的意图可以使用下面图片形象的表达出来
执行上述代码
root@test:~/kubernetes-1.22.2# go run test.go
gvk: /v1, Kind=Pod type: v1.Pod
gvk: extensions/v1beta1, Kind=DaemonSet type: v1.DaemonSet
gvk: /__internal, Kind=Pod type: v1.Pod
gvk: /v1, Kind=Status type: v1.Status
为什么println的是gvk,打印出来的是/v1, Kind=Status这种格式,因为gvk实现了String()函数
func (gvk GroupVersionKind) String() string {
return gvk.Group + "/" + gvk.Version + ", Kind=" + gvk.Kind
}