Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extension mechanism for CRDMetadata registry | 新增扩展机制,支持注册自定义的 CRDMetadata #15

Open
sczyh30 opened this issue Nov 3, 2022 · 6 comments
Labels
good first issue Good for newcomers kind/feature Category issues or PRs related to feature request.

Comments

@sczyh30
Copy link
Member

sczyh30 commented Nov 3, 2022

Add extension mechanism for CRDMetadata registry, so that we could register CRDMetadata outside the control plane pkg.

Related code: https://github.com/opensergo/opensergo-control-plane/blob/91ef7e92e2745c646f294f248c5c4249ef488af2/pkg/controller/crd_meta.go


新增扩展机制,支持注册自定义的 CRDMetadata,以便其他 control plane 能够更方便地扩展使用 OpenSergo 控制面模块。

@sczyh30 sczyh30 added good first issue Good for newcomers kind/feature Category issues or PRs related to feature request. labels Nov 3, 2022
@shawnh2
Copy link

shawnh2 commented Jan 10, 2023

请问这个扩展机制期望实现的效果是怎么样的呢?

@jnan806
Copy link
Collaborator

jnan806 commented Jan 11, 2023

请问这个扩展机制期望实现的效果是怎么样的呢?

@ShawnHXH

当 OpenSergo Control Plane 接收到 OpenSergoClient 发起 subscribe 自定义 CRD 资源的请求时,OpenSergo Control Plane 对 K8s 中的 资源 addWatch 时,能够找到 对应自定义CRDMetadata,即 在 AddWatch 方法内部调用的 GetCrdMetadata` 方法能够找到对应的 自定义 CRDMetadata。

AddWatch 方法内部内部调用 GetCrdMetadata` 代码位于:

crdMetadata, crdSupports := GetCrdMetadata(target.Kind)

GetCrdMetadata 方法位于

func GetCrdMetadata(kind CRDKind) (*CRDMetadata, bool) {
// TODO: should we put a lock here?
data, exists := crdMetadataMap[kind]
return data, exists
}

GetCrdMetadata 方法能够找到 对应的 自定义 CRD 信息,的前提是提前向 crdMetadataMap 缓存 存入相关 CRDMetadata.

crdMetadataMap 缓存位于:

crdMetadataMap = map[CRDKind]*CRDMetadata{
FaultToleranceRuleKind: NewCRDMetadata(FaultToleranceRuleKind, func() client.Object {
return &v1alpha1.FaultToleranceRule{}
}),
RateLimitStrategyKind: NewCRDMetadata(RateLimitStrategyKind, func() client.Object {
return &v1alpha1.RateLimitStrategy{}
}),
ThrottlingStrategyKind: NewCRDMetadata(ThrottlingStrategyKind, func() client.Object {
return &v1alpha1.ThrottlingStrategy{}
}),
ConcurrencyLimitStrategyKind: NewCRDMetadata(ConcurrencyLimitStrategyKind, func() client.Object {
return &v1alpha1.ConcurrencyLimitStrategy{}
}),
CircuitBreakerStrategyKind: NewCRDMetadata(CircuitBreakerStrategyKind, func() client.Object {
return &v1alpha1.CircuitBreakerStrategy{}
}),
}
)

因此,最终效果就是,将 OpenSergo 自身不支持的、用户自定义的 CRDMetadata 注册 至crdMetadataMap 缓存。 然后在 OpenSergoClient 发起 subscribe 请求时,OpenSergo Control Plane 能 在 crdMetadata 缓存中够找到对应的 CRDMetadata 对应的资源信息并且返回。

PS: 自定义的 CRDMetadata 的缓存 设计时可以与crdMetadataMap 区分开,只需要在上面提到的 GetCrdMetadata方法 能够找到、并且 OpenSergo Control Plane 能够如期实现 自定义 CRD 资源的 订阅效果即可。

@jnan806
Copy link
Collaborator

jnan806 commented Jan 11, 2023

@ShawnHXH Are you interested in contributing it ? 😃

@shawnh2
Copy link

shawnh2 commented Jan 11, 2023

Sure, please assign it to me.

@shawnh2
Copy link

shawnh2 commented Jan 11, 2023

Hi, 我阐述一下初步的实现思路,若有不妥请指正。

GetCrdMetadata 方法不只在 KubernetesOperator.AddWatcher 中调用,在 KubernetesOperator.RegisterWatcher 方法中也有调用:

func (k *KubernetesOperator) RegisterWatcher(target model.SubscribeTarget) (*CRDWatcher, error) {
k.controllerMux.Lock()
defer k.controllerMux.Unlock()
var err error
existingWatcher, exists := k.controllers[target.Kind]
if exists {
if existingWatcher.HasSubscribed(target) {
// Target has been subscribed
return existingWatcher, nil
} else {
// Add subscribe to existing watcher
err = existingWatcher.AddSubscribeTarget(target)
if err != nil {
return nil, err
}
}
} else {
crdMetadata, crdSupports := GetCrdMetadata(target.Kind)

也就是说在 ControlPlane.handleSubscribeRequest 之前,自定义的 CRDMetadata 就要被注册。

考虑到上述 handler 是在 NewControlPlane 时创建,并按注册顺序执行:

func NewControlPlane() (*ControlPlane, error) {
cp := &ControlPlane{}
operator, err := controller.NewKubernetesOperator(cp.sendMessage)
if err != nil {
return nil, err
}
cp.server = transport.NewServer(uint32(10246), []model.SubscribeRequestHandler{cp.handleSubscribeRequest})

for _, handler := range s.subscribeHandlers {
err = handler(clientIdentifier, recvData, stream)
if err != nil {
// TODO: handle error
log.Printf("Failed to handle SubscribeRequest, err=%s\n", err.Error())
}
}

所以可以在 ControlPlane.handleSubscribeRequest 这个 handler 注册之前,再注册一个用于检测自定义 CRDMetadata 的 Handler,其基本功能如下:

  1. 检查 SubscribeTargetKind 是否为 OpenSergo 预定义的 metadata,若是则跳过
  2. 若不是,则新建一个 CRDMetadata 并存储

这里规定 crdMetadataMap 是存储 OpenSergo 预定义的 CRDMetadata,然后再另创建一个全局 map 变量存储自定义的 CRDMetadata

这样的话,基本需求即可实现。

但是,我还有一个问题:CRDMetadata 结构体是由两个字段组成的,其中第一个字段可以使用 SubscribeTarget.Kind,但第二个字段该如何初始化呢?

type CRDMetadata struct {
kind CRDKind
generator CRDGenerator
}

type CRDGenerator = func() client.Object

@jnan806
Copy link
Collaborator

jnan806 commented Jan 11, 2023

Hi, 我阐述一下初步的实现思路,若有不妥请指正。

cc @sczyh30 PTAL

@shawnh2 shawnh2 removed their assignment Jan 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers kind/feature Category issues or PRs related to feature request.
Projects
None yet
Development

No branches or pull requests

3 participants