Kubernetes 系统拥有众多资源,每一种资源就是一个资源类型,这些资源类型需要有统一的注册、存储、查询、管理等机制--Scheme资源注册表,其是一个内存型的资源注册表,拥有如下特点。

  • 支持注册多种资源类型,包括内部版本和外部版本;

  • 支持多种版本转换机制;

  • 支持不同资源的序列化/反序列化机制。

1、资源类型

Scheme 资源注册表支持两种资源类型的注册,分别是 UnversionedType 和 KnownType 资源类型。

GVK(资源组、资源版本、资源种类)在 Scheme 资源注册表中以 <group>/<version>,Kind=<kind> 的形式存在,其中对于 Kind 字段,在注册时如果不指定该字段的名称,那么默认通过反射获取类型的名称。

1.1 UnversionedType

无版本资源类型,它主要应用于某些没有版本的资源类型,该类型的资源对象并不需要进行转换。在目前的 Kubernetes 发行版本中,几乎所有的资源对象都拥有版本,但在 metav1 元数据中还有部分类型,它们既属于 meta.k8s.io/v1 又属于 UnversionedType 无版本资源类型,例如 Status、APIVersions、APIGroupList 等。

该类型资源对象通过 scheme.AddUnversionedTypes 方法进行注册。

1.2 KnownType

是目前 Kubernetes 最常用的资源类型,也可称其为“拥有版本的资源类型”。

该类型资源对象通过 scheme.AddKnownTypes 方法进行注册。

2、注册表数据结构

type Scheme struct {
    gvkToType map[schema.GroupVersionKind]reflect.Type
    typeToGVK map[reflect.Type][]schema.GroupVersionKind
    unversionedTypes map[reflect.Type]schema.GroupVersionKind
    unversionedKinds map[string]reflect.Type
    fieldLabelConversionFuncs map[schema.GroupVersionKind]FieldLabelConversionFunc
    defaulterFuncs map[reflect.Type]func(interface{})
    converter *conversion.Converter
    versionPriority map[string][]string
    observedVersions []schema.GroupVersion
    schemeName string
}

如上代码所示,Scheme 资源注册表数据结构有4个 map 结构:

  • gvkToType:存储 GVK 与 Type 的映射关系;

  • typeToGVK:存储 Type 与 GVK 的映射关系,一个 Type 会对应一个或多个 GVK;

  • unversionedTypes:存储 UnversionedType 与 GVK 的映射关系;

  • unversionedKinds:存储 Kind 名称与 UnversionedType 的映射关系。

2.1 注册方法

在 Scheme 资源注册表中,不同的资源类型使用的注册方法不同:

  • scheme.AddUnversionedTypes:注册 UnversionedType 资源类型;

    func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Object) {
        s.addObservedVersion(version)
        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
        }
    }
  • scheme.AddKnownTypes:注册 KnownType 资源类型;

    func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
        s.addObservedVersion(gv)
        for _, obj := range types {
            t := reflect.TypeOf(obj)
            if t.Kind() != reflect.Pointer {
                panic("All types must be pointers to structs.")
            }
            t = t.Elem()
            s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
        }
    }
  • scheme.AddKnownTypeWithName:注册 KnownType 资源类型,须指定资源的 Kind 资源种类名称。

    func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
        s.addObservedVersion(gvk.GroupVersion())
        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.Pointer {
            panic("All types must be pointers to structs.")
        }
        t = t.Elem()
        if t.Kind() != reflect.Struct {
            panic("All types must be pointers to structs.")
        }
    ​
        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))
        }
    ​
        s.gvkToType[gvk] = t
    ​
        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)
            }
        }
    }

2.2 查询方法

在运行过程中,kube-apiserver 组件常对 Scheme 资源注册表进行查询:

  • scheme.KnownTypes:查询注册表中指定GV下的资源类型

    func (s *Scheme) KnownTypes(gv schema.GroupVersion) map[string]reflect.Type {
        types := make(map[string]reflect.Type)
        for gvk, t := range s.gvkToType {
            if gv != gvk.GroupVersion() {
                continue
            }
    ​
            types[gvk.Kind] = t
        }
        return types
    }
  • scheme.AllKnownTypes:查询注册表中所有 GVK 下的资源类型

    func (s *Scheme) AllKnownTypes() map[schema.GroupVersionKind]reflect.Type {
        return s.gvkToType
    }
  • scheme.ObjectKinds:查询资源对象所对应的 GVK,一个资源对象可能存在多个 GVK

    func (s *Scheme) ObjectKinds(obj Object) ([]schema.GroupVersionKind, bool, error) {
        // Unstructured objects are always considered to have their declared GVK
        if _, ok := obj.(Unstructured); ok {
            // we require that the GVK be populated in order to recognize the object
            gvk := obj.GetObjectKind().GroupVersionKind()
            if len(gvk.Kind) == 0 {
                return nil, false, NewMissingKindErr("unstructured object has no kind")
            }
            if len(gvk.Version) == 0 {
                return nil, false, NewMissingVersionErr("unstructured object has no version")
            }
            return []schema.GroupVersionKind{gvk}, false, nil
        }
    ​
        v, err := conversion.EnforcePtr(obj)
        if err != nil {
            return nil, false, err
        }
        t := v.Type()
    ​
        gvks, ok := s.typeToGVK[t]
        if !ok {
            return nil, false, NewNotRegisteredErrForType(s.schemeName, t)
        }
        _, unversionedType := s.unversionedTypes[t]
    ​
        return gvks, unversionedType, nil
    }
  • scheme.New:查询 GVK 所对应的资源对象

    func (s *Scheme) New(kind schema.GroupVersionKind) (Object, error) {
        if t, exists := s.gvkToType[kind]; exists {
            return reflect.New(t).Interface().(Object), nil
        }
    ​
        if t, exists := s.unversionedKinds[kind.Kind]; exists {
            return reflect.New(t).Interface().(Object), nil
        }
        return nil, NewNotRegisteredErrForKind(s.schemeName, kind)
    }
  • scheme.IsGroupRegistered:判断指定的资源组是否已经注册

    func (s *Scheme) IsGroupRegistered(group string) bool {
        for _, observedVersion := range s.observedVersions {
            if observedVersion.Group == group {
                return true
            }
        }
        return false
    }
  • scheme.IsVersionRegistered:判断指定的 GV 是否已经注册

    func (s *Scheme) IsVersionRegistered(version schema.GroupVersion) bool {
        for _, observedVersion := range s.observedVersions {
            if observedVersion == version {
                return true
            }
        }
    ​
        return false
    }
  • scheme.Recognizes:判断指定的 GVK 是否已经注册

    func (s *Scheme) Recognizes(gvk schema.GroupVersionKind) bool {
        _, exists := s.gvkToType[gvk]
        return exists
    }
  • scheme.IsUnversioned:判断指定的资源对象是否属于 UnversionedType 类型

    func (s *Scheme) IsUnversioned(obj Object) (bool, bool) {
        v, err := conversion.EnforcePtr(obj)
        if err != nil {
            return false, false
        }
        t := v.Type()
    ​
        if _, ok := s.typeToGVK[t]; !ok {
            return false, false
        }
        _, ok := s.unversionedTypes[t]
        return ok, true
    }