Skip to content

Instantly share code, notes, and snippets.

@pytimer
Created August 19, 2020 08:34
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save pytimer/0ad436972a073bb37b8b6b8b474520fc to your computer and use it in GitHub Desktop.
Save pytimer/0ad436972a073bb37b8b6b8b474520fc to your computer and use it in GitHub Desktop.

Parse the kubernetes manifest in yaml or json, don't care a manifest type.

Examples:

package main

import (
	"bytes"
	"context"
	"flag"
	"io"
	"io/ioutil"
	"log"

	"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/serializer/yaml"
	yamlutil "k8s.io/apimachinery/pkg/util/yaml"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/restmapper"
	"k8s.io/client-go/tools/clientcmd"
)

var (
	kubeconfig string
	filename   string
)

func main() {
	flag.StringVar(&kubeconfig, "kubeconfig", "", "")
	flag.StringVar(&filename, "f", "", "")
	flag.Parse()

	b, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("%q \n", string(b))

	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		log.Fatal(err)
	}

	c, err := kubernetes.NewForConfig(config)
	if err != nil {
		log.Fatal(err)
	}

	dd, err := dynamic.NewForConfig(config)
	if err != nil {
		log.Fatal(err)
	}

	decoder := yamlutil.NewYAMLOrJSONDecoder(bytes.NewReader(b), 100)
	for {
		var rawObj runtime.RawExtension
		if err = decoder.Decode(&rawObj); err != nil {
			break
		}

		obj, gvk, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil)
		unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
		if err != nil {
			log.Fatal(err)
		}

		unstructuredObj := &unstructured.Unstructured{Object: unstructuredMap}

		gr, err := restmapper.GetAPIGroupResources(c.Discovery())
		if err != nil {
			log.Fatal(err)
		}

		mapper := restmapper.NewDiscoveryRESTMapper(gr)
		mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
		if err != nil {
			log.Fatal(err)
		}

		var dri dynamic.ResourceInterface
		if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
			if unstructuredObj.GetNamespace() == "" {
				unstructuredObj.SetNamespace("default")
			}
			dri = dd.Resource(mapping.Resource).Namespace(unstructuredObj.GetNamespace())
		} else {
			dri = dd.Resource(mapping.Resource)
		}

		if _, err := dri.Create(context.Background(), unstructuredObj, metav1.CreateOptions{}); err != nil {
			log.Fatal(err)
		}
	}
	if err != io.EOF {
		log.Fatal("eof ", err)
	}
}

Usage:

app.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: demo
  name: demo
  namespace: default
spec:
  ports:
  - name: web
    port: 80
  selector:
    app: demo
  type: ClusterIP

go run demo.go -kubeconfig ~/.kube/config -f app.yaml

@pytimer
Copy link
Author

pytimer commented Mar 18, 2021

@pytimer
Copy link
Author

pytimer commented Mar 18, 2021

This code only work when the resource can not exist in k8s. So add https://github.com/pytimer/k8sutil/tree/main/examples/apply example, it like kubectl apply, support server-side and non server-side.

@mike-code
Copy link

Awesome mate! Thanks for sharing

@quentinalbertone
Copy link

quentinalbertone commented Apr 5, 2022

Thanks for your gist @pytimer it helps me A LOT, I know that you have a go-package that people can use (I'm not using it but it could help someone else so I post here).
I add the following line to this code :

var rawObj runtime.RawExtension
if err := decoder.Decode(&rawObj); err != nil {
     break
}
if len(rawObj.Raw) == 0 {
     // if the yaml object is empty just continue to the next one
     continue
}
obj, gvk, err := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme).Decode(rawObj.Raw, nil, nil)

Maybe it is not a common use case but in case your file looks like that

// A COMMENT HERE
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
[...]

it is useful

@kitarp29
Copy link

Thank you for this Code @pytimer

@doing-cr7
Copy link

thank you very much

@ankitkapoord76
Copy link

ankitkapoord76 commented Apr 6, 2023

This code only work when the resource can not exist in k8s. So add https://github.com/pytimer/k8sutil/tree/main/examples/apply example, it like kubectl apply, support server-side and non server-side.

Thanks @pytimer for sharing the code.
I was able to apply YAML using this, but when I re-tried again, it reported error that the resource already exists, even though I deleted it manually. Does it cache somewhere? I thought it would update it even if it is there. Could you please share the complete code as well incorporating the update part? or, let me know how to do it. That’s why it ‘d be great if you could also elaborate this comment. Thanks in advance!

@ofey404
Copy link

ofey404 commented Jul 5, 2023

Thank you!

@piyushdatazip
Copy link

Thanks @pytimer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment