client-go: анализ файлов json kubernetes для структур k8s

Я хотел бы проанализировать файл манифеста kubernetes (json/yaml) и иметь возможность конвертировать их в структуры k8s (чтобы позже манипулировать ими)

Я знаю, что есть функция NewYAMLOrJSONDecoder().Decode() ( https://github.com/kubernetes/apimachinery/blob/master/pkg/util/yaml/decoder.go) для чтения файла json / yaml, но Следующий шаг: как преобразовать их в структуру / тип k8s?

т.е. если я читаю файл yaml с объектом Namespace, как, например, преобразовать его в интерфейс core/v1/namespace

С Уважением,

2 ответа

   import (
        v1 "k8s.io/api/core/v1"
        "k8s.io/apimachinery/pkg/runtime"
        "k8s.io/apimachinery/pkg/runtime/serializer"
        "k8s.io/client-go/kubernetes/scheme"
    )
    
    func decode(data []byte) (*v1.Namespace, error) {
        decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDecoder()
        object := &v1.Namespace{}
        err := runtime.DecodeInto(decoder, data, object)
        if err != nil {
            return nil, err
        }
        return object, nil
    }

Проходить SchemeGroupVersion сгенерированы для ваших CRD на UniversalDecoder звоните, если нужно.

Использовать runtime.Decode вместо того runtime.DecodeInto для декодирования неопределенного типа объекта.

Спасибо svenwltr, я не знал, что мы можем сделать это.

В то же время мне удается найти не лучший подход, а другой:

package main

import (
    "flag"
    "fmt"
    "os"
    "io"
    "path/filepath"
    "log"
    "encoding/json"
    //"time"

    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/discovery"
    "k8s.io/client-go/dynamic"
    "k8s.io/apimachinery/pkg/util/yaml"
    "k8s.io/apimachinery/pkg/runtime"
    "k8s.io/apimachinery/pkg/runtime/schema"
    "k8s.io/apimachinery/pkg/api/meta"
    "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

)

func main() {
    var kubeconfig *string
    if home := homeDir(); home != "" {
        kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
    } else {
        kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
    }
    flag.Parse()

    // use the current context in kubeconfig
    config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
    if err != nil {
        panic(err.Error())
    }

    // create the clientset
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        panic(err.Error())
    }

    f,err := os.Open("namespace.yaml")
    if err!=nil {
        log.Fatal(err)
    }
    d := yaml.NewYAMLOrJSONDecoder(f,4096)
    dd := clientset.Discovery()
    apigroups,err := discovery.GetAPIGroupResources(dd)
    if err != nil {
        log.Fatal(err)
    }

    restmapper := discovery.NewRESTMapper(apigroups,meta.InterfacesForUnstructured)


    for {
        // https://github.com/kubernetes/apimachinery/blob/master/pkg/runtime/types.go
        ext := runtime.RawExtension{}
        if err := d.Decode(&ext); err!=nil {
            if err == io.EOF {
                break
            }
            log.Fatal(err)
        }
        fmt.Println("raw: ",string(ext.Raw))
        versions := &runtime.VersionedObjects{}
        //_, gvk, err := objectdecoder.Decode(ext.Raw,nil,versions)
        obj, gvk, err := unstructured.UnstructuredJSONScheme.Decode(ext.Raw,nil,versions)
        fmt.Println("obj: ",obj)

        // https://github.com/kubernetes/apimachinery/blob/master/pkg/api/meta/interfaces.go
        mapping, err := restmapper.RESTMapping(gvk.GroupKind(), gvk.Version)
        if err != nil {
            log.Fatal(err)
        }

        restconfig := config
        restconfig.GroupVersion = &schema.GroupVersion {
            Group: mapping.GroupVersionKind.Group,
            Version: mapping.GroupVersionKind.Version,
        } 
        dclient,err := dynamic.NewClient(restconfig)
        if err != nil {
            log.Fatal(err)
        }

        // https://github.com/kubernetes/client-go/blob/master/discovery/discovery_client.go
        apiresourcelist, err := dd.ServerResources()
        if err != nil {
            log.Fatal(err)
        }
        var myapiresource metav1.APIResource
        for _,apiresourcegroup := range(apiresourcelist) {
            if apiresourcegroup.GroupVersion == mapping.GroupVersionKind.Version {
                for _,apiresource := range(apiresourcegroup.APIResources) {
                    //fmt.Println(apiresource)

                    if apiresource.Name == mapping.Resource && apiresource.Kind == mapping.GroupVersionKind.Kind {
                        myapiresource = apiresource
                    }
                }
            }
        }
        fmt.Println(myapiresource)
        // https://github.com/kubernetes/client-go/blob/master/dynamic/client.go

        var unstruct unstructured.Unstructured
        unstruct.Object = make(map[string]interface{})
        var blob interface{}
        if err := json.Unmarshal(ext.Raw,&blob); err != nil {
            log.Fatal(err)
        }
        unstruct.Object = blob.(map[string]interface{})
        fmt.Println("unstruct:",unstruct)
        ns := "default"
        if md,ok := unstruct.Object["metadata"]; ok {
            metadata := md.(map[string]interface{})
            if internalns,ok := metadata["namespace"]; ok {
                ns = internalns.(string)
            }
        }
        res := dclient.Resource(&myapiresource,ns)
        fmt.Println(res)
        us,err := res.Create(&unstruct)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println("unstruct response:",us)


    }
}

func homeDir() string {
    if h := os.Getenv("HOME"); h != "" {
        return h
    }
    return os.Getenv("USERPROFILE") // windows
}

Этот вопрос очень похож на Как десериализовать файл YAML Kubernetes, но этот вопрос немного устарел, так как имена пакетов изменились.

Кроме того, он напрямую не использует клиент go, что означает, что для этого может быть другое решение.

Вот пример:

package main

import (
    "fmt"

    "k8s.io/kubernetes/pkg/api"
    _ "k8s.io/kubernetes/pkg/api/install"
    _ "k8s.io/kubernetes/pkg/apis/extensions/install"
    "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)

var json = `
{
  "apiVersion": "extensions/v1beta1",
  "kind": "Deployment",
  "metadata": null,
  "name": "my-nginx",
  "replicas": 2,
  "spec": null,
  "template": {
    "metadata": {
      "labels": {
        "run": "my-nginx"
      }
    },
    "spec": {
      "containers": [
        {
          "image": "nginx",
          "name": "my-nginx",
          "ports": [
            {
              "containerPort": 80
            }
          ]
        }
      ]
    }
  }
}
`

func main() {
    // decode := api.Codecs.UniversalDecoder().Decode
    decode := api.Codecs.UniversalDeserializer().Decode

    obj, _, err := decode([]byte(json), nil, nil)
    if err != nil {
        fmt.Printf("%#v", err)
    }

    deployment := obj.(*v1beta1.Deployment)

    fmt.Printf("%#v\n", deployment)
}

Заметки

  • .../install пакеты важны, так как они определяют, какие типы могут быть декодированы
  • он может декодировать JSON, YAML и, возможно, также все другие поддерживаемые типы файлов
  • не уверен, где разница между UniversalDecoder а также UniversalDeserializer является
Другие вопросы по тегам