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

Incorrect SharedInformerFactory#getExistingSharedIndexInformer #2937

Closed
hibnico opened this issue Mar 19, 2021 · 11 comments · Fixed by #2952 or #3230
Closed

Incorrect SharedInformerFactory#getExistingSharedIndexInformer #2937

hibnico opened this issue Mar 19, 2021 · 11 comments · Fixed by #2952 or #3230
Assignees
Labels
Milestone

Comments

@hibnico
Copy link
Contributor

hibnico commented Mar 19, 2021

The caching key of informers has changed in the client 5.2. And as far as I can see, the lookup function is not coherent with the create functions. The function getExistingSharedIndexInformer is sensitive to the name I chose for the Java class of my custom resource.

For instance, if I have a custom resource under the API com.acme/myapp:v1, and that I have created the Java class MyAppCustomResource annotated to be a proper fabric8 CustomResource, in the following code existingInformer will be null, but I would expect that existingInformer is the same instance as createdInformer.

SharedIndexInformer createdInformer = sharedInformerFactory.sharedIndexInformerForCustomResource(MyAppCustomResource.class, MyAppCustomResourceList.class)
SharedIndexInformer existingInformer = sharedInformerFactory.getExistingSharedIndexInformer(MyAppCustomResource.class)
@manusa manusa added the bug label Mar 22, 2021
@manusa
Copy link
Member

manusa commented Mar 22, 2021

Relates to: #2746 #2882

@rohanKanojia rohanKanojia self-assigned this Mar 25, 2021
@rohanKanojia
Copy link
Member

@hibnico : hello, I'm not able to reproduce this issue. I created MyAppCustomResource class like this:

package io.fabric8.crd;

import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Version;

@Group("com.acme")
@Version("v1")
public class MyAppCustomResource extends CustomResource<Void, Void> { }

When I'm running this code I'm able to see both createdInformer and existingInformer being not null and same:

try (KubernetesClient client = new DefaultKubernetesClient()) {
    SharedInformerFactory informerFactory = client.informers();

    SharedIndexInformer<MyAppCustomResource> createdInformer = informerFactory.sharedIndexInformerForCustomResource(MyAppCustomResource.class, MyAppCustomResourceList.class, 5 * 1000L);
    SharedIndexInformer<MyAppCustomResource> existingInformer = informerFactory.getExistingSharedIndexInformer(MyAppCustomResource.class);

    System.out.println(createdInformer);
    System.out.println(existingInformer);
}

Could you please check what I'm doing wrong?

@hibnico
Copy link
Contributor Author

hibnico commented Mar 25, 2021

The @kind annotation is missing, if the kind is not something close to the class name (considering the code, it seems about containing the plural of the class name), the lookup fails.

@rohanKanojia
Copy link
Member

Thanks for quick reply,

I think I can see what's the issue now. For calculating plural I'm resolving it from class name instead of checking kind:

