前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >K8s源码分析(11)-资源到schema的注册

K8s源码分析(11)-资源到schema的注册

作者头像
TA码字
发布2021-11-30 15:42:58
1.7K1
发布2021-11-30 15:42:58
举报
文章被收录于专栏:TA码字TA码字

上一篇文章的内容中,我们主要进行介绍了 kubernetes schema 这个重要概念。从数据结构角度上看,其中包括了它是如何来维护 group/version/kind 和资源 model 的对应关系,资源 model 和默认值函数的对应关系,不同资源版本之间相互转化函数的对应关系等等。从实现接口角度看, schema 实现了一系列接口,从而具备了创建资源对象,给资源对象赋默认值,识别资源对象类型,完成资源对象本版之间的转换,完成资源的 label 标签转化等功能。在本篇文章里, 我们主要介绍不同版本的资源到 schema 对象中的注册。

SchemeBuilder对象

schemabuilder 对象用以完成资源在 schema 中的注册,其图解和源码如下:

代码语言:javascript
复制
// staging/src/k8s.io/apimachinery/pkg/runtime/scheme_builder.go

type SchemeBuilder []func(*Scheme) error

func (sb *SchemeBuilder) AddToScheme(s *Scheme) error {
  for _, f := range *sb {
    if err := f(s); err != nil {
      return err
    }
  }
  return nil
}

func (sb *SchemeBuilder) Register(funcs ...func(*Scheme) error) {
  for _, f := range funcs {
    *sb = append(*sb, f)
  }
}

func NewSchemeBuilder(funcs ...func(*Scheme) error) SchemeBuilder {
  var sb SchemeBuilder
  sb.Register(funcs...)
  return sb
}
  • schemabuilder 对象本质是一个函数数组集合,其中的函数入参为 schema 类型。
  • schemabuilder 对象含有 Register() 这个方法,其本质是向函数数组集合中再添加一个入参为 schema 类型的函数。
  • schemabuilder 对象有 AddToSchema() 这个方法,它的入参为 schema 对象,其本质是把该 schema 对象传入函数数组集合中的每一个函数中,然后分别运行。

资源的外部本版注册

这里我们以 apps/v1beta1 为例子,介绍该组下的 v1beta1 版本的资源是如何注册到 schema 中的,其图解如下:

由图解我们发现对于外部资源版本的注册包括资源 model 类型的注册,资源的初始化函数(即默认值函数)的注册,资源的 label 转换函数的注册,和内部版本相互转换函数的注册。

  • 资源 model 类型的注册源码如下:
代码语言:javascript
复制
// staging/src/k8s.io/api/apps/v1beta1/register.go

const GroupName = "apps"

var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1beta1"}

var (
  SchemeBuilder      = runtime.NewSchemeBuilder(addKnownTypes)
  localSchemeBuilder = &SchemeBuilder
  AddToScheme        = localSchemeBuilder.AddToScheme
)

func addKnownTypes(scheme *runtime.Scheme) error {
  scheme.AddKnownTypes(SchemeGroupVersion,
    &Deployment{},
    &DeploymentList{},
    &DeploymentRollback{},
    &Scale{},
    &StatefulSet{},
    &StatefulSetList{},
    &ControllerRevision{},
    &ControllerRevisionList{},
  )
  metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
  return nil
}


func NewSchemeBuilder(funcs ...func(*Scheme) error) SchemeBuilder {
  var sb SchemeBuilder
  sb.Register(funcs...)
  return sb
}

由上述的源代码之中,我们会发现,在源码文件 k8s.io/api/apps/v1beta1/register.go 中去创建了 schemebuilder 这个对象,并且设置了组为 apps, 设置了版本为 v1beta1,然后把属于apps/v1beta1 中的所有类型的资源 model 进行注册,例如我们非常熟悉的 deployment, statsfulset 等资源。

  • 资源默认值初始化函数和 label 转换函数的注册的源码如下:
代码语言:javascript
复制
// pkg/apis/apps/v1beta1/register.go

var (
  localSchemeBuilder = &appsv1beta1.SchemeBuilder
  AddToScheme        = localSchemeBuilder.AddToScheme
)

func init() {
  localSchemeBuilder.Register(addDefaultingFuncs, addConversionFuncs)
}

// pkg/apis/apps/v1beta1/defaults.go

func addDefaultingFuncs(scheme *runtime.Scheme) error {
  return RegisterDefaults(scheme)
}

// pkg/apis/apps/v1beta1/conversion.go

