Repository: bentoml/BentoML

Issue:
Labels are not saved in YataiService repository
**Describe the bug**
<!--- A clear and concise description of what the bug is. -->
I found a bug that labels are not saved in YataiService after pushing my bento service.

**To Reproduce**
<!--
Steps to reproduce the issue:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
-->
- [click here to see the example below](https://docs.bentoml.org/en/latest/quickstart.html)

```python
from sklearn import svm
from sklearn import datasets

# Load training data
iris = datasets.load_iris()
X, y = iris.data, iris.target

# Model Training
clf = svm.SVC(gamma='scale')
clf.fit(X, y)
```

```python
%%writefile iris_classifier.py
import pandas as pd

from bentoml import env, artifacts, api, BentoService
from bentoml.adapters import DataframeInput
from bentoml.frameworks.sklearn import SklearnModelArtifact

@env(infer_pip_packages=True)
@artifacts([SklearnModelArtifact('model')])
class IrisClassifier(BentoService):
    """
    A minimum prediction service exposing a Scikit-learn model
    """

    @api(input=DataframeInput(), batch=True)
    def predict(self, df: pd.DataFrame):
        """
        An inference API named `predict` with Dataframe input adapter, which codifies
        how HTTP requests or CSV files are converted to a pandas Dataframe object as the
        inference API function input
        """
        return self.artifacts.model.predict(df)
```

```python
from iris_classifier import IrisClassifier

iris_classifier_service = IrisClassifier()
iris_classifier_service.pack('model', clf)

# Save my bento service with labels
iris_classifier_service.save(labels = {"model_name":"IrisClassifier"})

yatai_service_endpoint = <YOUR_YATAI_SERVICE_ENDPOINT>
bento_bundle_name = f"{iris_classifier_service.name}:{iris_classifier_service.version}"
```
```
# See the labels are exist.
!bentoml list

BENTO_SERVICE                         AGE                    APIS                                   ARTIFACTS                    LABELS
IrisClassifier:20210422163910_617F62  1.49 seconds           predict<DataframeInput:DefaultOutput>  model<SklearnModelArtifact>  model_name:IrisClassifier
```

```python
!bentoml push --yatai-url $yatai_service_endpoint $bento_bundle_name
```

```
# After pushing my bento service to YataiService repo, we can see that the labels are not exist. 
!bentoml list --yatai-url $yatai_service_endpoint

BENTO_SERVICE                         AGE                    APIS                                   ARTIFACTS                    LABELS
IrisClassifier:20210422163910_617F62  5.51 seconds           predict<DataframeInput:DefaultOutput>  model<SklearnModelArtifact>
```

**Expected behavior**
<!--- A clear and concise description of what you expected to happen. -->
I should be able to see the labels in the YataiService repository.

**Screenshots/Logs**
<!--- 
If applicable, add screenshots, logs or error outputs to help explain your problem.

To give us more information for diagnosing the issue, make sure to enable debug logging:

Enable via environment variable, e.g.:
```
$ git clone git@github.com:bentoml/BentoML.git && cd bentoml
$ BENTOML_DEBUG=TRUE python guides/quick-start/main.py
```

Or set debug logging in your Python code:
```python
from bentoml.configuration import set_debug_mode
set_debug_mode(True)
```

For BentoML CLI commands, simply add the `--verbose` flag, e.g.:
```bash
bentoml get IrisClassifier --verbose


```

-->
<img width="618" alt="bentoml_labels_bug" src="https://user-images.githubusercontent.com/36870121/115674494-ab50a180-a388-11eb-9851-173ce2319f27.png">

**Environment:**
 - OS: centOS 7
 - Python Version: 3.6.13
 - BentoML Version: 0.11.0


**Additional context**
<!-- Add any other context about the problem here. e.g. links to related discussion. -->



Discussion:
@chlee1016 Thank you for reporting, I will look into it.   Can you try with the latest BentoML version, `0.12.1` and see?
@yubozhao Okay, I will give it a try and let you know that. Thank you.
**Same test with BentoML version `0.12.1`**

Save my bento service with labels.
```
!bentoml list

BENTO_SERVICE                         AGE          APIS                                   ARTIFACTS                    LABELS
IrisClassifier:20210423092848_4A13BB  3.8 seconds  predict<DataframeInput:DefaultOutput>  model<SklearnModelArtifact>  model_name:IrisClassifier
```
```
!bentoml push --yatai-url $yatai_service_endpoint $bento_bundle_name
```
After bentoml push, I got the same result.
```
!bentoml list --yatai-url $yatai_service_endpoint

BENTO_SERVICE                         AGE                      APIS                                   ARTIFACTS                    LABELS
IrisClassifier:20210423092848_4A13BB  10.41 seconds            predict<DataframeInput:DefaultOutput>  model<SklearnModelArtifact>
```

**Screenshot**
<img width="753" alt="bentoml_bug_2" src="https://user-images.githubusercontent.com/36870121/115802249-822c2180-a419-11eb-868b-540f3323db19.png">


@chlee1016 I see. 

Right now, the python API expose `label` option, for the use cases that you might have different labels you want to add. This option is not exposed to the CLI.  We will match the behavior for them.

I will also add the current labels as default when no new labels are provided.
I will keep you updated, hopefully we can get this resolved before next release
@yubozhao Thank you for considering this issue. 

As I understand, the `labels` option in the CLI will be updated in the future and if there are no new labels in the CLI, current labels set via python API will be used as default. Therefore I will be able to see the `labels` in remote YataiService repo. Am I right?

Resolving patch:
diff --git a/bentoml/cli/bento_management.py b/bentoml/cli/bento_management.py
--- a/bentoml/cli/bento_management.py
+++ b/bentoml/cli/bento_management.py
@@ -296,12 +296,19 @@ def pull(bento, yatai_url):
         required=True,
         help='Remote YataiService URL. Example: "--yatai-url http://localhost:50050"',
     )
