Jeffail/gabs

K8s `unstructured` helper?

s3rj1k opened this issue · 6 comments

I am curious would it be possible to add some simple wrapper support for K8s unstructured, as it would be really great to use gabs with that object natively.

Hey @s3rj1k, I'd need some more details about that, but, skimming through this article, it sounds like you want to create a gabs container from a weakly-decoded JSON. Is that correct? If yes, try this:

package main

import (
	"fmt"

	"github.com/Jeffail/gabs/v2"
)

func main() {
	newEnvs := []interface{}{
		map[string]interface{}{
			"name": "NAMESPACE",
			"valueFrom": map[string]interface{}{
				"fieldRef": map[string]interface{}{
					"fieldPath": "metadata.namespace",
				},
			},
		},
		map[string]interface{}{
			"name": "POD_UID",
			"valueFrom": map[string]interface{}{
				"fieldRef": map[string]interface{}{
					"fieldPath": "metadata.uid",
				},
			},
		},
	}

	obj := gabs.Wrap(newEnvs)
	fmt.Println(obj.Path("*.valueFrom.fieldRef.fieldPath").String())
}

@mihaitodor hey, thanks for quick response, I just want a helper to convert gabs <-> unstructured

https://github.com/kubernetes/apimachinery/blob/v0.28.2/pkg/apis/meta/v1/unstructured/unstructured.go#L45

Basically unstructured has embedded Object map[string]interface{} where gabs container has interface{}.

The flow would be something like this:

  • unstructured -> gabs.container
  • some gabs magic manipulating data
  • gabs.container -> unstructured

(hopefully with minimal memory allocation)

@s3rj1k Sure, have you tried the .Data() method?

package main

import (
	"fmt"

	"github.com/Jeffail/gabs/v2"
	"github.com/davecgh/go-spew/spew"
)

func main() {
	newEnvs := []interface{}{
		map[string]interface{}{
			"name": "NAMESPACE",
			"valueFrom": map[string]interface{}{
				"fieldRef": map[string]interface{}{
					"fieldPath": "metadata.namespace",
				},
			},
		},
		map[string]interface{}{
			"name": "POD_UID",
			"valueFrom": map[string]interface{}{
				"fieldRef": map[string]interface{}{
					"fieldPath": "metadata.uid",
				},
			},
		},
	}

	obj := gabs.Wrap(newEnvs)
	fmt.Println(obj.Path("*.valueFrom.fieldRef.fieldPath").String())

	spew.Dump(obj.Data())

	obj.SetP(666, "0.valueFrom.fieldRef.fieldPath")

	spew.Dump(obj.Data())
}

PS: You should be able to use type assertions to get the actual type from the interface{} returned by Data() (in the example above, that would be obj.Data().([]interface{})).

Flow is a bit different, reversed

package main

import (
	"log"

	"github.com/Jeffail/gabs/v2"
	"github.com/davecgh/go-spew/spew"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"sigs.k8s.io/yaml"
)

func main() {
	b := []byte(`
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: default
spec:
  containers:
  - name: nginx
    image: nginx:latest
`)

	obj := new(unstructured.Unstructured)

	err := yaml.Unmarshal(b, obj)
	if err != nil {
		log.Fatalf("Failed to unmarshal YAML: %v", err)
	}

	container := gabs.Wrap(obj.Object)

	objNew := new(unstructured.Unstructured)
	objNew.SetUnstructuredContent(container.Data().(map[string]interface{}))

	spew.Dump(obj.Object)
	spew.Dump(objNew.Object)
}

so what I am after here is basically

container.Data().(map[string]interface{})

a more streamlined version of this ^^^

Sure, but that's specific to your use case. Nothing wrong with publishing your own gabs wrapper which exposes a method that returns container.Data().(map[string]interface{}), even if it's just some boilerplate.

Sure, but that's specific to your use case. Nothing wrong with publishing your own gabs wrapper which exposes a method that returns container.Data().(map[string]interface{}), even if it's just some boilerplate.

Yea, I was hoping for method similar to Data but that will return map[string]interface{} by type casting it internally and panic when error occurs.