如何在Kubernetes中返回自定义用户友好的错误消息?

yxyvkwin  于 2023-04-05  发布在  Kubernetes
关注(0)|答案(2)|浏览(131)

bounty将在2天后过期。回答此问题可获得+50声望奖励。dom1希望引起更多人关注此问题。

我有一个golang的后端,可以和k8s对话。我想重新定义从k8s得到的错误响应,并将其发送到前端。
我想为用户返回一个有意义的验证错误消息,当他添加一个无效的名称时,一些东西已经存在…
我想要一些通用的东西,而不是硬编码在每个端点的控制器。
我使用的是kubernetes/client-go
1.第一个错误
例如,假设我想将一个酒店添加到etcd,当我尝试添加酒店的名称时:hotel123已经存在了

  • 我收到此错误消息:\"hotel123\" already exists .
  • 我想要的:hotel123 already exists .

1.第二个错误
例如,假设我想在etcd中添加一个酒店,当我尝试添加酒店名称时:hotel_123,那是存在

  • 我收到此错误消息:\"hotel_123\" is invalid: metadata.name: Invalid value: \"hotel_123\": a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')" .
  • 我想要的:hotel_123 is invalid

如何返回自定义用户友好的错误消息?
PS:我有多个函数,所以验证应该是通用的。

3xiyfsfu

3xiyfsfu1#

一般来说(尽管有解决方法),如果要捕获错误以返回更有用的错误,则需要确保满足以下条件:
1.您正在捕获的错误具有meaningful type
1.您使用的Go版本〉= 1.13,随useful helper functions一起提供
在下面的例子中,我试图读取一个不存在的配置文件。我的代码检查返回的错误是fs.PathError,然后抛出它自己的更有用的错误。你可以将这个一般想法扩展到你的用例。

package main

import (
    "errors"
    "fmt"
    "io/fs"

    "k8s.io/client-go/tools/clientcmd"
)

func main() {

    var myError error

    config, originalError := clientcmd.BuildConfigFromFlags("", "/some/path/that/doesnt/exist")
    if originalError != nil {

        var pathError *fs.PathError

        switch {
        case errors.As(originalError, &pathError):

            myError = fmt.Errorf("there is no config file at %s", originalError.(*fs.PathError).Path)

        default:

            myError = fmt.Errorf("there was an error and it's type was %T", originalError)

        }

        fmt.Printf("%#v", myError)

    } else {

        fmt.Println("There was no error")
        fmt.Println(config)

    }

}

在调试过程中,您会发现%T化程序useful
下面是playground上的另一个示例,它使用了一个更通用的情况,因为playground只允许您使用标准库。
https://go.dev/play/p/p1qMiZlnqPK

qmb5sa22

qmb5sa222#

字符串err.Error()是您可以从Kubernetes服务器为用户获取的原始,有意义和最好的错误消息(或者您必须自己翻译)。

说明:

您需要超越kubernetes/client-go客户端库的表面。
每个客户端通过HTTP REST API与k8s服务端对话,k8s服务端返回的响应采用json,由client-go库对响应体进行解码,并尽可能将结果存储到object中。
至于你的案例,让我通过Namespace资源给予你一些例子:
1.第一个错误:

POST https://xxx.xx.xx.xx:6443/api/v1/namespaces?fieldManager=kubectl-create
Response Status: 409 Conflict
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "namespaces \"hotel123\" already exists",
  "reason": "AlreadyExists",
  "details": {
    "name": "hotel123",
    "kind": "namespaces"
  },
  "code": 409
}

1.第二个错误:

POST https://xxx.xx.xx.xx:6443/api/v1/namespaces?fieldManager=kubectl-create
Response Status: 422 Unprocessable Entity
{
    "kind": "Status",
    "apiVersion": "v1",
    "metadata": {},
    "status": "Failure",
    "message": "Namespace \"hotel_123\" is invalid: metadata.name: Invalid value: \"hotel_123\": a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name',  or '123-abc', regex used for validation is '[a-z0-9]\r\n([-a-z0-9]*[a-z0-9])?')",
    "reason": "Invalid",
    "details": {
        "name": "hotel_123",
        "kind": "Namespace",
        "causes": [
            {
                "reason": "FieldValueInvalid",
                "message": "Invalid value: \"hotel_123\": a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name',  or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')",
                "field": "metadata.name"
            }
        ]
    },
    "code": 422
}

1.正常返回:

POST https://xxx.xx.xx.xx:6443/api/v1/namespaces?fieldManager=kubectl-create
Response Status: 201 Created
{
    "kind": "Namespace",
    "apiVersion": "v1",
    "metadata": {
        "name": "hotel12345",
        "uid": "7a301d8b-37cd-45a5-8345-82wsufy88223456",
        "resourceVersion": "12233445566",
        "creationTimestamp": "2023-04-03T15:35:59Z",
        "managedFields": [
            {
                "manager": "kubectl-create",
                "operation": "Update",
                "apiVersion": "v1",
                "time": "2023-04-03T15:35:59Z",
                "fieldsType": "FieldsV1",
                "fieldsV1": {
                    "f:status": {
                        "f:phase": {}
                    }
                }
            }
        ]
    },
    "spec": {
        "finalizers": [
            "kubernetes"
        ]
    },
    "status": {
        "phase": "Active"
    }
}

总之,如果HTTP Status不是2xx,则返回的对象是Status类型,并且具有.Status!= StatusSuccess,Status中的附加信息(本例中为message)将用于丰富错误,如以下代码片段所示:

createdNamespace, err := clientset.CoreV1().Namespaces().Create(context.TODO(), namespace, metav1.CreateOptions{})
if err != nil {
    // print "namespaces \"hotel123\" already exists" or so
    fmt.Println(err.Error())
    return err.Error()
}
fmt.Printf("Created Namespace %+v in the cluster\n", createdNamespace)
return ""

相关问题