func addConversionFuncs(scheme *runtime.Scheme) error {
  // Add field label conversions for kinds having selectable nothing but ObjectMeta fields.
  if err := scheme.AddFieldLabelConversionFunc(SchemeGroupVersion.WithKind("StatefulSet"),
    func(label, value string) (string, string, error) {
      switch label {
      case "metadata.name", "metadata.namespace", "status.successful":
        return label, value, nil
      default:
        return "", "", fmt.Errorf("field label not supported for appsv1beta1.StatefulSet: %s", label)
      }
    }); err != nil {
    return err
  }

  return nil
}

由上述的源码代之中,我们会发现,在源码文件 pkg/apis/apps/v1beta1/register.go 中来引用了上面介绍的 schemabuilder 对象,然后在这个对象中添加 addDefaultingFuncs 函数作为资源的初始化函数,并添加 addConversionFuncs 这个函数作为资源的 label 转换函数。

  • 资源外部版本和内部版本相互转换函数注册的源码如下:
代码语言:javascript
复制
// pkg/apis/apps/v1beta1/zz_generated.conversion.go

func init() {
  localSchemeBuilder.Register(RegisterConversions)
}
func RegisterConversions(s *runtime.Scheme) error {
  ......
  if err := s.AddGeneratedConversionFunc((*v1beta1.Deployment)(nil), (*apps.Deployment)(nil), func(a, b interface{}, scope conversion.Scope) error {
    return Convert_v1beta1_Deployment_To_apps_Deployment(a.(*v1beta1.Deployment), b.(*apps.Deployment), scope)
  }); err != nil {
    return err
  }
  if err := s.AddGeneratedConversionFunc((*apps.Deployment)(nil), (*v1beta1.Deployment)(nil), func(a, b interface{}, scope conversion.Scope) error {
    return Convert_apps_Deployment_To_v1beta1_Deployment(a.(*apps.Deployment), b.(*v1beta1.Deployment), scope)
  }); err != nil {
    return err
  }
  ......
}

pkg/apis/apps/v1beta1/zz_generated.conversion.go 中引用了上面介绍的 schemabuilder 对象,然后在对象中添加 RegisterConversions 函数作为内外部本版转换函数,在该函数中定义了各个资源在当前版本和内部版本如何转换。

资源的内部本版注册

这里我们同样以 apps 组做为例子,介绍该组下内部版本资源是如何注册到 schema 中的,其图解如下:

由图解发现对于内部资源版本注册只包括资源 model 类型的注册,其源码如下:

代码语言:javascript
复制
// pkg/apis/apps/register.go

const GroupName = "apps"

var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}

var (
  SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes).
  AddToScheme = SchemeBuilder.AddToScheme
)

func addKnownTypes(scheme *runtime.Scheme) error {
  scheme.AddKnownTypes(SchemeGroupVersion,
    &DaemonSet{},
    &DaemonSetList{},
    &Deployment{},
    &DeploymentList{},
    &DeploymentRollback{},
    &autoscaling.Scale{},
    &StatefulSet{},
    &StatefulSetList{},
    &ControllerRevision{},
    &ControllerRevisionList{},
    &ReplicaSet{},
    &ReplicaSetList{},
  )
  return nil
}

// k8s.io/apimachinery/pkg/runtime/interfaces.go

const (
  APIVersionInternal = "__internal"
)

在源码文件 pkg/apis/apps/register.go 中进行了 schemebuilder 对象的创建,设置了组为 apps, 设置版本为内部版本,并把属于内部版本中的所有类型的资源 model 进行注册,例如我们非常熟悉的 deployment 资源, statsfulset 资源等等。

资源的内外部本版注册的驱动

这里我们同样以 apps 组作为例子从源码角度看,驱动整个内部版本资源和外部版本资源的注册如下:

代码语言:javascript
复制
// pkg/apis/apps/install/install.go

func init() {
  Install(legacyscheme.Scheme)
}

func Install(scheme *runtime.Scheme) {
  utilruntime.Must(apps.AddToScheme(scheme))
  utilruntime.Must(v1beta1.AddToScheme(scheme))
  utilruntime.Must(v1beta2.AddToScheme(scheme))
  utilruntime.Must(v1.AddToScheme(scheme))
  utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta2.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
}

在源代码文件 pkg/apis/apps/install/install.go 中,由 legacyscheme.Scheme 操作来得到一个 schema 对象,然后把 app 组下的所有版本的资源都进行注册,包括内部版本,以及 v1/v1beta1/v1beta2 等所有外部版本,如下图解展示了各个组下不同版本资源到 schema 中的注册。

目前先我们写到这里,在下一篇文章中我们继续来介绍资源的数据访问层,也就是我们常常讲的 DAO。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 TA码字 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档