Repository: kubeflow/pipelines

Issue:
[SDK] Unable to choose pipeline version in run_pipeline
Currently it does not look like there is a way to choose a pipeline version when running in the kfp SDK.

https://github.com/kubeflow/pipelines/blob/master/sdk/python/kfp/_client.py#L304

The UI allows you to do with a request like:

```json
{
  "description": "",
  "name": "Run of 15a3ef1 (a7a1d)",
  "pipeline_spec": {
    "parameters": []
  },
  "resource_references": [
    {
      "key": {
        "id": "2d5b7fa6-dff8-41b4-b0a3-66ba57d73124",
        "type": "PIPELINE_VERSION"
      },
      "relationship": "CREATOR"
    }
  ]
}
```

### Environment:
<!-- Please fill in those that seem relevant. -->

How did you deploy Kubeflow Pipelines (KFP)?
One click installer

KFP version: 0.2.5

KFP SDK version: 0.2.5


### Anything else you would like to add:

/kind bug
<!-- Please include labels by uncommenting them to help us better triage issues, choose from the following -->
<!--
// /area frontend
// /area backend
/area sdk
// /area testing
// /area engprod
-->



Discussion:
/assign @jingzhang36 
Hi @jingzhang36 I believe that it's in our plan, right?

Resolving patch:
diff --git a/sdk/python/kfp/_client.py b/sdk/python/kfp/_client.py
--- a/sdk/python/kfp/_client.py
+++ b/sdk/python/kfp/_client.py
@@ -377,7 +377,7 @@ def list_pipelines(self, page_token='', page_size=10, sort_by=''):
     return self._pipelines_api.list_pipelines(page_token=page_token, page_size=page_size, sort_by=sort_by)
 
   # TODO: provide default namespace, similar to kubectl default namespaces.
-  def run_pipeline(self, experiment_id, job_name, pipeline_package_path=None, params={}, pipeline_id=None):
+  def run_pipeline(self, experiment_id, job_name, pipeline_package_path=None, params={}, pipeline_id=None, version_id=None):
     """Run a specified pipeline.
 
     Args:
@@ -386,6 +386,9 @@ def run_pipeline(self, experiment_id, job_name, pipeline_package_path=None, para
       pipeline_package_path: local path of the pipeline package(the filename should end with one of the following .tar.gz, .tgz, .zip, .yaml, .yml).
       params: a dictionary with key (string) as param name and value (string) as as param value.
       pipeline_id: the string ID of a pipeline.
+      version_id: the string ID of a pipeline version. 
+        If both pipeline_id and version_id are specified, pipeline_id will take precendence
+        This will change in a future version, so it is recommended to use version_id by itself
 
     Returns:
       A run object. Most important field is id.
@@ -398,18 +401,26 @@ def run_pipeline(self, experiment_id, job_name, pipeline_package_path=None, para
     api_params = [kfp_server_api.ApiParameter(
         name=sanitize_k8s_name(name=k, allow_capital_underscore=True),
         value=str(v)) for k,v in params.items()]
-
+    resource_references = []
     key = kfp_server_api.models.ApiResourceKey(id=experiment_id,
                                         type=kfp_server_api.models.ApiResourceType.EXPERIMENT)
     reference = kfp_server_api.models.ApiResourceReference(key=key,
                                                            relationship=kfp_server_api.models.ApiRelationship.OWNER)
+    resource_references.append(reference)
+
+    if version_id:
+      key = kfp_server_api.models.ApiResourceKey(id=version_id,
+                                                 type=kfp_server_api.models.ApiResourceType.PIPELINE_VERSION)
+      reference = kfp_server_api.models.ApiResourceReference(key=key, 
+                                                             relationship=kfp_server_api.models.ApiRelationship.CREATOR)
+      resource_references.append(reference)
 
     spec = kfp_server_api.models.ApiPipelineSpec(
         pipeline_id=pipeline_id,
         workflow_manifest=pipeline_json_string,
         parameters=api_params)
     run_body = kfp_server_api.models.ApiRun(
-        pipeline_spec=spec, resource_references=[reference], name=job_name)
+        pipeline_spec=spec, resource_references=resource_references, name=job_name)
 
     response = self._run_api.create_run(body=run_body)
 
diff --git a/sdk/python/kfp/cli/run.py b/sdk/python/kfp/cli/run.py
--- a/sdk/python/kfp/cli/run.py
+++ b/sdk/python/kfp/cli/run.py
@@ -46,9 +46,10 @@ def list(ctx, experiment_id, max_size):
 @click.option('-f', '--package-file', type=click.Path(exists=True, dir_okay=False), help='Path of the pipeline package file.')
 @click.option('-p', '--pipeline-id', help='ID of the pipeline template.')
 @click.option('-w', '--watch', is_flag=True, default=False, help='Watch the run status until it finishes.')
+@click.option('-v', '--version', help='ID of the pipeline version.')
 @click.argument('args', nargs=-1)
 @click.pass_context
-def submit(ctx, experiment_name, run_name, package_file, pipeline_id, watch, args):
+def submit(ctx, experiment_name, run_name, package_file, pipeline_id, watch, version, args):
     """submit a KFP run"""
     client = ctx.obj['client']
     namespace = ctx.obj['namespace']
@@ -61,7 +62,7 @@ def submit(ctx, experiment_name, run_name, package_file, pipeline_id, watch, arg
 
     arg_dict = dict(arg.split('=') for arg in args)
     experiment = client.create_experiment(experiment_name)
-    run = client.run_pipeline(experiment.id, run_name, package_file, arg_dict, pipeline_id)
+    run = client.run_pipeline(experiment.id, run_name, package_file, arg_dict, pipeline_id, version_id=version)
     print('Run {} is submitted'.format(run.id))
     _display_run(client, namespace, run.id, watch)
 
@@ -100,4 +101,4 @@ def _display_run(client, namespace, run_id, watch):
 def _print_runs(runs):
     headers = ['run id', 'name', 'status', 'created at']
     data = [[run.id, run.name, run.status, run.created_at.isoformat()] for run in runs]
-    print(tabulate(data, headers=headers, tablefmt='grid'))
\ No newline at end of file
+    print(tabulate(data, headers=headers, tablefmt='grid'))


Test patch: