前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >避坑指南 | 我非要把这个bug优雅的解决掉

避坑指南 | 我非要把这个bug优雅的解决掉

作者头像
我的小碗汤
发布2019-11-14 15:28:59
1.4K0
发布2019-11-14 15:28:59
举报
文章被收录于专栏:我的小碗汤我的小碗汤

腾讯云服务器http://t.cn/AirXAQ1X

之前写过手把手教你编写一个operator在中间件容器化中的实践,以及自定义代码生成:

https://liabio.blog.csdn.net/article/details/93620155

记录了编写redis-operator的过程,达到自动操作redis集群,达到自动化运维的目的。

最近搞正在搞给redis operator加记录events事件功能时,报错了(可以说每次加功能都没有顺顺利利的,但这又如何,奈何它也挡不住洒家探索的脚步)报错如下:

代码语言:javascript
复制
Could not construct reference to ***due to: ‘no kind is registered for the type *** in scheme... ’

记录事件events代码如下:

可以一路跟踪代码到client-go/tools/reference/ref.go中:

可以看到45行,其实强转为v1.ObjectReference,也是不会报错的。所以我们可以构建如下的结构体:

代码像这样子:

代码语言:javascript
复制
ref := &v1.ObjectReference{
            APIVersion: controllerKind.Group + "/" + controllerKind.Version,
            Kind: controllerKind.Kind,
            Name: redisCluster.Name,
            Namespace: redisCluster.Namespace,
            UID:redisCluster.UID,
            ResourceVersion: redisCluster.ResourceVersion,
        }
rco.eventRecorder.Eventf(ref, v1.EventTypeNormal, "CreateCluster", "create cluster start")

但每次记录events前,都写这么一坨,显然是逼格很低的,况且纵观k8s源码,到处都是直接传pod、svc等内置类型(其实现了runtime.Object),随手一找就看到下面这个样子,将&svc直接传入,优雅很多。

那么该怎么搞才能直接把redisCluster结构体传进去不报错呢?更接近原生代码风格,更优雅呢?我们先分析下面的源码:

代码语言:javascript
复制
// GetReference returns an ObjectReference which refers to the given
// object, or an error if the object doesn't follow the conventions
// that would allow this.
// TODO: should take a meta.Interface see http://issue.k8s.io/7127
func GetReference(scheme *runtime.Scheme, obj runtime.Object) (*v1.ObjectReference, error) {
    if obj == nil {
        return nil, ErrNilObject
    }
    if ref, ok := obj.(*v1.ObjectReference); ok {
        // Don't make a reference to a reference.
        return ref, nil
    }

    gvk := obj.GetObjectKind().GroupVersionKind()

    // if the object referenced is actually persisted, we can just get kind from meta
    // if we are building an object reference to something not yet persisted, we should fallback to scheme
    kind := gvk.Kind
    if len(kind) == 0 {
        // TODO: this is wrong
        gvks, _, err := scheme.ObjectKinds(obj)
        if err != nil {
            return nil, err
        }
        kind = gvks[0].Kind
    }

    // An object that implements only List has enough metadata to build a reference
    var listMeta metav1.Common
    objectMeta, err := meta.Accessor(obj)
    if err != nil {
        listMeta, err = meta.CommonAccessor(obj)
        if err != nil {
            return nil, err
        }
    } else {
        listMeta = objectMeta
    }

    // if the object referenced is actually persisted, we can also get version from meta
    version := gvk.GroupVersion().String()
    if len(version) == 0 {
        selfLink := listMeta.GetSelfLink()
        if len(selfLink) == 0 {
            return nil, ErrNoSelfLink
        }
        selfLinkUrl, err := url.Parse(selfLink)
        if err != nil {
            return nil, err
        }
        // example paths: /<prefix>/<version>/*
        parts := strings.Split(selfLinkUrl.Path, "/")
        if len(parts) < 4 {
            return nil, fmt.Errorf("unexpected self link format: '%v'; got version '%v'", selfLink, version)
        }
        if parts[1] == "api" {
            version = parts[2]
        } else {
            version = parts[2] + "/" + parts[3]
        }
    }

    // only has list metadata
    if objectMeta == nil {
        return &v1.ObjectReference{
            Kind:            kind,
            APIVersion:      version,
            ResourceVersion: listMeta.GetResourceVersion(),
        }, nil
    }

    return &v1.ObjectReference{
        Kind:            kind,
        APIVersion:      version,
        Name:            objectMeta.GetName(),
        Namespace:       objectMeta.GetNamespace(),
        UID:             objectMeta.GetUID(),
        ResourceVersion: objectMeta.GetResourceVersion(),
    }, nil
}

看到最后其实会帮助我们构造v1.ObjectReference对象,但前提是能正确获取到从传入CRD的objectMeta、kind、version,一开始的报错就是因为 scheme.ObjectKinds(obj)获取kind时返回error导致。

怎么才能正确获取到kind、version、objectMeta呢?可以查看官方提供的sample-controller,这也是最简单的通过CRD扩展的控制器示例:

代码语言:javascript
复制
// Create event broadcaster
// Add sample-controller types to the default Kubernetes Scheme so Events can be
// logged for sample-controller types.
utilruntime.Must(samplescheme.AddToScheme(scheme.Scheme))

创建事件广播器,将样本控制器类型添加到默认的Kubernetes方案,以便可以记录事件以获取样本控制器类型。

samplescheme.AddToScheme(scheme.Scheme)会去注册一些GroupVersion、kind等信息到Scheme,这样在记录events时才可以正确获取到。对应到我们的CRD,redisCluster对象,就应该是

代码语言:javascript
复制
utilruntime.Must(redisscheme.AddToScheme(scheme.Scheme))

这里的redisscheme是指向code-generator生成的clientset包下的versioned/scheme,register.go中

根据golang代码执行顺序可知,被调用时,init函数会被执行,从而调用 v1.AddToGroupVersion进行注册流程:

这样分析,顺便把代码修改一番后,重新调试,可以看到正确记录了events事件:

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

本文分享自 进击云原生 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档