Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for parsing K8s yaml spec into client-go data structures #193

Closed
newtonkishore opened this issue May 14, 2017 · 51 comments
Closed
Labels
lifecycle/rotten Denotes an issue or PR that has aged beyond stale and will be auto-closed.

Comments

@newtonkishore
Copy link

newtonkishore commented May 14, 2017

Hi,

I am trying to create K8s cluster with spec coming from yaml files. I had to write Yaml equivalent of the same datastructures to convert the spec from yaml to go data structures to start the cluster.

The Kubernetes/client-go data structures are tuned to Json Parsing, can we just add "yaml:"" " support to client-go datastructures?

Or is there a better way to do this?

Thanks.

@ahmetb
Copy link
Member

ahmetb commented May 15, 2017

@newtonkishore consider converting from YAML to JSON and then unmarshaling into structs as the json:"" field tags are there. The API works with JSON primarily and kubectl converts YAML to JSON before sending the request.

@ahmetb
Copy link
Member

ahmetb commented May 15, 2017

Or actually I'm not sure, I saw something in the issue description of #194 which looks like there's a universal decoder you can use.

@rmohr
Copy link
Contributor

rmohr commented May 16, 2017

@newtonkishore you can use yaml.NewYAMLOrJSONDecoder(reader, buffSize).Decode(k8sStruct) from k8s.io/client-go/pkg/util/yaml. You can pass in JSON or YAML, and it will use the json:"" annotations for both formats.

@kenontech
Copy link

Follow up to this question, what is the right decoder to use if the type of the k8sStruct is not known before decoding?

@christianhuening
Copy link

yaml.NewYAMLOrJSONDecoder() etc. has been moved to k8s.io/apimachinery/pkg/util/yaml

@lavalamp
Copy link
Member

lavalamp commented Nov 7, 2017 via email

@christianhuening
Copy link

christianhuening commented Nov 9, 2017

@kenontech @ahmetb @newtonkishore @rmohr Here's my solution (thanks to Stefan Schimanski for valuable hints!):

import (
	"k8s.io/client-go/pkg/api"
	_ "k8s.io/client-go/pkg/api/install"
	_ "k8s.io/client-go/pkg/apis/extensions/install"
	_ "k8s.io/client-go/pkg/apis/rbac/install"
	//...
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/rbac/v1beta1"
	// make sure to import all client-go/pkg/api(s) you need to cover with your code
)

func someFunc() {
	// yamlFiles is an []string
	for _, f := range yamlFiles {

		decode := api.Codecs.UniversalDeserializer().Decode
		obj, groupVersionKind, err := decode([]byte(f), nil, nil)

		if err != nil {
			log.Fatal(fmt.Sprintf("Error while decoding YAML object. Err was: %s", err))
		}

		// now use switch over the type of the object
		// and match each type-case
		switch o := obj.(type) {
		case *v1.Pod:
			// o is a pod
		case *v1beta1.Role:
			// o is the actual role Object with all fields etc
		case *v1beta1.RoleBinding:
		case *v1beta1.ClusterRole:
		case *v1beta1.ClusterRoleBinding:
		case *v1.ServiceAccount:
		default:
			//o is unknown for us
		}
	}
}

@mofelee
Copy link

mofelee commented Nov 9, 2017

@christianhuening

image

image

Why my client-go doesn't have api inside pkg folder?

I use go get k8s.io/client-go/... to get client-go

@lavalamp
Copy link
Member

lavalamp commented Nov 9, 2017 via email

@mofelee
Copy link

mofelee commented Nov 9, 2017

@lavalamp Thanks!

@iazel
Copy link

iazel commented Nov 19, 2017

For future reference, if you don't have have k8s.io/client-go/pkg/api, then:

import "k8s.io/client-go/kubernetes/scheme"
scheme.Codecs.UniversalDeserializer()

@mhindery
Copy link

mhindery commented Feb 5, 2018

I am trying a similar thing and am completely lost. What packages are moved to where? I've read the scheme.Codecs.* are not supposed to be used anymore, and I have no clue what to import.

My goal is to read a yaml file with a list of rolebindings into a client-go structure, and there seem to be no possibility to do so, and no documentation at all. I've asked on the Slack channel and nobody could help there. There are some SO questions, but none have an answer to how to work with multiple items in a list in a yaml file. e.g.:

https://stackoverflow.com/questions/44306554/how-to-deserialize-kubernetes-yaml-file.
https://stackoverflow.com/questions/47116811/client-go-parse-kubernetes-json-files-to-k8s-structures?noredirect=1&lq=1

My yaml file looks like this

apiVersion: v1
kind: List
items:
# --- # Full cluster admin
- kind: ClusterRoleBinding
  apiVersion: rbac.authorization.k8s.io/v1
  metadata:
    name: ***
    labels:
      source: ***
  roleRef:
    kind: ClusterRole
    name: cluster-admin
    apiGroup: rbac.authorization.k8s.io
  subjects:
  - kind: User
    name: ***
    apiGroup: rbac.authorization.k8s.io
...

This can be used perfectly with kubectl apply -f ... however it seems impossible to parse using go ...

@christianhuening
Copy link

@mhindery #193 (comment) doesn't work anymore?

@sttts
Copy link
Contributor

sttts commented Feb 5, 2018

The packages have changes. The codec to use is https://github.com/kubernetes/client-go/blob/master/kubernetes/scheme/register.go#L55. But otherwise, not much has changed.

@mhindery
Copy link

mhindery commented Feb 5, 2018

While I can read the file using the method above, I never get into the case in the switch with the correct type. Using this file

package main

import (
	"fmt"

	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/client-go/kubernetes/scheme"
)

var filecontent = `
apiVersion: v1
kind: List
items:
- kind: ClusterRoleBinding
  apiVersion: rbac.authorization.k8s.io/v1
  metadata:
    name: myName
    labels:
      source: myLabel
  roleRef:
    kind: ClusterRole
    name: cluster-admin
    apiGroup: rbac.authorization.k8s.io
  subjects:
  - kind: User
    name: my@user.com
    apiGroup: rbac.authorization.k8s.io
`

func main() {
	decode := scheme.Codecs.UniversalDeserializer().Decode
	obj, _, _ := decode([]byte(filecontent), nil, nil)

	fmt.Printf("%++v\n\n", obj.GetObjectKind())
	fmt.Printf("%++v\n\n", obj)

	cbl := obj.(*rbacv1.ClusterRoleBindingList) // This fails
	fmt.Printf("%++v\n", cbl)

	switch o := obj.(type) {
	case *rbacv1.ClusterRoleBindingList:
		fmt.Println("correct found") // Never happens
	default:
		fmt.Println("default case")
		_ = o
	}
}

I get this output:

&TypeMeta{Kind:List,APIVersion:v1,}

&List{ListMeta:k8s_io_apimachinery_pkg_apis_meta_v1.ListMeta{SelfLink:,ResourceVersion:,Continue:,},Items:[{[123 34 97 112 105 86 101 114 115 105 111 110 34 58 34 114 98 97 99 46 97 117 116 104 111 114 105 122 97 116 105 111 110 46 107 56 115 46 105 111 47 118 49 34 44 34 107 105 110 100 34 58 34 67 108 117 115 116 101 114 82 111 108 101 66 105 110 100 105 110 103 34 44 34 109 101 116 97 100 97 116 97 3458 123 34 108 97 98 101 108 115 34 58 123 34 115 111 117 114 99 101 34 58 34 109 121 76 97 98 101 108 34 125 44 34 110 97 109 101 34 58 34 109 121 78 97 109 101 34 125 44 34 114 111 108 101 82 101 102 3458 123 34 97 112 105 71 114 111 117 112 34 58 34 114 98 97 99 46 97 117 116 104 111 114 105 122 97 116 105 111 110 46 107 56 115 46 105 111 34 44 34 107 105 110 100 34 58 34 67 108 117 115 116 101 114 82111 108 101 34 44 34 110 97 109 101 34 58 34 99 108 117 115 116 101 114 45 97 100 109 105 110 34 125 44 34 115 117 98 106 101 99 116 115 34 58 91 123 34 97 112 105 71 114 111 117 112 34 58 34 114 98 97 99 46 97 117 116 104 111 114 105 122 97 116 105 111 110 46 107 56 115 46 105 111 34 44 34 107 105 110 100 34 58 34 85 115 101 114 34 44 34 110 97 109 101 34 58 34 109 121 64 117 115 101 114 46 99 111 109 34 125 93 125] <nil>}],}

panic: interface conversion: runtime.Object is *v1.List, not *v1.ClusterRoleBindingList

The object I end up with is of the type runtime.Object and I don't see anywhere how to convert this to my desired type (the ClusterRoleBindingList). From the printed output, I can see the object has the fields items / typemeta / listmeta somewhere in it, which the ClusterRoleBindingList also defines, so the data seems to be in there. It's 'simply' the conversion between runtime.Object and my type that I'm missing.

In #193 (comment), you seem to immediately end up with the right types in the switch-case, while I have a generic one. I've looked at the linked scheme.Scheme.Conver() function, but that hasn't worked for me. What am I missing?

@christianhuening
Copy link

christianhuening commented Feb 6, 2018

@mhindery As your error states your (...) runtime.Object is *v1.List, not *v1.ClusterRoleBindingList, so I guess you should first switch case for v1.List and then look into the items of that list.
I don't use that list type, but have a single file, where each entry is separated via ---.
Here's my parseK8sYaml(...):

func parseK8sYaml(fileR []byte) []runtime.Object {

	acceptedK8sTypes := regexp.MustCompile(`(Role|ClusterRole|RoleBinding|ClusterRoleBinding|ServiceAccount)`)
	fileAsString := string(fileR[:])
	sepYamlfiles := strings.Split(fileAsString, "---")
	retVal := make([]runtime.Object, 0, len(sepYamlfiles))
	for _, f := range sepYamlfiles {
		if f == "\n" || f == "" {
			// ignore empty cases
			continue
		}

		decode := scheme.Codecs.UniversalDeserializer().Decode
		obj, groupVersionKind, err := decode([]byte(f), nil, nil)

		if err != nil {
			log.Println(fmt.Sprintf("Error while decoding YAML object. Err was: %s", err))
			continue
		}

		if !acceptedK8sTypes.MatchString(groupVersionKind.Kind) {
			log.Printf("The custom-roles configMap contained K8s object types which are not supported! Skipping object with type: %s", groupVersionKind.Kind)
		} else {
			retVal = append(retVal, obj)
		}

	}
	return retVal
}

@huydinhle
Copy link

@christianhuening Thanks for your solution. I am having a hard time to convert the runtime.Object to let's say something like v1.ConfigMap. Any suggestion?

@harryleesan
Copy link

I have a similar issue to @huydinhle, however, I am trying to convert runtime.Object to a Deployment using the following:

package main

import (
  "fmt"

  "k8s.io/api/apps/v1beta1"
  "k8s.io/client-go/kubernetes/scheme"
)

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 := scheme.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)
}

I get this error:

panic: interface conversion: runtime.Object is *v1beta1.Deployment, not *v1beta1.Deployment

My glide.yaml:

package: main
import:
- package: k8s.io/client-go
  version: v6.0.0

The error does not make sense to me since it seems like the type is the same. The conversion from runtime.Object to resources is not intuitive to me. Was anyone able to solve this?

@harryleesan
Copy link

I have managed to solve it... the deployment resource is of apiVersion extensions/v1beta1 and I imported k8s.io/api/apps/v1beta1, once I changed it to k8s.io/api/extensions/v1beta1 everything worked!

Here is the working code:

package main

import (
  "fmt"

  "k8s.io/api/extensions/v1beta1"
  "k8s.io/client-go/kubernetes/scheme"
)

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 := scheme.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)
}

@trobert2
Copy link

why does scheme.Codecs.UniversalDeserializer().Decode while fetching a kubernetes object from file return the proper TypeMeta and when fetching the object from the cluster (a config map in this case) using

clientset, err := kubernetes.NewForConfig(config)
clientset.CoreV1().ConfigMaps(namespace).Get(name, metav1.GetOptions{})

the TypeMeta is empty.

  TypeMeta: {
-  Kind: "ConfigMap",
-  APIVersion: "v1",
+  Kind: "",
+  APIVersion: "",
  },

Tested on HEAD and version 6 of the client-go

If I add a print statement in the func (r *Request) Do() Result function in: k8s.io/client-go/rest/request.go here

like so: fmt.Printf("%++v\n\n", string(result.body[:len(result.body)]))

I can see the response from the server being:

{"kind":"ConfigMap","apiVersion":"v1","metadata":{"name":"test-map","namespace":"default","selfLink":"/api/v1/namespaces/default/configmaps/test-map","uid":"27ccefdc-463b-11e8-bd71-080027205854","resourceVersion":"1584679","creationTimestamp":"2018-04-22T14:40:51Z","annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"data\":{\"game.properties\":\"enemies=aliens\\nlives=3\\nenemies.cheat=true\\nenemies.cheat.level=noGoodRotten\\nsecret.code.passphrase=UUDDLRLRBABAS\\nsecret.code.allowed=true\\nsecret.code.lives=30\\n\",\"ui.properties\":\"color.good=purple\\ncolor.bad=yellow\\nallow.textmode=true\\nhow.nice.to.look=fairlyNice\\n\"},\"kind\":\"ConfigMap\",\"metadata\":{\"annotations\":{},\"name\":\"test-map\",\"namespace\":\"default\"}}\n"}},"data":{"game.properties":"enemies=aliens\nlives=3\nenemies.cheat=true\nenemies.cheat.level=noGoodRotten\nsecret.code.passphrase=UUDDLRLRBABAS\nsecret.code.allowed=true\nsecret.code.lives=30\n","ui.properties":"color.good=purple\ncolor.bad=yellow\nallow.textmode=true\nhow.nice.to.look=fairlyNice\n"}}

kind and apiVersion are there in the response. Is there something wrong with the Marshal/Unmarshal process? what's happening here?

@27149chen
Copy link
Member

Hi, I'm trying to covert a json to a crd struct in my unittest, using the decoder "scheme.Codecs.UniversalDeserializer().Decode". but got an error no kind "*" is registered for version "*"
Do I need to "register" it, and how?

@dionysius
Copy link

@27149chen

This happened to me with my own CRD's, you'll need to add the scheme like:

//import
    myscheme "github.com/user/project/go/client/clientset/versioned/scheme"

//use AddToScheme before using the Decoder
    myscheme.AddToScheme(scheme.Scheme)

@integrii
Copy link

integrii commented Jul 18, 2018

I used a nifty YAMLtoJSON package to get this working.

Check it out:

// yamlBytes contains a []byte of my yaml job spec
// convert the yaml to json
jsonBytes, err := yaml.YAMLToJSON(yamlBytes)
if err != nil {
	return v1.Job{}, err
}
// unmarshal the json into the kube struct
var job = v1.Job{}
err = json.Unmarshal(jsonBytes, &job)
if err != nil {
	return v1.Job{}, err
}
// job now contains the for the job that was in the YAML!

@fejta-bot
Copy link

Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale.
Stale issues rot after an additional 30d of inactivity and eventually close.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/lifecycle stale

@k8s-ci-robot k8s-ci-robot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Oct 16, 2018
@harsh-98
Copy link

harsh-98 commented May 28, 2020

It is really helpful for decoding CRDs or other resource from yaml.

@Abirdcfly
Copy link
Member

but If my yaml is kubeconfig.(ns:kube-public cm:cluster-info).I have to use ("k8s.io/client-go/tools/clientcmd")clientcmd.Load.

yaml.NewYAMLOrJSONDecoder() etc will get me a error

"error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go struct field Config.clusters of type map[string]*api.Cluster"

why?  😭

@pmgexpo17
Copy link

Here is some code I found useful to discover the right GroupVersionKind for confirming the source package and formatting my yaml config correctly
`
import (

"log"
"os"
"k8s.io/apimachinery/pkg/runtime"
v1apps "k8s.io/api/apps/v1"
v1core "k8s.io/api/core/v1")

logg := log.New(os.Stdout, "INFO: ", log.Lshortfile)
logg.SetOutput(os.Stdout)
scheme := runtime.NewScheme()
_ = v1apps.AddToScheme(scheme)
_ = v1core.AddToScheme(scheme)
logg.Printf("Printing All known types for schemes app/v1 and core/v1\n")
tmap := scheme.AllKnownTypes()
for key, value := range tmap {
	logg.Printf("GroupVersionKind : %v, Type : %v\n",key,value)
}

`

Output
INFO: example.go:24: GroupVersionKind : /v1, Kind=ConfigMap, Type : v1.ConfigMap
INFO: example.go:24: GroupVersionKind : apps/v1, Kind=Deployment, Type : v1.Deployment

In configMap.yaml the apiVersion must = v1
In deployment.yaml the apiVersion must = app/v1
Otherwise decoding will fail

`

decode := serializer.NewCodecFactory(scheme).UniversalDeserializer().Decode
stream, ferr := ioutils.ReadFile(filename)
if ferr != nil {
	log.Fatal(ferr)
}
obj, groupKindVersion, err := decode(stream, nil, nil)
switch groupKindVersion.Kind {
case "Deployment": ...

`

@thobianchi
Copy link

thobianchi commented Nov 28, 2021

if stream contains a custom resource this line:
obj, groupKindVersion, err := decode(stream, nil, nil)
will fail with error: no kind "X" is registered for version "G/V" in scheme "pkg/runtime/scheme.go:100"
using scheme.Codecs.UniversalDecoder().Decode.

Is possible to create a decoder for every resource known to an actual k8s Cluster with a discovery client?.
methods like k8s.io/client-go/discovery.GroupVersionResources() seems oriented in the right direction but I can't find the way to go.

@dionysius
Copy link

@thobianchi

I think you're missing to load the scheme for your CRD. Something like:

scheme := runtime.NewScheme()
_ = v1apps.AddToScheme(scheme)

where v1apps should be the package of your CRD

@thobianchi
Copy link

I am decoding some yaml that can be everything, like kubectl does lauching apply.

@dionysius
Copy link

dionysius commented Nov 29, 2021

I think kubectl apply does no magic, basically just sending the json. And the API server knows everything possible since you had to explicitly register the CRDs.

The universal decoder actually creates the fitting typed object, using reflect, and returns it as Object. So you have to add the scheme for the decoder to know it.

A gereric way might be using Unstructured, but this might be done in a different way.