for (Map.Entry<String, SharedIndexInformer> entry : this.informers.entrySet()) {
if (entry.getKey().contains(Pluralize.toPlural(apiTypeClass.getSimpleName().toLowerCase()))) {
foundSharedIndexInformer = (SharedIndexInformer<T>) entry.getValue();

rohanKanojia added a commit to rohanKanojia/kubernetes-client that referenced this issue Mar 25, 2021
…mer returns null when @kind configured

Right now we're just getting plural form of Class name for
CustomResource while resolving SharedInformerFactory informer map's
keys. But this leads to issues when user can configure his kind using
`@Kind` annotation. This PR changes plural calculation logic to this:
- Check @plural annotation, if present use this to check key
- Check @kind annotation, use Pluralize.toPlural(kind.value()) to check
  key
- Check Pluralize.toPlural(apiType.getSimpleName().toLowerCase()) to
  check key
manusa pushed a commit to rohanKanojia/kubernetes-client that referenced this issue Apr 7, 2021
…mer returns null when @kind configured

Right now we're just getting plural form of Class name for
CustomResource while resolving SharedInformerFactory informer map's
keys. But this leads to issues when user can configure his kind using
`@Kind` annotation. This PR changes plural calculation logic to this:
- Check @plural annotation, if present use this to check key
- Check @kind annotation, use Pluralize.toPlural(kind.value()) to check
  key
- Check Pluralize.toPlural(apiType.getSimpleName().toLowerCase()) to
  check key
metacosm pushed a commit to rohanKanojia/kubernetes-client that referenced this issue Apr 7, 2021
…mer returns null when @kind configured

Right now we're just getting plural form of Class name for
CustomResource while resolving SharedInformerFactory informer map's
keys. But this leads to issues when user can configure his kind using
`@Kind` annotation. This PR changes plural calculation logic to this:
- Check @plural annotation, if present use this to check key
- Check @kind annotation, use Pluralize.toPlural(kind.value()) to check
  key
- Check Pluralize.toPlural(apiType.getSimpleName().toLowerCase()) to
  check key
manusa pushed a commit that referenced this issue Apr 8, 2021
…ns null when @kind configured

Right now we're just getting plural form of Class name for
CustomResource while resolving SharedInformerFactory informer map's
keys. But this leads to issues when user can configure his kind using
`@Kind` annotation. This PR changes plural calculation logic to this:
- Check @plural annotation, if present use this to check key
- Check @kind annotation, use Pluralize.toPlural(kind.value()) to check
  key
- Check Pluralize.toPlural(apiType.getSimpleName().toLowerCase()) to
  check key
@manusa manusa added this to the 5.3.0 milestone Apr 8, 2021
@hibnico
Copy link
Contributor Author

hibnico commented Apr 8, 2021

I am sorry I have missed the updates on this issue. Looking at the fix now, it will indeed fix the issue I faced, but I fear that the lookup strategy in the map of informers is still not safe.

In the SharedInformerFactory, the function getInformerKey which is used at insert time in the internal map, is not the symmetric of isKeyOfType which is used at lookup time. The former function is very precise, the latter is matching the first that match a subset of the key. It means that if two informers with kinds with the same plural are created, both will be indeed created and stored in the internal map, but only one can be looked up.

@manusa manusa reopened this Apr 8, 2021
@rohanKanojia
Copy link
Member

I think I should have removed getExistingSharedIndexInformer(Class<T> apiTypeClass) while changing the key of informers to String rather than type. It doesn't make much sense to get Informer with just the type when we're forming a key based on OperationContext.

@rohanKanojia
Copy link
Member

What I'm saying is that it's no longer possible to fetch informer from map precisely when you have more than one informer of same type present in informer map.

Do you think it's a good idea to add getExistingSharedIndexInformer(OperationContext context)? I think it will be even more confusing.

@hibnico
Copy link
Contributor Author

hibnico commented Apr 8, 2021

Ok, I see the deeper issue at stake here. Making getExistingSharedIndexInformer working properly in every case seems hard.

In my use case, I wanted to have access to my specific informer into two parts of my software. And since in these two parts I can easily have access to the SharedInformerFactory, my code just leverages the fact that in an SharedInformerFactory is stored what I am looking for. Considering the effort at stake here, the safer way to handle my use case is just managing myself the sharing of my specific informer, and not rely on getExistingSharedIndexInformer.

@rohanKanojia
Copy link
Member

Umm, do you think it's a good idea for users to provide their own key. In case someone provides their own key like this:

informerFactory.inNamespace("foo").withInformerKey("MyAppResource_Informer1").sharedIndexInformerForCustomResource(MyAppCustomResource.class, MyAppCustomResourceList.class, 5 * 1000L);

Or maybe add another method List<SharedIndexInformer<T>> getExistingSharedIndexInformer ? It'll be up to the user to iterate informers matching type

@hibnico
Copy link
Contributor Author

hibnico commented Apr 8, 2021

Letting choose the key in the API is probably error prone, since the key must be unique. And then if a key has been overriden, the API should notify about it somehow. This doesn't simplify the API much.

I like though the idea of being able to list all existing informers. Maybe the API could list all the informers with their creating contexts. Something like: List<Entry<OperationContext, SharedIndexInformer<T>>> getExistingSharedIndexInformers()

@manusa manusa modified the milestones: 5.3.0, 5.4.0 Apr 16, 2021
@manusa manusa modified the milestones: 5.4.0, 5.5.0 May 24, 2021
rohanKanojia added a commit to rohanKanojia/kubernetes-client that referenced this issue Jun 9, 2021
…Informers` to return list of registered informers

+ Add a new method `getExistingSharedIndexInformers()` to
  SharedInformerFactory which would return a list of entries containing
  registered SharedIndexInformer and their respective OperationContext.

  This is added to overcome limitation of old `getExistingSharedIndexInformer(Class<T> apiTypeClass)`
  in case there are two informers registered with same type.
rohanKanojia added a commit to rohanKanojia/kubernetes-client that referenced this issue Jun 10, 2021
…Informers` to return list of registered informers

+ Add a new method `getExistingSharedIndexInformers()` to
  SharedInformerFactory which would return a list of entries containing
  registered SharedIndexInformer and their respective OperationContext.

  This is added to overcome limitation of old `getExistingSharedIndexInformer(Class<T> apiTypeClass)`
  in case there are two informers registered with same type.
rohanKanojia added a commit to rohanKanojia/kubernetes-client that referenced this issue Jun 11, 2021
…Informers` to return list of registered informers

+ Add a new method `getExistingSharedIndexInformers()` to
  SharedInformerFactory which would return a list of entries containing
  registered SharedIndexInformer and their respective OperationContext.

  This is added to overcome limitation of old `getExistingSharedIndexInformer(Class<T> apiTypeClass)`
  in case there are two informers registered with same type.
rohanKanojia added a commit to rohanKanojia/kubernetes-client that referenced this issue Jun 16, 2021
…Informers` to return list of registered informers

+ Add a new method `getExistingSharedIndexInformers()` to
  SharedInformerFactory which would return a list of entries containing
  registered SharedIndexInformer and their respective OperationContext.

  This is added to overcome limitation of old `getExistingSharedIndexInformer(Class<T> apiTypeClass)`
  in case there are two informers registered with same type.
manusa pushed a commit that referenced this issue Jun 18, 2021
…` to return list of registered informers

+ Add a new method `getExistingSharedIndexInformers()` to
  SharedInformerFactory which would return a list of entries containing
  registered SharedIndexInformer and their respective OperationContext.

  This is added to overcome limitation of old `getExistingSharedIndexInformer(Class<T> apiTypeClass)`
  in case there are two informers registered with same type.
@manusa
Copy link
Member

manusa commented Jun 23, 2021

Relates to: #2010

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment