Skip to content

SharedIndexInformer for Custom resource stuck in "Start listing and watching" stage in reflector runnable #2042

@sharath-sri-chellappa

Description

@sharath-sri-chellappa

Client Version
14.0.0

Kubernetes Version
1.22.4

Java Version
Java 11

I am trying to create a SharedIndexInformer for a Kubevirt Virtual Machine Instance (custom resource - http://kubevirt.io/api-reference/main/index.html) and carry out a set of actions on add for the Custom Resource. I have used the Official Kubernetes Java client for the creation of the SharedIndexInformer (not fabric8)

I have gone ahead and created the libraries/models for the custom resource java client from the instructions given here - https://github.com/kubernetes-client/java/blob/master/docs/generate-model-from-third-party-resources.md

What I seem to be stuck at is when I am creating the sharedIndexInformer for the VMI Resource, the call to io.kubernetes.client.informer.cache.ReflectorRunnable.run() function seems to be stuck at Start listing and watching (code line - https://github.com/kubernetes-client/java/blob/master/util/src/main/java/io/kubernetes/client/informer/cache/ReflectorRunnable.java#L87)

Output Snippet

Kubeconfig Path - C:\GitFolder/config
16:31:55.867 [informer-controller-V1Pod] INFO io.kubernetes.client.informer.cache.Controller - informer#Controller: ready to run resync & reflector runnable
16:31:55.867 [informer-controller-V1alpha3VirtualMachineInstance] INFO io.kubernetes.client.informer.cache.Controller - informer#Controller: ready to run resync & reflector runnable
16:31:55.870 [informer-controller-V1Pod] INFO io.kubernetes.client.informer.cache.Controller - informer#Controller: resync skipped due to 0 full resync period
16:31:55.873 [controller-reflector-io.kubernetes.client.openapi.models.V1Pod-1] INFO io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.openapi.models.V1Pod#Start listing and watching...
16:31:55.873 [controller-reflector-io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance-1] INFO io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance#Start listing and watching...
16:31:58.524 [controller-reflector-io.kubernetes.client.openapi.models.V1Pod-1] DEBUG io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.openapi.models.V1Pod#Extract resourceVersion 110011207 list meta
16:31:58.524 [controller-reflector-io.kubernetes.client.openapi.models.V1Pod-1] DEBUG io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.openapi.models.V1Pod#Start watching with 110011207...
16:31:58.524 [controller-reflector-io.kubernetes.client.openapi.models.V1Pod-1] DEBUG io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.openapi.models.V1Pod#Start watch with resource version 110011207
16:31:59.461 [controller-reflector-io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance-1] INFO io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance#Start listing and watching...
16:32:00.726 [controller-reflector-io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance-1] INFO io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance#Start listing and watching...
16:32:01.994 [controller-reflector-io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance-1] INFO io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance#Start listing and watching...
16:32:03.267 [controller-reflector-io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance-1] INFO io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance#Start listing and watching...
16:32:04.533 [controller-reflector-io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance-1] INFO io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance#Start listing and watching...
16:32:05.810 [controller-reflector-io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance-1] INFO io.kubernetes.client.informer.cache.ReflectorRunnable - class io.kubernetes.client.kubevirt.models.V1alpha3VirtualMachineInstance#Start listing and watching...

If you look at this output, an informer which I created from V1Pod resource seems to work correctly however that is not the case with V1alpha3VirtualMachineInstances.

Code Snippet for my Informer -

private static Call listNamespacedVmiCall(ApiClient client, String namespace, String pretty, Boolean allowWatchBookmarks, String _continue, String fieldSelector, String labelSelector, Integer limit, String resourceVersion, String resourceVersionMatch, Integer timeoutSeconds, Boolean watch, ApiCallback _callback) throws ApiException {
	Object localVarPostBody = null;
	String localVarPath = "/api/v1/namespaces/{namespace}/vmi".replaceAll("\\{namespace\\}", client.escapeString(namespace.toString()));
	List<Pair> localVarQueryParams = new ArrayList();
	List<Pair> localVarCollectionQueryParams = new ArrayList();
	if (pretty != null) {
		localVarQueryParams.addAll(client.parameterToPair("pretty", pretty));
	}

	if (allowWatchBookmarks != null) {
		localVarQueryParams.addAll(client.parameterToPair("allowWatchBookmarks", allowWatchBookmarks));
	}

	if (_continue != null) {
		localVarQueryParams.addAll(client.parameterToPair("continue", _continue));
	}

	if (fieldSelector != null) {
		localVarQueryParams.addAll(client.parameterToPair("fieldSelector", fieldSelector));
	}

	if (labelSelector != null) {
		localVarQueryParams.addAll(client.parameterToPair("labelSelector", labelSelector));
	}

	if (limit != null) {
		localVarQueryParams.addAll(client.parameterToPair("limit", limit));
	}

	if (resourceVersion != null) {
		localVarQueryParams.addAll(client.parameterToPair("resourceVersion", resourceVersion));
	}

	if (resourceVersionMatch != null) {
		localVarQueryParams.addAll(client.parameterToPair("resourceVersionMatch", resourceVersionMatch));
	}

	if (timeoutSeconds != null) {
		localVarQueryParams.addAll(client.parameterToPair("timeoutSeconds", timeoutSeconds));
	}

	if (watch != null) {
		localVarQueryParams.addAll(client.parameterToPair("watch", watch));
	}

	Map<String, String> localVarHeaderParams = new HashMap<String, String>();
	Map<String, String> localVarCookieParams = new HashMap<String, String>();
	Map<String, Object> localVarFormParams = new HashMap<String, Object>();
	String[] localVarAccepts = new String[]{"application/json", "application/yaml", "application/vnd.kubernetes.protobuf", "application/json;stream=watch", "application/vnd.kubernetes.protobuf;stream=watch"};
	String localVarAccept = client.selectHeaderAccept(localVarAccepts);
	if (localVarAccept != null) {
		localVarHeaderParams.put("Accept", localVarAccept);
	}

	String[] localVarContentTypes = new String[0];
	String localVarContentType = client.selectHeaderContentType(localVarContentTypes);
	localVarHeaderParams.put("Content-Type", localVarContentType);
	String[] localVarAuthNames = new String[]{"BearerToken"};

	return client.buildCall(
			localVarPath,
			"GET",
			localVarQueryParams,
			localVarCollectionQueryParams,
			localVarPostBody,
			localVarHeaderParams,
			localVarCookieParams,
			localVarFormParams,
			localVarAuthNames,
			_callback);
}
public static SharedIndexInformer<V1alpha3VirtualMachineInstance> createVmiInformer(String namespace, SharedInformerFactory factory, CoreV1Api coreV1Api) {
	ApiClient apiClient = coreV1Api.getApiClient();
        SharedIndexInformer<V1alpha3VirtualMachineInstance> vmiInformer =
                factory.sharedIndexInformerFor(
                (CallGeneratorParams params) -> {
                    return listNamespacedVmiCall(
                            apiClient,
                            namespace,
                            null,
                            null,
                            null,
                            null,
                            null,
                            null,
                            params.resourceVersion,
                            null,
                            params.timeoutSeconds,
                            params.watch,
                            null);
                },
                V1alpha3VirtualMachineInstance.class,
                V1alpha3VirtualMachineInstanceList.class);

	vmiInformer.addEventHandler(
			new ResourceEventHandler<V1alpha3VirtualMachineInstance>() {
				@Override
				public void onAdd(V1alpha3VirtualMachineInstance vmi) {
					System.out.printf("%s VMI added! Timestamp %s\n", vmi.getMetadata().getName(),
							vmi.getMetadata().getCreationTimestamp());
				}

				@Override
				public void onUpdate(V1alpha3VirtualMachineInstance oldVmi, V1alpha3VirtualMachineInstance newVmi) {
					System.out.printf(
							"%s => %s VMI updated!\n",
							oldVmi.getMetadata().getName(), newVmi.getMetadata().getName());
					System.out.printf("Data Updated - \nDifferences between the 2 strings - %s\n",
							StringUtils.difference(oldVmi.getMetadata().toString(), newVmi.getMetadata().toString()));
				}

				@Override
				public void onDelete(V1alpha3VirtualMachineInstance vmi, boolean deletedFinalStateUnknown) {
					System.out.printf("%s VMI deleted!\n", vmi.getMetadata().getName());
				}
			});

	return vmiInformer;
}

Here listNamespacedVmiCall is the function that lists and watches for the virtual machine instance on a particular namespace. This is a static function which has been written by me.

The Virtual Machine Class has been autogenerated using the instructions from the link given above (https://github.com/kubernetes-client/java/blob/master/docs/generate-model-from-third-party-resources.md) and looks extremely similar to the V1Pod Class -

public class V1alpha3VirtualMachineInstance implements io.kubernetes.client.common.KubernetesObject {
  public static final String SERIALIZED_NAME_API_VERSION = "apiVersion";
  @SerializedName(SERIALIZED_NAME_API_VERSION)
  private String apiVersion;

  public static final String SERIALIZED_NAME_KIND = "kind";
  @SerializedName(SERIALIZED_NAME_KIND)
  private String kind;

  public static final String SERIALIZED_NAME_METADATA = "metadata";
  @SerializedName(SERIALIZED_NAME_METADATA)
  private V1ObjectMeta metadata = null;

  public static final String SERIALIZED_NAME_SPEC = "spec";
  @SerializedName(SERIALIZED_NAME_SPEC)
  private V1alpha3VirtualMachineInstanceSpec spec;

  public static final String SERIALIZED_NAME_STATUS = "status";
  @SerializedName(SERIALIZED_NAME_STATUS)
  private V1alpha3VirtualMachineInstanceStatus status;


  public V1alpha3VirtualMachineInstance apiVersion(String apiVersion) {
    
    this.apiVersion = apiVersion;
    return this;
  }

   /**
   * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
   * @return apiVersion
  **/
  @javax.annotation.Nullable
  @ApiModelProperty(value = "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources")

  public String getApiVersion() {
    return apiVersion;
  }


  public void setApiVersion(String apiVersion) {
    this.apiVersion = apiVersion;
  }


  public V1alpha3VirtualMachineInstance kind(String kind) {
    
    this.kind = kind;
    return this;
  }

   /**
   * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
   * @return kind
  **/
  @javax.annotation.Nullable
  @ApiModelProperty(value = "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds")

  public String getKind() {
    return kind;
  }


  public void setKind(String kind) {
    this.kind = kind;
  }


  public V1alpha3VirtualMachineInstance metadata(V1ObjectMeta metadata) {
    
    this.metadata = metadata;
    return this;
  }

   /**
   * Get metadata
   * @return metadata
  **/
  @javax.annotation.Nullable
  @ApiModelProperty(value = "")

  public V1ObjectMeta getMetadata() {
    return metadata;
  }


  public void setMetadata(V1ObjectMeta metadata) {
    this.metadata = metadata;
  }


  public V1alpha3VirtualMachineInstance spec(V1alpha3VirtualMachineInstanceSpec spec) {
    
    this.spec = spec;
    return this;
  }

   /**
   * Get spec
   * @return spec
  **/
  @ApiModelProperty(required = true, value = "")

  public V1alpha3VirtualMachineInstanceSpec getSpec() {
    return spec;
  }


  public void setSpec(V1alpha3VirtualMachineInstanceSpec spec) {
    this.spec = spec;
  }


  public V1alpha3VirtualMachineInstance status(V1alpha3VirtualMachineInstanceStatus status) {
    
    this.status = status;
    return this;
  }

   /**
   * Get status
   * @return status
  **/
  @javax.annotation.Nullable
  @ApiModelProperty(value = "")

  public V1alpha3VirtualMachineInstanceStatus getStatus() {
    return status;
  }


  public void setStatus(V1alpha3VirtualMachineInstanceStatus status) {
    this.status = status;
  }


  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    V1alpha3VirtualMachineInstance v1alpha3VirtualMachineInstance = (V1alpha3VirtualMachineInstance) o;
    return Objects.equals(this.apiVersion, v1alpha3VirtualMachineInstance.apiVersion) &&
        Objects.equals(this.kind, v1alpha3VirtualMachineInstance.kind) &&
        Objects.equals(this.metadata, v1alpha3VirtualMachineInstance.metadata) &&
        Objects.equals(this.spec, v1alpha3VirtualMachineInstance.spec) &&
        Objects.equals(this.status, v1alpha3VirtualMachineInstance.status);
  }

  @Override
  public int hashCode() {
    return Objects.hash(apiVersion, kind, metadata, spec, status);
  }


  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("class V1alpha3VirtualMachineInstance {\n");
    sb.append("    apiVersion: ").append(toIndentedString(apiVersion)).append("\n");
    sb.append("    kind: ").append(toIndentedString(kind)).append("\n");
    sb.append("    metadata: ").append(toIndentedString(metadata)).append("\n");
    sb.append("    spec: ").append(toIndentedString(spec)).append("\n");
    sb.append("    status: ").append(toIndentedString(status)).append("\n");
    sb.append("}");
    return sb.toString();
  }

  /**
   * Convert the given object to string with each line indented by 4 spaces
   * (except the first line).
   */
  private String toIndentedString(Object o) {
    if (o == null) {
      return "null";
    }
    return o.toString().replace("\n", "\n    ");
  }

}

The same can be said for V1alpha3VirtualMachineInstanceList.java (which is on the lines of V1PodList.java)

public class V1alpha3VirtualMachineInstanceList implements io.kubernetes.client.common.KubernetesListObject {
  public static final String SERIALIZED_NAME_API_VERSION = "apiVersion";
  @SerializedName(SERIALIZED_NAME_API_VERSION)
  private String apiVersion;

  public static final String SERIALIZED_NAME_ITEMS = "items";
  @SerializedName(SERIALIZED_NAME_ITEMS)
  private List<V1alpha3VirtualMachineInstance> items = new ArrayList<>();

  public static final String SERIALIZED_NAME_KIND = "kind";
  @SerializedName(SERIALIZED_NAME_KIND)
  private String kind;

  public static final String SERIALIZED_NAME_METADATA = "metadata";
  @SerializedName(SERIALIZED_NAME_METADATA)
  private V1ListMeta metadata = null;


  public V1alpha3VirtualMachineInstanceList apiVersion(String apiVersion) {
    
    this.apiVersion = apiVersion;
    return this;
  }

   /**
   * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
   * @return apiVersion
  **/
  @javax.annotation.Nullable
  @ApiModelProperty(value = "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources")

  public String getApiVersion() {
    return apiVersion;
  }


  public void setApiVersion(String apiVersion) {
    this.apiVersion = apiVersion;
  }


  public V1alpha3VirtualMachineInstanceList items(List<V1alpha3VirtualMachineInstance> items) {
    
    this.items = items;
    return this;
  }

  public V1alpha3VirtualMachineInstanceList addItemsItem(V1alpha3VirtualMachineInstance itemsItem) {
    this.items.add(itemsItem);
    return this;
  }

   /**
   * List of virtualmachineinstances. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md
   * @return items
  **/
  @ApiModelProperty(required = true, value = "List of virtualmachineinstances. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md")

  public List<V1alpha3VirtualMachineInstance> getItems() {
    return items;
  }


  public void setItems(List<V1alpha3VirtualMachineInstance> items) {
    this.items = items;
  }


  public V1alpha3VirtualMachineInstanceList kind(String kind) {
    
    this.kind = kind;
    return this;
  }

   /**
   * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
   * @return kind
  **/
  @javax.annotation.Nullable
  @ApiModelProperty(value = "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds")

  public String getKind() {
    return kind;
  }


  public void setKind(String kind) {
    this.kind = kind;
  }


  public V1alpha3VirtualMachineInstanceList metadata(V1ListMeta metadata) {
    
    this.metadata = metadata;
    return this;
  }

   /**
   * Get metadata
   * @return metadata
  **/
  @javax.annotation.Nullable
  @ApiModelProperty(value = "")

  public V1ListMeta getMetadata() {
    return metadata;
  }


  public void setMetadata(V1ListMeta metadata) {
    this.metadata = metadata;
  }


  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    V1alpha3VirtualMachineInstanceList v1alpha3VirtualMachineInstanceList = (V1alpha3VirtualMachineInstanceList) o;
    return Objects.equals(this.apiVersion, v1alpha3VirtualMachineInstanceList.apiVersion) &&
        Objects.equals(this.items, v1alpha3VirtualMachineInstanceList.items) &&
        Objects.equals(this.kind, v1alpha3VirtualMachineInstanceList.kind) &&
        Objects.equals(this.metadata, v1alpha3VirtualMachineInstanceList.metadata);
  }

  @Override
  public int hashCode() {
    return Objects.hash(apiVersion, items, kind, metadata);
  }


  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("class V1alpha3VirtualMachineInstanceList {\n");
    sb.append("    apiVersion: ").append(toIndentedString(apiVersion)).append("\n");
    sb.append("    items: ").append(toIndentedString(items)).append("\n");
    sb.append("    kind: ").append(toIndentedString(kind)).append("\n");
    sb.append("    metadata: ").append(toIndentedString(metadata)).append("\n");
    sb.append("}");
    return sb.toString();
  }

  /**
   * Convert the given object to string with each line indented by 4 spaces
   * (except the first line).
   */
  private String toIndentedString(Object o) {
    if (o == null) {
      return "null";
    }
    return o.toString().replace("\n", "\n    ");
  }

}

I am really not sure where I am going wrong here since both classes implement io.kubernetes.client.common.KubernetesListObject and can work exactly like the V1Pod and V1PodList classes. Can someone tell me what I am doing wrong here ?

The way I am creating my informer is as follows -

        String kubeConfigPath = System.getenv("HOME") + "/config";
        System.out.println("Kubeconfig Path - " + kubeConfigPath);
        // loading the out-of-cluster config, a kubeconfig from file-system
        ApiClient client =
                ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kubeConfigPath))).build();

        // set the global default api-client to the in-cluster one from above
        Configuration.setDefaultApiClient(client);
        CoreV1Api coreV1Api = new CoreV1Api();
        ApiClient apiClient = coreV1Api.getApiClient();
        OkHttpClient httpClient =
                apiClient.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build();
        apiClient.setHttpClient(httpClient);

        SharedInformerFactory factory = new SharedInformerFactory();
        SharedIndexInformer<V1alpha3VirtualMachineInstance> vmiInformer = createVmiInformer(<namespace>, factory, coreV1Api);
        factory.startAllRegisteredInformers();

Any ideas would help me proceed here would be welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    lifecycle/rottenDenotes an issue or PR that has aged beyond stale and will be auto-closed.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions