Skip to content

Commit

Permalink
adding repalce and promote APIs for kfserving sdk (kubeflow#357)
Browse files Browse the repository at this point in the history
* adding replace api for kfserving sdk

* rollback replace api
  • Loading branch information
jinchihe authored and k8s-ci-robot committed Sep 26, 2019
1 parent c2da53a commit 1eb093b
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 10 deletions.
10 changes: 1 addition & 9 deletions docs/samples/client/kfserving_sdk_sample.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -171,15 +171,7 @@
"metadata": {},
"outputs": [],
"source": [
"kfsvc = V1alpha2KFService(api_version=api_version,\n",
" kind=constants.KFSERVING_KIND,\n",
" metadata=client.V1ObjectMeta(\n",
" name='flower-sample', namespace='kubeflow'),\n",
" spec=V1alpha2KFServiceSpec(default=canary_endpoint_spec,\n",
" canary=None,\n",
" canary_traffic_percent=0))\n",
"\n",
"KFServing.patch('flower-sample', kfsvc)"
"KFServing.promote('flower-sample', namespace='kubeflow')"
]
},
{
Expand Down
86 changes: 85 additions & 1 deletion python/kfserving/docs/KFServingClient.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ KFServingClient | [set_credentials](#set_credentials) | Set Credentials|
KFServingClient | [create](#create) | Create KFService|
KFServingClient | [get](#get) | Get or watch the specified KFService or all KFServices in the namespace |
KFServingClient | [patch](#patch) | Patch the specified KFService|
KFServingClient | [replace](#replace) | Replace the specified KFService|
KFServingClient | [promote](#promote) | Promote the `canary` version of the KFService to `default`|
KFServingClient | [delete](#delete) | Delete the specified KFService |

## set_credentials
Expand Down Expand Up @@ -177,7 +179,9 @@ object
## patch
> patch(name, kfservice, namespace=None, watch=False, timeout_seconds=600)
Patch the created KFService in the specified namespace
Patch the created KFService in the specified namespace.

Note that if you want to set the field from existing value to `None`, `patch` API may not work, you need to use [replace](#replace) API to remove the field value.

### Example

Expand Down Expand Up @@ -221,6 +225,86 @@ timeout_seconds | int | Timeout seconds for watching. Defaults to 600. | Optiona
### Return type
object

## replace
> replace(name, kfservice, namespace=None, watch=False, timeout_seconds=600)
Replace the created KFService in the specified namespace. Generally use the `replace` API to update whole KFService or remove a field such as canary or other components of the KFService.

### Example

```python
from kubernetes import client
from kfserving import constants
from kfserving import V1alpha2EndpointSpec
from kfserving import V1alpha2PredictorSpec
from kfserving import V1alpha2TensorflowSpec
from kfserving import V1alpha2KFServiceSpec
from kfserving import V1alpha2KFService
from kfserving import KFServingClient

default_endpoint_spec = V1alpha2EndpointSpec(
predictor=V1alpha2PredictorSpec(
tensorflow=V1alpha2TensorflowSpec(
storage_uri='gs://kfserving-samples/models/tensorflow/flowers',
resources=None)))

kfsvc = V1alpha2KFService(api_version=api_version,
kind=constants.KFSERVING_KIND,
metadata=client.V1ObjectMeta(
name='flower-sample',
namespace='kubeflow',
resource_version=resource_version),
spec=V1alpha2KFServiceSpec(default=default_endpoint_spec,
canary=None,
canary_traffic_percent=0))


KFServing = KFServingClient()
KFServing.replace('flower-sample', kfsvc)

# The API also supports watching the replaced KFService status till it's READY.
# KFServing.replace('flower-sample', kfsvc, watch=True)
```

### Parameters
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
kfservice | [V1alpha2KFService](V1alpha2KFService.md) | KFService defination| Required |
namespace | str | The KFService's namespace. If the `namespace` is not defined, will align with KFService definition, or use current or default namespace if namespace is not specified in KFService definition. | Optional|
watch | bool | Watch the patched KFService if `True`, otherwise will return the replaced KFService object. Stop watching if KFService reaches the optional specified `timeout_seconds` or once the KFService overall status `READY` is `True`. | Optional |
timeout_seconds | int | Timeout seconds for watching. Defaults to 600. | Optional |

### Return type
object


## promote
> promote(name, namespace=None, watch=False, timeout_seconds=600)
Promote the `Canary KFServiceSpec` to `Default KFServiceSpec` for the created KFService in the specified namespace.

### Example

```python

KFServing = KFServingClient()
KFServing.promote('flower-sample', namespace='kubeflow')

# The API also supports watching the promoted KFService status till it's READY.
# KFServing.promote('flower-sample', namespace='kubeflow', watch=True)
```

### Parameters
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
Name | str | The KFService name for promoting.| |
namespace | str | The KFService's namespace. If the `namespace` is not defined, will align with KFService definition, or use current or default namespace if namespace is not specified in KFService definition. | Optional|
watch | bool | Watch the promoted KFService if `True`, otherwise will return the promoted KFService object. Stop watching if KFService reaches the optional specified `timeout_seconds` or once the KFService overall status `READY` is `True`. | Optional |
timeout_seconds | int | Timeout seconds for watching. Defaults to 600. | Optional |

### Return type
object


## delete
> delete(name, namespace=None)
Expand Down
64 changes: 64 additions & 0 deletions python/kfserving/kfserving/api/kf_serving_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from ..utils import utils
from .creds_utils import set_gcs_credentials, set_s3_credentials, set_azure_credentials
from .kf_serving_watch import watch as kfsvc_watch
from ..models.v1alpha2_kf_service import V1alpha2KFService
from ..models.v1alpha2_kf_service_spec import V1alpha2KFServiceSpec


class KFServingClient(object):
Expand Down Expand Up @@ -167,6 +169,68 @@ def patch(self, name, kfservice, namespace=None, watch=False, timeout_seconds=60
else:
return outputs

def replace(self, name, kfservice, namespace=None, watch=False, timeout_seconds=600): # pylint:disable=too-many-arguments,inconsistent-return-statements
"""Replace the created KFService in the specified namespace"""

if namespace is None:
namespace = utils.set_kfsvc_namespace(kfservice)

if kfservice.metadata.resource_version is None:
current_kfsvc = self.get(name, namespace=namespace)
current_resource_version = current_kfsvc['metadata']['resourceVersion']
kfservice.metadata.resource_version = current_resource_version

try:
outputs = self.api_instance.replace_namespaced_custom_object(
constants.KFSERVING_GROUP,
constants.KFSERVING_VERSION,
namespace,
constants.KFSERVING_PLURAL,
name,
kfservice)
except client.rest.ApiException as e:
raise RuntimeError(
"Exception when calling CustomObjectsApi->replace_namespaced_custom_object:\
%s\n" % e)

if watch:
kfsvc_watch(
name=outputs['metadata']['name'],
namespace=namespace,
timeout_seconds=timeout_seconds)
else:
return outputs


def promote(self, name, namespace=None, watch=False, timeout_seconds=600): # pylint:disable=too-many-arguments,inconsistent-return-statements
"""Promote the created KFService in the specified namespace"""

if namespace is None:
namespace = utils.get_default_target_namespace()

current_kfsvc = self.get(name, namespace=namespace)
api_version = current_kfsvc['apiVersion']

try:
current_canary_spec = current_kfsvc['spec']['canary']
except KeyError:
raise RuntimeError("Cannot promote a KFService that has no Canary Spec.")

kfservice = V1alpha2KFService(
api_version=api_version,
kind=constants.KFSERVING_KIND,
metadata=client.V1ObjectMeta(
name=name,
namespace=namespace),
spec=V1alpha2KFServiceSpec(
default=current_canary_spec,
canary=None,
canary_traffic_percent=0))

return self.replace(name=name, kfservice=kfservice, namespace=namespace,
watch=watch, timeout_seconds=timeout_seconds)


def delete(self, name, namespace=None):
"""Delete the provided KFService in the specified namespace"""

Expand Down
12 changes: 12 additions & 0 deletions python/kfserving/test/test_kfservice_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ def test_kfservice_client_patch():
kfsvc = generate_kfservice()
assert mocked_unit_result == KFServing.patch('flower-sample', kfsvc, namespace='kubeflow')

def test_kfservice_client_promote():
'''Unit test for kfserving promote api'''
with patch('kfserving.api.kf_serving_client.KFServingClient.promote',
return_value=mocked_unit_result):
assert mocked_unit_result == KFServing.promote('flower-sample', namespace='kubeflow')

def test_kfservice_client_replace():
'''Unit test for kfserving replace api'''
with patch('kfserving.api.kf_serving_client.KFServingClient.replace',
return_value=mocked_unit_result):
kfsvc = generate_kfservice()
assert mocked_unit_result == KFServing.replace('flower-sample', kfsvc, namespace='kubeflow')

def test_kfservice_client_delete():
'''Unit test for kfserving delete api'''
Expand Down

0 comments on commit 1eb093b

Please sign in to comment.