Repository: kserve/kserve

Issue:
Need a API for user to wait for kfserving ready in SDK.
/kind feature

**Describe the solution you'd like**
[A clear and concise description of what you want to happen.]
Need a API for user to wait for kfserving ready in SDK. such as :
```
KFServing = KFServingClient()
KFServing.wait_for_ready('flower-sample', namespace='kubeflow')
```
if so user can easily know the kfserving has been ready and do following...

**Anything else you would like to add:**
[Miscellaneous information that will assist in solving the issue.]



Discussion:
/assign 

Resolving patch:
diff --git a/python/kfserving/kfserving/api/kf_serving_client.py b/python/kfserving/kfserving/api/kf_serving_client.py
--- a/python/kfserving/kfserving/api/kf_serving_client.py
+++ b/python/kfserving/kfserving/api/kf_serving_client.py
@@ -325,3 +325,49 @@ def delete(self, name, namespace=None):
             raise RuntimeError(
                 "Exception when calling CustomObjectsApi->delete_namespaced_custom_object:\
                  %s\n" % e)
+
+
+    def is_isvc_ready(self, name, namespace=None): #pylint:disable=inconsistent-return-statements
+        """
+        Check if the inference service is ready.
+        :param name: inference service name
+        :param namespace: defaults to current or default namespace
+        :return:
+        """
+        kfsvc_status = self.get(name, namespace=namespace)
+        status = 'Unknown'
+        for condition in kfsvc_status['status'].get('conditions', {}):
+            if condition.get('type', '') == 'Ready':
+                status = condition.get('status', 'Unknown')
+                return status.lower() == "true"
+        return False
+
+
+    def wait_isvc_ready(self, name, namespace=None, #pylint:disable=too-many-arguments
+                        watch=False,
+                        timeout_seconds=600,
+                        polling_interval=10):
+        """
+        Waiting for inference service ready, print out the inference service if timeout.
+        :param name: inference service name
+        :param namespace: defaults to current or default namespace
+        :param watch: True to watch the service until timeout elapsed or status is ready
+        :param timeout_seconds: timeout seconds for waiting, default to 600s.
+               Print out the InferenceService if timeout.
+        :param polling_interval: The time interval to poll status
+        :return:
+        """
+        if watch:
+            isvc_watch(
+                name=name,
+                namespace=namespace,
+                timeout_seconds=timeout_seconds)
+        else:
+            for _ in range(round(timeout_seconds/polling_interval)):
+                time.sleep(polling_interval)
+                if self.is_isvc_ready(name, namespace=namespace):
+                    return
+
+            current_isvc = self.get(name, namespace=namespace)
+            raise RuntimeError("Timeout to start the InferenceService {}. \
+                               The InferenceService is as following: {}".format(name, current_isvc))
diff --git a/python/kfserving/kfserving/constants/constants.py b/python/kfserving/kfserving/constants/constants.py
--- a/python/kfserving/kfserving/constants/constants.py
+++ b/python/kfserving/kfserving/constants/constants.py
@@ -15,10 +15,10 @@
 import os
 
 # KFServing K8S constants
-KFSERVING_GROUP = "serving.kubeflow.org"
-KFSERVING_KIND = "InferenceService"
-KFSERVING_PLURAL = "inferenceservices"
-KFSERVING_VERSION = "v1alpha2"
+KFSERVING_GROUP = 'serving.kubeflow.org'
+KFSERVING_KIND = 'InferenceService'
+KFSERVING_PLURAL = 'inferenceservices'
+KFSERVING_VERSION = os.environ.get('KFSERVING_VERSION', 'v1alpha2')
 
 KFSERVING_LOGLEVEL = os.environ.get('KFSERVING_LOGLEVEL', 'INFO').upper()
 


Test patch:
diff --git a/test/e2e/common/utils.py b/test/e2e/common/utils.py
--- a/test/e2e/common/utils.py
+++ b/test/e2e/common/utils.py
@@ -26,42 +26,6 @@
 KFSERVING_TEST_NAMESPACE = "kfserving-ci-e2e-test"
 
 
-def wait_for_isvc_ready(name, namespace=KFSERVING_TEST_NAMESPACE,
-                        timeout_seconds=600, debug=True):
-    for _ in range(round(timeout_seconds/10)):
-        time.sleep(10)
-        kfsvc_status = KFServing.get(name, namespace=namespace)
-        status = 'Unknown'
-        for condition in kfsvc_status['status'].get('conditions', {}):
-            if condition.get('type', '') == 'Ready':
-                status = condition.get('status', 'Unknown')
-                if status == 'True':
-                    return
-    if debug is True:
-        logging.warning("Timeout to start the InferenceService %s.", name)
-        logging.info("Getting the detailed InferenceService ...")
-        logging.info(KFServing.get(name, namespace=namespace))
-        get_pod_log(pod='kfserving-controller-manager-0',
-                    namespace=KFSERVING_NAMESPACE,
-                    container='manager')
-    raise RuntimeError("Timeout to start the InferenceService %s. See above log for details.", name)
-
-
-def get_pod_log(pod, namespace=KFSERVING_NAMESPACE, container=''):
-    """
-    Note the arg container must be '' here, instead of None.
-    Otherwise API read_namespaced_pod_log will fail if no specified container.
-    """
-    logging.info("Getting logs of Pod %s ... ", pod)
-    try:
-        config.load_kube_config()
-        core_api = client.CoreV1Api()
-        pod_logs = core_api.read_namespaced_pod_log(pod, namespace, container=container)
-        logging.info("The logs of Pod %s log:\n %s", pod, pod_logs)
-    except client.rest.ApiException as e:
-        logging.info("Exception when calling CoreV1Api->read_namespaced_pod_log: %s\n", e)
-
-
 def predict(service_name, input_json):
     isvc = KFServing.get(service_name, namespace=KFSERVING_TEST_NAMESPACE)
     api_instance = client.CoreV1Api(client.ApiClient())
diff --git a/test/e2e/predictor/test_sklearn.py b/test/e2e/predictor/test_sklearn.py
--- a/test/e2e/predictor/test_sklearn.py
+++ b/test/e2e/predictor/test_sklearn.py
@@ -23,7 +23,7 @@
 from kfserving import V1alpha2InferenceService
 from kubernetes.client import V1ResourceRequirements
 
-from ..common.utils import wait_for_isvc_ready, predict
+from ..common.utils import predict
 from ..common.utils import KFSERVING_TEST_NAMESPACE
 
 api_version = constants.KFSERVING_GROUP + '/' + constants.KFSERVING_VERSION
@@ -48,7 +48,7 @@ def test_sklearn_kfserving():
                                     spec=V1alpha2InferenceServiceSpec(default=default_endpoint_spec))
 
     KFServing.create(isvc)
-    wait_for_isvc_ready(service_name)
+    KFServing.wait_isvc_ready(service_name, namespace=KFSERVING_TEST_NAMESPACE)
     probs = predict(service_name, './data/iris_input.json')
     assert(probs == [1, 1])
     KFServing.delete(service_name, KFSERVING_TEST_NAMESPACE)
diff --git a/test/e2e/predictor/test_tensorflow.py b/test/e2e/predictor/test_tensorflow.py
--- a/test/e2e/predictor/test_tensorflow.py
+++ b/test/e2e/predictor/test_tensorflow.py
@@ -24,7 +24,7 @@
 from kfserving import V1alpha2InferenceService
 from kubernetes.client import V1ResourceRequirements
 
-from ..common.utils import wait_for_isvc_ready, predict
+from ..common.utils import predict
 from ..common.utils import KFSERVING_TEST_NAMESPACE
 
 api_version = constants.KFSERVING_GROUP + '/' + constants.KFSERVING_VERSION
@@ -49,5 +49,4 @@ def test_tensorflow_kfserving():
                                     spec=V1alpha2InferenceServiceSpec(default=default_endpoint_spec))
 
     KFServing.create(isvc)
-    wait_for_isvc_ready(service_name)
-
+    KFServing.wait_isvc_ready(service_name, namespace=KFSERVING_TEST_NAMESPACE)
diff --git a/test/e2e/predictor/test_xgboost.py b/test/e2e/predictor/test_xgboost.py
--- a/test/e2e/predictor/test_xgboost.py
+++ b/test/e2e/predictor/test_xgboost.py
@@ -23,7 +23,7 @@
 from kfserving import V1alpha2InferenceService
 from kubernetes.client import V1ResourceRequirements
 
-from ..common.utils import wait_for_isvc_ready, predict
+from ..common.utils import predict
 from ..common.utils import KFSERVING_TEST_NAMESPACE
 
 api_version = constants.KFSERVING_GROUP + '/' + constants.KFSERVING_VERSION
@@ -48,7 +48,7 @@ def test_sklearn_kfserving():
                                     spec=V1alpha2InferenceServiceSpec(default=default_endpoint_spec))
 
     KFServing.create(isvc)
-    wait_for_isvc_ready(service_name)
+    KFServing.wait_isvc_ready(service_name, namespace=KFSERVING_TEST_NAMESPACE)
     probs = predict(service_name, './data/iris_input.json')
     assert(probs == [1, 1])
     KFServing.delete(service_name, KFSERVING_TEST_NAMESPACE)
diff --git a/test/e2e/transformer/test_transformer.py b/test/e2e/transformer/test_transformer.py
--- a/test/e2e/transformer/test_transformer.py
+++ b/test/e2e/transformer/test_transformer.py
@@ -26,7 +26,7 @@
 from kfserving import V1alpha2InferenceService
 from kubernetes.client import V1ResourceRequirements
 from kubernetes.client import V1Container
-from ..common.utils import wait_for_isvc_ready, predict
+from ..common.utils import predict
 from ..common.utils import KFSERVING_TEST_NAMESPACE
 
 api_version = constants.KFSERVING_GROUP + '/' + constants.KFSERVING_VERSION
@@ -61,7 +61,7 @@ def test_transformer():
                                     spec=V1alpha2InferenceServiceSpec(default=default_endpoint_spec))
 
     KFServing.create(isvc)
-    wait_for_isvc_ready(service_name)
+    KFServing.wait_isvc_ready(service_name, namespace=KFSERVING_TEST_NAMESPACE)
     probs = predict(service_name, './data/transformer.json')
     assert(np.argmax(probs) == 3)
     KFServing.delete(service_name, KFSERVING_TEST_NAMESPACE)