kubernetes-client / java

Official Java client library for kubernetes
http://kubernetes.io/
Apache License 2.0
3.54k stars 1.88k forks source link

Delete and Modify events are not getting trigged #3652

Open MohammadNC opened 3 weeks ago

MohammadNC commented 3 weeks ago

Describe the bug I am watching kubernate secret in infine loop, in any change happened to secret, watch should trigger appropriate events like ADDED or MODIFIED or DELETED. but in my case DELETE event is not triggered and most important point is that Thread is getting struck.

Client Version 19.0.0

Kubernetes Version 1.23.10

Java Version . Java 17

below is code snippet to reproduce the issue.

import io.kubernetes.client.openapi.ApiClient; import io.kubernetes.client.openapi.ApiException; import io.kubernetes.client.openapi.Configuration; import io.kubernetes.client.openapi.apis.CoreV1Api; import io.kubernetes.client.openapi.models.V1Secret; import io.kubernetes.client.openapi.models.V1SecretList; import io.kubernetes.client.util.Config; import io.kubernetes.client.util.Watch; import com.google.gson.reflect.TypeToken;

import java.io.IOException; import java.util.List;

public class SecretWatcher {

public static void watchSecret() throws IOException, ApiException {
    ApiClient client = Config.defaultClient();
    CoreV1Api api = new CoreV1Api(client);

    List<String> secretNames = List.of("secret1", "secret2", "secret3");

    while (true) {
try (Watch<V1Secret> watch = watchForNamespace(this.nameSpace)) {
        try {
            for (Watch.Response<V1Secret> item : watch) {
                if (secretNames.contains(item.object.getMetadata().getName())) {
                    logger.debug("Item type received is: " + item.type);
                    switch (item.type) {
                        case "ADDED":
                            handleEventAdded(item.object);
                            break;
                        case "MODIFIED":
                            handleEventModified(item.object);
                            break;
                        case "DELETED":
                            handleEventDeleted(item.object);
                            break;
                        default:
                            continue;
                    }
                    break;
                }
            }
        } catch (Exception e) {

            logger.error("Exception occurred during handling item.type event. Reason: " + e.getMessage());
        }
    } catch (Exception excep) {
        logger.error("Exception occurred while monitoring K8 Resource  " + excep.getMessage());
    }
    }
}

    public Watch<V1Secret> watchForNamespace(String nameSpace) {
    Watch<V1Secret> watch = null;
    try {
        watch = Watch.createWatch(getClient(), getApi().listNamespacedSecretCall(nameSpace, null, null, null, null,
                null, null, null, null, Boolean.TRUE, null, Boolean.TRUE, null), new TypeToken<Watch.Response<V1Secret>>() {
        }.getType());
        logger.debug("Watch created for secrets in namespace: " + nameSpace);
    } catch (ApiException e) {
        logger.error("Failed to create watch for secrets in namespace:" + nameSpace + " Reason: " + e.getMessage());
    }
    return watch;
}

}

Kindly suggest how we can achieve all event triggered correctly.

MohammadNC commented 3 weeks ago

And also noticed that with the below code snippet thread is getting stuck.

private boolean checkConfigFilesPresent(String nameSpace, String secretName, ArrayList fileNameList) {

    try {
        V1SecretList listSecret = new CoreV1Api().listNamespacedSecret(nameSpace, null, null, null, null,
                null, null, null, null, null, null, Boolean.TRUE);
        for (V1Secret secret : listSecret.getItems()) {
            if (secret.getMetadata().getName().equals(secretName)) {
                Map<String, byte[]> map = secret.getData();
                for (String fileName : fileNameList) {
                    if (map.get(fileName) == null) {
                        return false;
                    }
                }
            }
        }

    } catch (ApiException e) {
        e.printStackTrace();
    }
    return true;
}

please check this one also.

brendandburns commented 3 weeks ago

Getting watches right is tricky, I would strongly encourage you to use the Informer class instead of trying to roll your own watch code, there is an example here:

