Code Monkey home page Code Monkey logo

Comments (5)

iximiuz avatar iximiuz commented on June 27, 2024

There might be an alternative solution - check out the "Read Kubernetes Manifests Into Go Structs" section of this article of mine. And here is the corresponding mini-program from this repo. Hope this helps!

from client-go-examples.

yuzhichang avatar yuzhichang commented on June 27, 2024

@iximiuz info.Object has different type with unstructured.Unstructured.Object. I cannot figure out how to use info.Object with dynamic API. Could you provide a complete example?

from client-go-examples.

iximiuz avatar iximiuz commented on June 27, 2024

@yuzhichang info.Object is of the type runtime.Object. It's a special interface type that can front many concrete API types, in particular unstructured.Unstructured.

Here is an example of how to type-cast a runtime.Object into a concrete unstructured object. And you can read more about the Kubernetes API types in another my article.

from client-go-examples.

yuzhichang avatar yuzhichang commented on June 27, 2024

I've figured out the reason of error client.Resource.Namespace.Create failed: the server could not find the requested resource.
Cluster-wide resources' namespace shall not be specified at creation.

The complete code is the following:

package main

import (
	"context"
	"flag"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/thanos-io/thanos/pkg/errors"
	apiv1 "k8s.io/api/core/v1"
	apiErrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/yaml"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
)

func kind2Resource(group, version, kind string) (gvr schema.GroupVersionResource, err error) {
	// https://www.cnblogs.com/zhangmingcheng/p/16128224.html
	// https://iximiuz.com/en/posts/kubernetes-api-structure-and-terminology/
	// https://github.com/kubernetes/kubernetes/issues/18622
	// kubectl api-resources
	gvk := schema.GroupVersionKind{Group: group, Version: version, Kind: kind}
	gvr, _ = meta.UnsafeGuessKindToResource(gvk)
	//fmt.Printf("%s %s %s -> %+v\n", group, version, kind, gvr)
	return
}

func isClusterWideResource(resource string) bool {
	if strings.HasPrefix(resource, "cluster") || resource == "customresourcedefinitions" || resource == "namespaces" {
		return true
	}
	return false
}

func createObject(client dynamic.Interface, fp string) (err error) {
	fmt.Printf("Reading %s\n", fp)
	// YAML -> Unstructured (through JSON)
	var yConfigMap, jConfigMap []byte
	if yConfigMap, err = os.ReadFile(fp); err != nil {
		err = errors.Wrapf(err, "os.ReadFile failed")
		return
	}
	jConfigMap, err = yaml.ToJSON(yConfigMap)
	if err != nil {
		err = errors.Wrapf(err, "yaml.ToJSON failed")
		return
	}

	var object runtime.Object
	object, err = runtime.Decode(unstructured.UnstructuredJSONScheme, jConfigMap)
	if err != nil {
		err = errors.Wrapf(err, "runtime.Decode failed")
		return
	}

	var uConfigMaps []unstructured.Unstructured
	switch t := object.(type) {
	case *unstructured.Unstructured:
		uConfigMaps = append(uConfigMaps, *t)
	case *unstructured.UnstructuredList:
		uConfigMaps = t.Items
	default:
		err = errors.Wrapf(err, "unstructured.Unstructured or unstructured.UnstructuredList expected")
		return
	}
	for _, uConfigMap := range uConfigMaps {
		var apiVersion, kind, namespace string
		apiVersion = uConfigMap.GetAPIVersion()
		kind = uConfigMap.GetKind()
		gv, _ := schema.ParseGroupVersion(apiVersion)
		var gvr schema.GroupVersionResource
		if gvr, err = kind2Resource(gv.Group, gv.Version, kind); err != nil {
			return
		}
		if isClusterWideResource(gvr.Resource) {
			_, err = client.Resource(gvr).Create(context.TODO(), &uConfigMap, metav1.CreateOptions{})
		} else {
			namespace = uConfigMap.GetNamespace()
			if namespace == "" {
				namespace = apiv1.NamespaceDefault
			}
			_, err = client.Resource(gvr).Namespace(namespace).Create(context.TODO(), &uConfigMap, metav1.CreateOptions{})
		}
		if err != nil {
			if apiErrors.IsAlreadyExists(err) {
				err = nil
				fmt.Printf("Object already exist: namespace %s, name %s, resource %s\n", namespace, uConfigMap.GetName(), gvr.Resource)
				return
			} else {
				err = errors.Wrapf(err, "client.Resource.Namespace.Create %+v failed", gvr)
				return
			}
		}
		fmt.Printf("Created object: namespace %s, name %s, resource %s\n", namespace, uConfigMap.GetName(), gvr.Resource)
	}
	return
}

func main() {
	// Initialize client
	var kubeconfig *string
	if home := homedir.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")
	}
	pth := flag.String("f", ".", "path of manifest file or directory")
	flag.Parse()

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

	// Get manifest list
	var fi fs.FileInfo
	if fi, err = os.Stat(*pth); err != nil {
		panic(err)
	}
	var fps []string
	if fi.IsDir() {
		var des []fs.DirEntry
		if des, err = os.ReadDir(*pth); err != nil {
			panic(err)
		}
		for _, de := range des {
			if de.IsDir() {
				continue
			}
			fn := de.Name()
			ext := filepath.Ext(fn)
			if ext != ".yaml" && ext != "yml" {
				continue
			}
			fps = append(fps, filepath.Join(*pth, fn))
		}
		sort.Strings(fps)
	} else {
		fps = append(fps, *pth)
	}

	// Create resource for each manifest
	for _, fp := range fps {
		if err = createObject(client, fp); err != nil {
			fmt.Println(err)
		}
	}
}

from client-go-examples.

iximiuz avatar iximiuz commented on June 27, 2024

It's good that you figured it out. At the same time, it seems like you're reinventing the wheel. The module k8s.io/cli-runtime does pretty much the same for you out of the box.

Also, beware that isClusterWideResource() is not the most reliable way of telling if a resource is namespaced or cluster-wide. Every APIResource object has a dedicated Namespaced bool flag for that.

from client-go-examples.

Related Issues (7)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.