【问题标题】:Kubernetes Fake Client doesn't handle GenerateName in ObjectMetaKubernetes Fake Client 不处理 ObjectMeta 中的 GenerateName
【发布时间】:2021-10-18 00:24:34
【问题描述】:

当使用 Kubernetes Fake Client 编写单元测试时,我注意到它无法创建两个相同的对象,它们的 ObjectMeta.GenerateName 字段设置为某个字符串。一个真正的集群接受这个规范并为每个对象生成一个唯一的名称。

运行以下测试代码:

package main

import (
    "context"
    "testing"

    "github.com/stretchr/testify/assert"
    corev1 "k8s.io/api/core/v1"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes/fake"
)

func TestFake(t *testing.T) {
    ctx := context.Background()
    client := fake.NewSimpleClientset()

    _, err := client.CoreV1().Secrets("default").Create(ctx, &corev1.Secret{
        ObjectMeta: metav1.ObjectMeta{
            GenerateName: "generated",
        },
        StringData: map[string]string{"foo": "bar"},
    }, metav1.CreateOptions{})
    assert.NoError(t, err)

    _, err = client.CoreV1().Secrets("default").Create(ctx, &corev1.Secret{
        ObjectMeta: metav1.ObjectMeta{
            GenerateName: "generated",
        },
        StringData: map[string]string{"foo": "bar"},
    }, metav1.CreateOptions{})
    assert.NoError(t, err)
}

失败

--- FAIL: TestFake (0.00s)
    /Users/mihaitodor/Projects/kubernetes/main_test.go:44: 
            Error Trace:    main_test.go:44
            Error:          Received unexpected error:
                            secrets "" already exists
            Test:           TestFake
FAIL
FAIL    kubernetes  0.401s
FAIL

【问题讨论】:

    标签: unit-testing go kubernetes client-go


    【解决方案1】:

    根据thisGitHub问题评论:

    假客户端不会尝试复制服务器端行为 如验证、名称生成、uid 分配等,如果您愿意 测试类似的东西,你可以添加反应器来模拟这种行为。

    要添加所需的反应器,我们可以在创建corev1.Secret 对象之前插入以下代码:

    client.PrependReactor(
        "create", "*",
        func(action k8sTesting.Action) (handled bool, ret runtime.Object, err error) {
            ret = action.(k8sTesting.CreateAction).GetObject()
            meta, ok := ret.(metav1.Object)
            if !ok {
                return
            }
    
            if meta.GetName() == "" && meta.GetGenerateName() != "" {
                meta.SetName(names.SimpleNameGenerator.GenerateName(meta.GetGenerateName()))
            }
    
            return
        },
    )
    

    里面有一些陷阱:

    • Clientset 包含一个嵌入的Fake 结构,它具有我们需要为此用例调用的PrependReactor 方法(还有一些其他的)。创建此类对象时会调用此代码here
    • PrependReactor 方法有 3 个参数:verbresourcereaction。对于verbresource,我找不到任何命名常量,因此,在这种情况下,“create”和“secrets”(奇怪的是它不是“secret”)似乎是它们的正确值,如果我们想要超级具体,但在这种情况下将resource 设置为“*”应该是可以接受的。
    • reaction 参数是ReactionFunc 类型,它接受Action 作为参数并返回handledreterr。经过一番挖掘,我注意到action 参数将被强制转换为CreateAction,其中GetObject() 方法返回一个runtime.Object 实例,可以将其强制转换为metav1.Object。这个接口允许我们获取和设置底层对象的各种元数据字段。根据需要设置对象Name字段后,我们要返回handled = falseret = mutatedObjecterr = nil来指示调用代码执行剩余的reactor。
    • 挖掘 apiserver 代码,我注意到使用 names.SimpleNameGenerator.GenerateName 实用程序的 ObjectMeta.GenerateName 字段中的 ObjectMeta.Name 字段 is generated

    【讨论】:

      猜你喜欢
      • 2019-08-06
      • 1970-01-01
      • 2019-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-10
      相关资源
      最近更新 更多