-    def push(bento, yatai_url):
+    @click.option(
+        '--with-labels/--without-labels',
+        default=True,
+        help="Retain bento bundle's labels or not for push Bento bundle to remote "
+        "yatai. When running with --without-labels, labels are not retained in the "
+        "remote yatai server",
+    )
+    def push(bento, yatai_url, with_labels):
         if ':' not in bento:
             _echo(f'BentoService {bento} invalid - specify name:version')
             return
         yc = get_yatai_client(yatai_url)
-        yc.repository.push(bento=bento)
+        yc.repository.push(bento, with_labels)
         _echo(f'Pushed {bento} to {yatai_url}')
 
     @cli.command(help='Retrieve')
diff --git a/bentoml/yatai/client/bento_repository_api.py b/bentoml/yatai/client/bento_repository_api.py
--- a/bentoml/yatai/client/bento_repository_api.py
+++ b/bentoml/yatai/client/bento_repository_api.py
@@ -64,13 +64,12 @@ def __init__(self, yatai_service):
         # YataiService stub for accessing remote YataiService RPCs
         self.yatai_service = yatai_service
 
-    def push(self, bento, labels=None):
+    def push(self, bento, with_labels=True):
         """
         Push a local BentoService to a remote yatai server.
 
         Args:
             bento: a BentoService identifier in the format of NAME:VERSION
-            labels: optional. List of labels for the BentoService.
 
         Returns:
             BentoService saved path
@@ -97,6 +96,12 @@ def push(self, bento, labels=None):
             bento_bundle_path = local_bento_pb.uri.gcs_presigned_url
         else:
             bento_bundle_path = local_bento_pb.uri.uri
+        labels = (
+            dict(local_bento_pb.bento_service_metadata.labels)
+            if with_labels is True and local_bento_pb.bento_service_metadata.labels
+            else None
+        )
+
         return self.upload_from_dir(bento_bundle_path, labels=labels)
 
     def pull(self, bento):
@@ -125,8 +130,16 @@ def pull(self, bento):
 
             from bentoml.yatai.client import get_yatai_client
 
+            labels = (
+                dict(bento_pb.bento_service_metadata.labels)
+                if bento_pb.bento_service_metadata.labels
+                else None
+            )
+
             local_yc = get_yatai_client()
-            return local_yc.repository.upload_from_dir(target_bundle_path)
+            return local_yc.repository.upload_from_dir(
+                target_bundle_path, labels=labels
+            )
 
     def upload(self, bento_service, version=None, labels=None):
         """Save and upload given bento_service to yatai_service, which manages all your


