kubernetes动态客户端和服务端apply字段冲突

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

如何正确使用 kubernetes

client-go
库与动态客户端和服务器端应用?

我正在创建并尝试使用动态客户端更新 Kubernetes 资源,并设置

FieldManager
(以启用服务器端应用)。尽管为两个操作设置了相同的
FieldManager
值,但更新失败并出现以下错误:

panic: Apply failed with 1 conflict: conflict with "test" using cert-manager.io/v1: .spec.secretName

我的代码如下,您可以看到它对这两个操作使用相同的

FieldManager
,所以我不明白为什么会发生冲突:

package main

import (
    "context"
    "flag"

    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/client-go/dynamic"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    kubeconfig := flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
    flag.Parse()

    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
    if err != nil {
        panic(err)
    }
    client, err := dynamic.NewForConfig(config)
    if err != nil {
        panic(err)
    }

    fieldManager := "test"
    name := "foo"
    namespace := "default"
    certificate := &unstructured.Unstructured{
        Object: map[string]interface{}{
            "apiVersion": "cert-manager.io/v1",
            "kind":       "Certificate",
            "metadata":   map[string]interface{}{"name": name, "namespace": namespace},
            "spec": map[string]interface{}{
                "dnsNames": []string{"example.com"},
                "issuerRef": map[string]interface{}{
                    "group": "cert-manager.io",
                    "kind":  "ClusterIssuer",
                    "name":  "foo",
                },
                "secretName": "foo",
            },
        },
    }

    certificateGvr := schema.GroupVersionResource{Group: "cert-manager.io", Version: "v1", Resource: "certificates"}

    _, err = client.Resource(certificateGvr).
        Namespace(namespace).
        Create(context.TODO(), certificate, metav1.CreateOptions{FieldManager: fieldManager})
    if err != nil {
        panic(err)
    }

    unstructured.SetNestedField(certificate.UnstructuredContent(), "bar", "spec", "secretName")
    _, err = client.Resource(certificateGvr).
        Namespace(namespace).
        Apply(context.TODO(), name, certificate, metav1.ApplyOptions{FieldManager: fieldManager, Force: true})
    if err != nil {
        panic(err)
    }
}

我注意到,如果我强制更新并查看托管字段,则会出现两组托管字段(每个操作一组)。但我更愿意避免强制,因为它可能会导致意外覆盖字段。

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  creationTimestamp: "2024-05-24T10:01:09Z"
  generation: 2
  managedFields:
  - apiVersion: cert-manager.io/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:spec:
        f:dnsNames: {}
        f:issuerRef:
          f:group: {}
          f:kind: {}
          f:name: {}
        f:secretName: {}
    manager: test
    operation: Apply
    time: "2024-05-24T10:01:09Z"
  - apiVersion: cert-manager.io/v1
    fieldsType: FieldsV1
    fieldsV1:
      f:spec:
        .: {}
        f:dnsNames: {}
        f:issuerRef:
          .: {}
          f:group: {}
          f:kind: {}
          f:name: {}
    manager: test
    operation: Update
    time: "2024-05-24T10:01:09Z"
  name: foo
  namespace: default
  resourceVersion: "56441"
  uid: d4b97ef5-3314-498b-bb13-786967f1fcf8
spec:
  dnsNames:
  - example.com
  issuerRef:
    group: cert-manager.io
    kind: ClusterIssuer
    name: foo
  secretName: bar
go kubernetes client-go
1个回答
0
投票

到目前为止,我发现的最佳解决方案是使用

Apply
来创建和更新对象,如下所示:

    // Create the object
    _, err = client.Resource(certificateGvr).
        Namespace(namespace).
        Apply(context.TODO(), name, certificate, metav1.ApplyOptions{FieldManager: fieldManager})
    if err != nil {
        panic(err)
    }

    // Modify and update it
    unstructured.SetNestedField(certificate.UnstructuredContent(), "bar", "spec", "secretName")
    _, err = client.Resource(certificateGvr).
        Namespace(namespace).
        Apply(context.TODO(), name, certificate, metav1.ApplyOptions{FieldManager: fieldManager})
    if err != nil {
        panic(err)
    }

缺点是,与

Create
不同,如果对象已经存在(只要它由同一个
fieldManager
管理),则第一次操作不会出错。因此,如果您想确保使用此方法创建一个对象,您需要为该对象生成并跟踪唯一的
fieldManager

可能有更好的方法,所以我暂时保留这个问题。

© www.soinside.com 2019 - 2024. All rights reserved.