Jim
Jim

Reputation: 1568

client-go var set from function which returns empty interface; can't seem to use it though("obj is not a type")...need clarification

So, I'm writing a client-go app and took some code as boilerplate, but I'm struggling with a portion of code:

Here's what I'm using for boilerplate: https://www.codetd.com/en/article/7292846

and I'm tweaking to my needs. Here's my code:

// syncToStdout is the business logic of the controller. In this controller it simply prints
// information about the secret to stdout. In case an error happened, it has to simply return the error.
// The retry logic should not be part of the business logic.
func (c *Controller) syncToStdout(key string) error {
    obj, exists, err := c.indexer.GetByKey(key)
    if err != nil {
        klog.Errorf("Fetching object with key %s from store failed with %v", key, err)
        return err
    }

    if !exists {
        klog.Infof("delete? %+v", obj)
        // Below we will warm up our cache with a Secret, so that we will see a delete action for one secret
        klog.Infof("Secret %s does not exist anymore, but not delering from AMZ", key)
    } else {

        // This is where I am stuck...
        // I need to dereference `obj` to exact annotations...
        // But this doesn't work.
        // `obj` is an empty interface
        objectMeta := obj{
            &v1.Secret{
                ObjectMeta: metav1.ObjectMeta{},
            },
        }

        syncEnabled := objectMeta.Annotations[operatorName+"/sync-enabled"]
        acmEnabled := objectMeta.Annotations[operatorName+"/acm-enabled"]
        asmEnabled := objectMeta.Annotations[operatorName+"/asm-enabled"]

        //obj{v1.Secret{TypeMeta:v1.TypeMeta}
        klog.Infof("add/update? %#v", obj)

        // Note that you also have to check the uid if you have a local controlled resource, which
        // is dependent on the actual instance, to detect that a Secret was recreated with the same name
        klog.Infof("Sync/Add/Update for Secret %s", obj.(*v1.Secret).GetName())
    }
    return nil
}

OK, so I'm working with K8s Secrets here. I found the following:

https://github.com/kubernetes/client-go/issues/297 which led to https://github.com/tamalsaha/workqueue-demo/blob/4012bea01dca4987de51a78c2edb529ae96fdbbf/main.go

but this isn't necessarily answering my question. If Indexer is returning an obj which is of type empty interface, I'm assuming it's not really nil? Empty interfaces elude me. I see them used often but I don't fully understand how they work. I can't reference the stuff inside this empty interface, though, and I'm not sure why.

IDE view

=IDE view

empty interface hint to obj creation states "obj is not a type," and this seems to be an empty interface returned from the function Indexer.GetByKey(key) empty interface hint to obj creation

but I can fill in all fields so is it really empty? but I can fill all fields so is it really empty?

so like it's empty but it's not nil? so like it's empty but it's not nil?

And there don't seem to be any methods attached to it no functions

so what's most confusing is that if I print it with %#v I get an entire object and one which contains the data I'm seeking:

(note that this has been scrubbed)

I1208 12:13:53.365680   98945 kubernetes-controller.go:67] 
add/update? &v1.Secret{TypeMeta:v1.TypeMeta{Kind:"", APIVersion:""}, 
ObjectMeta:v1.ObjectMeta{Name:"some-cert", GenerateName:"", 
Namespace:"cert-manager", SelfLink:"", UID:"ac3072ea-95fa-4b99-b284-b9755b1859c4", ResourceVersion:"xxxxx2721", Generation:0,
 CreationTimestamp:time.Date(2022, time.March, 28, 11, 24, 56, 0, time.Local), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string(nil),
 Annotations:map[string]string{"cert-manager.io/allow-direct-injection":"true"}, OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ManagedFields:
[]v1.ManagedFieldsEntry{v1.ManagedFieldsEntry{Manager:"thingy", 
Operation:"Update", APIVersion:"v1", Time:time.Date(2022, 
time.March, 28, 11, 24, 56, 0, time.Local), FieldsType:"FieldsV1",
 FieldsV1:(*v1.FieldsV1)(0x1400000ec78), Subresource:""}}}, 
Immutable:(*bool)(nil), Data:map[string][]uint8{"ca.crt":[]uint8{<REDACTED>}}, StringData:map[string]string(nil), 
Type:"Opaque"}

the Annotations are what I'm in need of grabbing. So how come go reflection can retrieve the data, but I can't reference it? Do I need to write a bunch of reflection code to get to what I need?

Upvotes: 1

Views: 248

Answers (1)

Jim
Jim

Reputation: 1568

OK! Finally figured this out.

The function returns an empty interface as a type. So I need to do a type assertion to propagate the data into the variable.

    } else {
        obj := obj.(*v1.Secret)   // <=== this is the "magic" line

        annotations := obj.ObjectMeta.Annotations

        syncEnabled := annotations[operatorName+"/sync-enabled"]
        cmEnabled := annotations[operatorName+"/acm-enabled"]
        smEnabled := annotations[operatorName+"/asm-enabled"]

        klog.Infof("add/update? %#v", obj)

        // Note that you also have to check the uid if you have a local controlled resource, which
        // is dependent on the actual instance, to detect that a Secret was recreated with the same name
        klog.Infof("Sync/Add/Update for Secret %s", obj.(*v1.Secret).GetName())
        //      }
    }
    return nil
}

I can probably do this more efficiently somewhere else, but this was what I needed. And I also see that the original coder used this in the code and I simply didn't understand.

Had I been paying closer attention, I'd have seen the reference to the answer in klog.Infof("Sync/Add/Update for Secret %s", obj.(*v1.Secret).GetName())

Oh well.

Upvotes: 0

Related Questions