Reputation: 1568
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
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)
but I can fill in all fields so is it really empty?
so like it's empty but it's not nil?
And there don't seem to be any methods attached to it
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
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