如何在Kubernetes中提交Velero自定义Restore对象到API服务器?
错误根因
- 你使用的
apiClient.CoreV1()是Kubernetes内置核心API组的专用客户端,无法操作属于velero.io/v1自定义API组的Restore资源 types.ApplyPatchType对应的服务端应用(SSA)模式要求请求体必须是序列化后的JSON/YAML字节流,你直接传入Go结构体对象导致客户端无法识别类型,抛出未知类型错误- 你的Restore对象缺失必填的
Name、Namespace元数据字段,Kubernetes API无法识别资源标识 - 如果你是首次创建Restore资源,优先使用
Create方法而非Patch方法,后者默认用于更新已存在的资源
正确实现步骤
前置准备:注册Velero API类型到Scheme
首先需要将Velero的自定义类型注册到客户端的Scheme中,确保序列化/反序列化正常:
import ( veleroApi "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "k8s.io/client-go/kubernetes/scheme" ) func init() { // 将Velero v1 API注册到全局Scheme _ = veleroApi.AddToScheme(scheme.Scheme) }
方式1:使用动态客户端创建Restore(推荐)
动态客户端是操作Kubernetes自定义资源的通用方案,无需生成专用客户端代码:
import ( "context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" ) // 定义Restore资源的GVR(GroupVersionResource)标识 var RestoreGVR = schema.GroupVersionResource{ Group: "velero.io", Version: "v1", Resource: "restores", } func CreateRestore(config *rest.Config, gracePeriodSeconds int64, restorePV bool) error { // 初始化动态客户端 dynamicClient, err := dynamic.NewForConfig(config) if err != nil { return err } // 构造完整的Restore对象,补全必填字段 restoreObj := veleroApi.Restore{ TypeMeta: metav1.TypeMeta{ Kind: "Restore", APIVersion: "velero.io/v1", }, ObjectMeta: metav1.ObjectMeta{ Name: "test-restore-20211101", // 自定义资源名称,必填 Namespace: "velero", // Velero资源默认部署在velero命名空间,必填 DeletionGracePeriodSeconds: &gracePeriodSeconds, }, Spec: veleroApi.RestoreSpec{ BackupName: "backup-name-20211101", RestorePVs: &restorePV, }, } // 将结构体转换为Unstructured对象 unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&restoreObj) if err != nil { return err } // 提交创建请求到Kube APIServer _, err = dynamicClient.Resource(RestoreGVR).Namespace(restoreObj.Namespace).Create( context.TODO(), &unstructured.Unstructured{Object: unstructuredObj}, metav1.CreateOptions{}, ) return err }
方式2:如果确实需要使用Patch模式(服务端应用创建)
如果你的场景要求使用SSA(Server-Side Apply)模式提交资源,需要先序列化结构体,并且指定FieldManager参数:
import ( "context" "encoding/json" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" ) var RestoreGVR = schema.GroupVersionResource{ Group: "velero.io", Version: "v1", Resource: "restores", } func ApplyRestore(config *rest.Config, gracePeriodSeconds int64, restorePV bool) error { dynamicClient, err := dynamic.NewForConfig(config) if err != nil { return err } restoreObj := veleroApi.Restore{ TypeMeta: metav1.TypeMeta{ Kind: "Restore", APIVersion: "velero.io/v1", }, ObjectMeta: metav1.ObjectMeta{ Name: "test-restore-20211101", Namespace: "velero", DeletionGracePeriodSeconds: &gracePeriodSeconds, }, Spec: veleroApi.RestoreSpec{ BackupName: "backup-name-20211101", RestorePVs: &restorePV, }, } // 序列化结构体为JSON字节流 patchData, err := json.Marshal(restoreObj) if err != nil { return err } // 提交Patch请求,指定FieldManager为你的控制器名称 _, err = dynamicClient.Resource(RestoreGVR).Namespace(restoreObj.Namespace).Patch( context.TODO(), restoreObj.Name, types.ApplyPatchType, patchData, metav1.PatchOptions{ FieldManager: "your-custom-controller", // SSA模式必填,用于标识资源字段的管理者 }, ) return err }
内容的提问来源于stack exchange,提问作者Vishrant
相关产品推荐
相关产品推荐

