k8s 资源注册表

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 main

import (
    "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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值