【问题标题】:Resource not found error performing SSA create using dynamic client使用动态客户端执行 SSA 创建时找不到资源错误
【发布时间】:2021-04-07 12:46:59
【问题描述】:

我关注了@ymmt2005 优秀的dynamic client guide。一切都很好,直到我进行实际 PATCH 调用的最后一步,我得到一个the server could not find the requested resource 错误。几乎一切似乎都是正确的,除了我不确定 PathOptions 结构中的“FieldManager”字段。我不确定 “进行这些更改的参与者或实体” 指的是什么。这是否需要匹配我的代码或系统中的某些内容?还有其他想法吗?

package main

import (
...
)

const resourceYAML = `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mike-nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: 'nginx:latest'
          ports:
            - containerPort: 80
`

func main() {
    ctx := context.Background()

    // Create dynamic discovery client from local kubeconfig file
    kubePath := filepath.Join(homedir.HomeDir(), ".kube", "config")
    cfg, err := clientcmd.BuildConfigFromFlags("", kubePath)
    if err != nil {
        log.Fatalf("error building config, %v\n", err)
    }
    dynClient, err := dynamic.NewForConfig(cfg)
    if err != nil {
        log.Fatalf("error creating client, %v\n", err)
    }
    disClient, err := discovery.NewDiscoveryClientForConfig(cfg)
    if err != nil {
        log.Fatalf("error creating discovery client, %v\n", err)
    }

    // Decode YAML manifest & get GVK
    decodeUnstr := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
    obj := &unstructured.Unstructured{}
    _, gvk, err := decodeUnstr.Decode([]byte(resourceYAML), nil, obj)
    if err != nil {
        log.Fatalf("error decoding manifest, %v\n", err)
    }
    jsonObj, err := json.Marshal(obj)
    if err != nil {
        log.Fatalf("error marshaling object, %v\n", err)
    }

    // Find GVR using GVK
    mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(disClient))
    mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
    if err != nil {
        log.Fatalf("error finding GVR, %v\n", err)
    }

    // Get REST interface for the GVR, checking for namespace or cluster-wide
    var dr dynamic.ResourceInterface
    if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
        // Namespaced resource
        dr = dynClient.Resource(mapping.Resource).Namespace(obj.GetNamespace())
    } else {
        // Cluster-wide resource
        dr = dynClient.Resource(mapping.Resource)
    }

    // Create or Update the object with SSA
    options := metav1.PatchOptions{FieldManager: "sample-controller"}
    _, err = dr.Patch(ctx, obj.GetName(), types.ApplyPatchType, jsonObj, options)
    if err != nil {
        log.Fatalf("error patching, %v\n", err)
    }
}

[edit] 我确认我只能在已经存在的资源上使用“补丁”。我调整了代码以使用“创建”来创建资源,然后我能够成功地对其进行“补丁”以进行更改。为了克服 FieldManager 的不一致,我在文档中推荐的 PatchOptions 添加了 Force=true 。我仍然想知道如果资源不存在我如何创建,如果存在则更新——也许只是测试存在?

【问题讨论】:

  • 再仔细看一下 k8s REST api,似乎 PATCH 不创建资源,如果它不存在——这与 HTTP 动词 PATCH 一致。像“kubectl apply -f”一样,如果资源不存在,我想要一个创建资源的命令。这可能吗?
  • ...或者我需要测试是否存在然后决定 POST 还是 PATCH?我想在 Git 中为集群保留所需的状态(又名资源清单),然后让代理更新任何新提交的资源(也就是对资源清单的更改)。

标签: kubernetes kubernetes-go-client


【解决方案1】:

答案真的很琐碎。原始代码假定清单中提供了namespace。如果提供的命名空间是“”,部署端点不会自动将命名空间设置为default,并且由于“”不是有效的命名空间而出错。因此,我添加了将命名空间设置为default(如果未提供)的逻辑,并且如果资源不存在,服务器端应用将创建资源,如果存在则更新。再次感谢@ymmt2005。

package main

import (
...
)

const resourceYAML = `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mike-nginx
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: 'nginx:latest'
          ports:
            - containerPort: 80
`

func main() {
    ctx := context.Background()

    // Create dynamic discovery client from local kubeconfig file
    kubePath := filepath.Join(homedir.HomeDir(), ".kube", "config")
    cfg, err := clientcmd.BuildConfigFromFlags("", kubePath)
    if err != nil {
        log.Fatalf("error building config, %v\n", err)
    }
    dynClient, err := dynamic.NewForConfig(cfg)
    if err != nil {
        log.Fatalf("error creating client, %v\n", err)
    }
    disClient, err := discovery.NewDiscoveryClientForConfig(cfg)
    if err != nil {
        log.Fatalf("error creating discovery client, %v\n", err)
    }

    // Decode YAML manifest & get GVK
    decodeUnstr := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
    obj := &unstructured.Unstructured{}
    _, gvk, err := decodeUnstr.Decode([]byte(resourceYAML), nil, obj)
    if err != nil {
        log.Fatalf("error decoding manifest, %v\n", err)
    }
    jsonObj, err := json.Marshal(obj)
    if err != nil {
        log.Fatalf("error marshaling object, %v\n", err)
    }

    // Find GVR using GVK
    mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(disClient))
    mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
    if err != nil {
        log.Fatalf("error finding GVR, %v\n", err)
    }

    // Set Namespace to default if not provided in manifest
    var ns string
    if ns = obj.GetNamespace(); ns == "" {
        ns = "default"
    }

    // Get REST interface for the GVR, checking for namespace or cluster-wide
    var dr dynamic.ResourceInterface
    if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
        // Namespaced resource
        dr = dynClient.Resource(mapping.Resource).Namespace(ns)
    } else {
        // Cluster-wide resource
        dr = dynClient.Resource(mapping.Resource)
    }

    // Create or Update the object with SSA
    options := metav1.PatchOptions{FieldManager: "sample-controller"}
    _, err = dr.Patch(ctx, obj.GetName(), types.ApplyPatchType, jsonObj, options)
    if err != nil {
        log.Fatalf("error patching, %v\n", err)
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-28
    • 2021-10-06
    相关资源
    最近更新 更多