Test patch:
diff --git a/tests/yatai/client/test_bento_repository_api.py b/tests/yatai/client/test_bento_repository_api.py
--- a/tests/yatai/client/test_bento_repository_api.py
+++ b/tests/yatai/client/test_bento_repository_api.py
@@ -174,6 +174,59 @@ def test_pull(yatai_server_container, example_bento_service_class):
     assert pulled_local_path != saved_path
 
 
+@pytest.mark.skipif(
+    sys.platform == "darwin",
+    reason="Requires docker, skipping test for Mac OS on Github Action",
+)
+@pytest.mark.skipif('not psutil.POSIX')
+def test_push_with_labels(yatai_server_container, example_bento_service_class):
+    example_bento_service_class = bentoml.ver(major=2, minor=2)(
+        example_bento_service_class
+    )
+
+    yc = get_yatai_client(yatai_server_container)
+    test_model = TestModel()
+    svc = example_bento_service_class()
+    svc.pack('model', test_model)
+    saved_path = svc.save(labels={'foo': 'bar', 'abc': '123'})
+
+    pushed_path = yc.repository.push(f'{svc.name}:{svc.version}')
+    assert pushed_path != saved_path
+    remote_bento_pb = yc.repository.get(f'{svc.name}:{svc.version}')
+    assert remote_bento_pb.bento_service_metadata.labels
+    labels = dict(remote_bento_pb.bento_service_metadata.labels)
+    assert labels['foo'] == 'bar'
+    assert labels['abc'] == '123'
+
+
+@pytest.mark.skipif(
+    sys.platform == "darwin",
+    reason="Requires docker, skipping test for Mac OS on Github Action",
+)
+@pytest.mark.skipif('not psutil.POSIX')
+def test_pull_with_labels(yatai_server_container, example_bento_service_class):
+    example_bento_service_class = bentoml.ver(major=2, minor=3)(
+        example_bento_service_class
+    )
+
+    yc = get_yatai_client(yatai_server_container)
+    test_model = TestModel()
+    svc = example_bento_service_class()
+    svc.pack('model', test_model)
+    saved_path = svc.save(
+        yatai_url=yatai_server_container, labels={'foo': 'bar', 'abc': '123'}
+    )
+
+    pulled_local_path = yc.repository.pull(f'{svc.name}:{svc.version}')
+    assert pulled_local_path != saved_path
+    local_yc = get_yatai_client()
+    local_bento_pb = local_yc.repository.get(f'{svc.name}:{svc.version}')
+    assert local_bento_pb.bento_service_metadata.labels
+    labels = dict(local_bento_pb.bento_service_metadata.labels)
+    assert labels['foo'] == 'bar'
+    assert labels['abc'] == '123'
+
+
 @pytest.mark.skipif(
     sys.platform == "darwin",
     reason="Requires docker, skipping test for Mac OS for Github Action",
@@ -210,7 +263,7 @@ def test_list(yatai_server_container, example_bento_service_class):
     svc.save(yatai_url=yatai_server_container)
 
     bentos = yc.repository.list(bento_name=svc.name)
-    assert len(bentos) == 5
+    assert len(bentos) == 7
 
 
 @pytest.mark.skipif(