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

问题描述 投票:0回答:2

我有一个与 k8s 对话的 golang 后端。我想重新制定从 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:我有多个函数,所以验证应该是通用的。

go kubernetes etcd client-go kubernetes-custom-resources
2个回答
1
投票

一般来说(虽然有变通方法),如果你想捕获错误以返回更有用的错误,你要确保满足以下条件:

  1. 您捕获的错误具有有意义的类型
  2. 您使用的是 go version >= 1.13,它附带了 有用的辅助函数

在下面的例子中,我试图读取一个不存在的配置文件。我的代码检查返回的错误是

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
格式化程序很有用

这里是 playground 上的另一个例子,使用更通用的案例,因为 playground 只允许你使用标准库。

https://go.dev/play/p/p1qMiZlnqPK


1
投票

String

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

说明:

你需要超越

kubernetes/client-go
客户端库的表面。

每个客户端通过 HTTP REST APIs 与 k8s 服务器对话,它在

json
中发送回响应。如果可能的话,它是
client-go
库解码响应主体并将结果存储到对象中。

至于你的情况,让我通过

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 ""
© www.soinside.com 2019 - 2024. All rights reserved.