https://github.com/kubernetes-client/java/blob/b85fb963d2796a1cb982d73ae2ae36cdce857e5e/examples/examples-release-latest/src/main/java/io/kubernetes/client/examples/InformerExample.java

MohammadNC commented 3 weeks ago

Hi @brendandburns

Thank you for your suggestion.

I implemented the following code snippet to monitor Kubernetes secrets. However, when performing different operations like adding, modifying, and deleting a secret, no events were triggered.

`package com.example.demo.informer;

import io.kubernetes.client.informer.ResourceEventHandler; import io.kubernetes.client.informer.SharedIndexInformer; import io.kubernetes.client.informer.SharedInformerFactory; import io.kubernetes.client.openapi.ApiClient; import io.kubernetes.client.openapi.apis.CoreV1Api; import io.kubernetes.client.openapi.models.V1Secret; import io.kubernetes.client.openapi.models.V1SecretList; import okhttp3.OkHttpClient; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger;

import java.util.concurrent.TimeUnit;

public class SecretWatcher implements Runnable {

private static final Logger logger = LogManager.getLogger(SecretWatcher.class);
String nameSpace;
private final CoreV1Api api;
SharedInformerFactory informerFactory;

public SecretWatcher(String nameSpace) {
    this.nameSpace = nameSpace;
    this.api = new CoreV1Api();
    ApiClient apiClient = this.api.getApiClient();
    OkHttpClient httpClient =
            apiClient.getHttpClient().newBuilder().readTimeout(0, TimeUnit.SECONDS).build();
    apiClient.setHttpClient(httpClient);
    this.informerFactory = new SharedInformerFactory(apiClient);
}

@Override
public void run() {
    logger.info("Starting ConfigWatchProcess ");

    while (true) {
        try {
            SharedIndexInformer<V1Secret> secretInformer =
                    this.informerFactory.sharedIndexInformerFor(
                            (CallGeneratorParams) -> {
                                return this.api.listNamespacedSecretCall(nameSpace, null, null,
                                        null, null, null, null, null,
                                        null, Boolean.TRUE, null, Boolean.TRUE, null);
                            },
                            V1Secret.class,
                            V1SecretList.class);

            secretInformer.addEventHandler(
                    new ResourceEventHandler<V1Secret>() {
                        @Override
                        public void onAdd(V1Secret secret) {
                            String secretName = getSecretName(secret);
                            logger.warn("Add event is triggered for Secret : {}", secretName);
                            handleEventAdded(secret);
                        }

                        @Override
                        public void onUpdate(V1Secret oldSecret, V1Secret newSecret) {
                            String olSecretName = getSecretName(newSecret);
                            String newSecretName = getSecretName(newSecret);
                            logger.warn("olSecretName: {}, newSecretName:{}", olSecretName, newSecretName);
                            handleEventModified(newSecret);
                        }

                        @Override
                        public void onDelete(V1Secret secret, boolean deletedFinalStateUnknown) {
                            String secretName = getSecretName(secret);
                            logger.warn("Delete event is triggered for Secret : {}", secretName);
                            handleEventDeleted(secret);
                        }
                    });
            logger.warn("Starting the informer..");
            informerFactory.startAllRegisteredInformers();
            Thread.sleep(1000);
        } catch (Exception e) {
            logger.error("Exception occurred while monitorning...");
        }
    }

}

private void handleEventAdded(V1Secret secret) {
    logger.warn("Handling of Add secret ....");
}

private void handleEventModified(V1Secret secret) {
    logger.warn("Handling of Modified secret ....");
}

private void handleEventDeleted(V1Secret secret) {
    logger.warn("Handling of deleted secret ....");
}

private String getSecretName(V1Secret secret) {
    return secret.getMetadata().getName();
}

} `

Could you please suggest how to resolve this issue so that all events are properly triggered?

My request is to continuously monitor Kubernetes secrets, ensuring that whenever any action is performed on a secret, the appropriate event is triggered.

brendandburns commented 3 weeks ago

Please review the InformerExample here:

https://github.com/kubernetes-client/java/blob/master/examples/examples-release-latest/src/main/java/io/kubernetes/client/examples/InformerExample.java