kubernetes / client-go

Go client for Kubernetes.
Apache License 2.0
9.12k stars 2.95k forks source link

Parsing of generic object from yaml without type switch? #1379

Closed jvilhuber closed 2 months ago

jvilhuber commented 2 months ago

I see https://github.com/kubernetes/client-go/issues/193 which is handy to parse yaml data into a go-object, but the methodology there involves type-casting into specific types and then dealing with whatever they are (specifically).

I have a slightly different use-case: I want to read a yaml file for one or more objects, convert them into the corresponding kubernetes objects, and then, ignoring the type, modify some of the ObjectMeta (mostly add some annotations, labels, and/or ownership links), and then create-or-update the object. This is purely doing some modifications on the ObjectMeta, and ignores any type-specific Spec fields.

If I use a type-switch, I'll wind up with a boat-load of essentially duplicated code (including the Client.Create()), which I really want to avoid. However, the object I get from serializer.NewCodecFactory(reconciler.scheme).UniversalDeserializer().Decode.decode() is a runtime.Object, which apparently can't be converted into a v1.PartialObjectMetadata, which is probably what (in theory) I want.

rough code (ignoring error checking):

yamlData := `apiVersion: v1
kind: PersistentVolume
metadata:
  labels:
    app: someApp
  name: pv-somepv
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 100Gi
`
decode := serializer.NewCodecFactory(reconciler.Scheme).UniversalDeserializer().Decode
runObj, gvk, err := decode([]byte(yamlData), nil, nil)
if err != nil {
 ...
}
obj, ok := runObj.(*v1.PartialObjectMetadata)
if !ok {
 ....
}
obj.ObjectMeta.Labels["app.kubernetes.io/managed-by"] = "myController"
obj.ObjMeta.OwnerReferences = append(obj.ObjMeta.OwnerReferences, metav1.OwnerReference{
        APIVersion:        GroupVersion.String(),
        Kind:                  "mySpecialKind",
        Name:                "ownerName",
        UID:                    "ownerUid",
        Controller:          ptr.To(true),
        BlockOwnerDeletion: ptr.To(true),
    })
err = reconciler.Client.Create(ctx, obj)
if err != nil {
 ...
}

This fails at this part: obj, ok := runObj.(*v1.PartialObjectMetadata)

Any suggestions?

jvilhuber commented 2 months ago

AHA! I found my mistake: The trick is to typecast to a client.Object, which is what the client internally uses, anyway. These can still be manipulated in certain ways, and it removes the need to do a type-switch.

I.e.

        obj, ok := runObj.(client.Object)