mfojtik pushed a commit to mfojtik/client-go that referenced this issue Apr 11, 2022
Bug 1986003: bump(k8s.io/*): v0.22.1
@TheApeMachine
Copy link

TheApeMachine commented May 1, 2022

I guess I kept it simple...

import (
    "os"
    "k8s.io/kubectl/pkg/cmd"
)

func deploy() {
    os.Args = []string{
        "kubectl",
        "apply",
        "-f",
        "./cmd/manifests/",
    }

    cmd.NewDefaultKubectlCommand().Execute()
}

It is essentially the same way I integrated kind (kubernetes in docker), containerd, and istio into my project. I went down that path once, using gopkg.in/yaml.v2 which can unmarshal yaml onto a struct, which I did not write manually, but pulled from their respective packages. Still needed a lot of code, and was generally a very frustrating experience. It's probably much better to just use the dynamic example in the client-go repo, but I wasn't sure whether or not that was only for deployments, or all other kinds too.

@lavalamp
Copy link
Member

lavalamp commented May 2, 2022

  1. Make sure you use the k8s library for yaml parsing: https://pkg.go.dev/sigs.k8s.io/yaml
  2. Consider using the SSA client instead of shelling out to kubectl apply: https://kubernetes.io/blog/2021/08/06/server-side-apply-ga/#server-side-apply-support-in-client-go

@TheApeMachine
Copy link

Yeah, this looks cool, thanks for that :)

@Arsen-Uulu
Copy link

Does anyone know how to decode a series of documents from a single YAML file?

I have namespace then stateful set with --- delimter

@astingengo
Copy link

astingengo commented Nov 30, 2022

@DiveInto
I'm ussing this

	sch := runtime.NewScheme()
	_ = clientgoscheme.AddToScheme(sch)
	_ = apiextv1beta1.AddToScheme(sch)

	decode := serializer.NewCodecFactory(sch).UniversalDeserializer().Decode

	obj, _, err := decode(yamlFile, nil, nil)
	if err != nil {
		fmt.Println(err)
	}

but still I'm seeing this error

no kind "CustomResourceDefinition" is registered for version "apiextensions.k8s.io/v1" in scheme "pkg/runtime/scheme.go:100"

Am I doing something wrong?

Founded ...
I'm using v1 and not v1beta1 so I need to

apiv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
...
_ = apiv1.AddToScheme(sch)

@astingengo
Copy link

@Arsen-Uulu
This is what I did

utilyaml "k8s.io/apimachinery/pkg/util/yaml"


yamlFile, err := ioutil.ReadFile("multi.yaml")
if err != nil {
    log.Printf("yamlFile.Get err   #%v ", err)
}

multidocReader := utilyaml.NewYAMLReader(bufio.NewReader(bytes.NewReader(yamlFile)))

for {
    buf, err := multidocReader.Read()
    if err != nil {
        if err == io.EOF {
            break
        }
        panic(err)
    }

    obj, _, err := decode(buf, nil, nil)
    if err != nil {
        panic(err)
    }
    ...
}

@astingengo
Copy link

How to import a custom kind that was created on the fly with a crds?
E.g. How to deploy Elasticsearch with this approach as I ended up having the same issue but now as:

panic: no kind "Elasticsearch" is registered for version "elasticsearch.k8s.elastic.co/v1" in scheme "pkg/runtime/scheme.go:100"

after sucessfully deployed crds and operator

@Subhajit97
Copy link

Subhajit97 commented Apr 7, 2023

Can we get the line number while parsing K8s yaml spec into client-go data structures?
I have a single configuration file with multiple resources (separated by ---) specified in it. It will be good to get the line number (at least the starting line from where the resource configuration starts).

@lavalamp
Copy link
Member

lavalamp commented Apr 7, 2023

May I suggest y'all ask in slack or something? This issue is from 2017 and it's not a place where many people are going to be expecting questions

no kind "Elasticsearch" is registered for version "elasticsearch.k8s.elastic.co/v1" in scheme "pkg/runtime/scheme.go:100"

The solution to this is to register the type with the scheme...

Can we get the line number while parsing K8s yaml spec into client-go data structures?

All the mechanisms I'm aware of have no way of getting a line number like that at the place where they hit an error

@christianhuening
Copy link

I find it amusing this pops up every so often over the past 6 years. Yet I wonder: would it make sense to document this some place?

@jmazzitelli
Copy link

I find it amusing this pops up every so often over the past 6 years. Yet I wonder: would it make sense to document this some place?

You know what would make more sense and will make all of this go away? The types just need to add yaml struct tags next to the already-existing json tags. I don't understand why that hasn't been done in all the years this keeps coming up. It's so simple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
lifecycle/rotten Denotes an issue or PR that has aged beyond stale and will be auto-closed.
Projects
None yet
Development

No branches or pull requests