• 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(), 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

  • 相关阅读:
    Oracle归档日志暴增排查优化
    面向对象设计原则
    刷题分析工具
    常用的分布式ID解决方案原理解析
    「AI知多少」第一期推荐《生命3.0》
    linux scsi命令读取文件
    POM配置
    在职场上有多少人输在了不会用Python数据分析
    大数据从入门到实战-HDFS-JAVA接口之删除文件
    NLP相关
  • 原文地址:https://blog.csdn.net/fengcai_ke/article/details/126690047