2
2
3
3
import datetime
4
4
import logging
5
+ import os
5
6
import uuid
7
+ import tempfile
6
8
import yaml
7
9
10
+ from google .cloud import storage
8
11
from kubernetes import client as k8s_client
9
12
from kubeflow .testing import argo_build_util
13
+ from kubeflow .testing import prow_artifacts
10
14
from kubeflow .testing import util
11
15
12
- def run_papermill_job (name , namespace , # pylint: disable=too-many-branches,too-many-statements
16
+ # This is the bucket where the batch jobs will uploaded an HTML version of the
17
+ # notebook will be written to. The K8s job is running in a Kubeflow cluster
18
+ # so it needs to be a bucket that the kubeflow cluster can write to.
19
+ # This is why we don't write directly to the bucket used for prow artifacts
20
+ NB_BUCKET = "kubeflow-ci-deployment"
21
+ PROJECT = "kbueflow-ci-deployment"
22
+
23
+ def run_papermill_job (notebook_path , name , namespace , # pylint: disable=too-many-branches,too-many-statements
13
24
repos , image ):
14
25
"""Generate a K8s job to run a notebook using papermill
15
26
16
27
Args:
28
+ notebook_path: Path to the notebook. This should be in the form
29
+ "{REPO_OWNER}/{REPO}/path/to/notebook.ipynb"
17
30
name: Name for the K8s job
18
31
namespace: The namespace where the job should run.
19
- repos: (Optional) Which repos to checkout; if not specified tries
32
+ repos: Which repos to checkout; if None or empty tries
20
33
to infer based on PROW environment variables
21
- image:
34
+ image: The docker image to run the notebook in.
22
35
"""
23
36
24
37
util .maybe_activate_service_account ()
25
38
26
39
with open ("job.yaml" ) as hf :
27
40
job = yaml .load (hf )
28
41
42
+ if notebook_path .startswith ("/" ):
43
+ raise ValueError ("notebook_path={0} should not start with /" .format (
44
+ notebook_path ))
45
+
29
46
# We need to checkout the correct version of the code
30
47
# in presubmits and postsubmits. We should check the environment variables
31
48
# for the prow environment variables to get the appropriate values.
@@ -35,23 +52,68 @@ def run_papermill_job(name, namespace, # pylint: disable=too-many-branches,too-m
35
52
if not repos :
36
53
repos = argo_build_util .get_repo_from_prow_env ()
37
54
55
+ if not repos :
56
+ raise ValueError ("Could not get repos from prow environment variable "
57
+ "and --repos isn't explicitly set" )
58
+
59
+ repos += ",kubeflow/testing@HEAD"
60
+
38
61
logging .info ("Repos set to %s" , repos )
39
62
job ["spec" ]["template" ]["spec" ]["initContainers" ][0 ]["command" ] = [
40
63
"/usr/local/bin/checkout_repos.sh" ,
41
64
"--repos=" + repos ,
42
65
"--src_dir=/src" ,
43
66
"--depth=all" ,
44
67
]
68
+
45
69
job ["spec" ]["template" ]["spec" ]["containers" ][0 ]["image" ] = image
70
+
71
+ full_notebook_path = os .path .join ("/src" , notebook_path )
72
+ job ["spec" ]["template" ]["spec" ]["containers" ][0 ]["command" ] = [
73
+ "python3" , "-m" ,
74
+ "kubeflow.examples.notebook_tests.execute_notebook" ,
75
+ "--notebook_path" , full_notebook_path ]
76
+
77
+ job ["spec" ]["template" ]["spec" ]["containers" ][0 ][
78
+ "workingDir" ] = os .path .dirname (full_notebook_path )
79
+
80
+ # The prow bucket to use for results/artifacts
81
+ prow_bucket = prow_artifacts .PROW_RESULTS_BUCKET
82
+
83
+ if os .getenv ("REPO_OWNER" ) and os .getenv ("REPO_NAME" ):
84
+ # Running under prow
85
+ prow_dir = prow_artifacts .get_gcs_dir (prow_bucket )
86
+ prow_dir = os .path .join (prow_dir , "artifacts" )
87
+
88
+ if os .getenv ("TEST_TARGET_NAME" ):
89
+ prow_dir = os .path .join (
90
+ prow_dir , os .getenv ("TEST_TARGET_NAME" ).lstrip ("/" ))
91
+ prow_bucket , prow_path = util .split_gcs_uri (prow_dir )
92
+
93
+ else :
94
+ prow_path = "notebook-test" + datetime .datetime .now ().strftime ("%H%M%S" )
95
+ prow_path = prow_path + "-" + uuid .uuid4 ().hex [0 :3 ]
96
+ prow_dir = util .to_gcs_uri (prow_bucket , prow_path )
97
+
98
+ prow_path = os .path .join (prow_path , name + ".html" )
99
+ output_gcs = util .to_gcs_uri (NB_BUCKET , prow_path )
100
+
101
+ job ["spec" ]["template" ]["spec" ]["containers" ][0 ]["env" ] = [
102
+ {"name" : "OUTPUT_GCS" , "value" : output_gcs },
103
+ {"name" : "PYTHONPATH" ,
104
+ "value" : "/src/kubeflow/testing/py:/src/kubeflow/examples/py" },
105
+ ]
106
+
107
+ logging .info ("Notebook will be written to %s" , output_gcs )
46
108
util .load_kube_config (persist_config = False )
47
109
48
110
if name :
49
111
job ["metadata" ]["name" ] = name
50
112
else :
51
- job ["metadata" ]["name" ] = ("xgboost -test-" +
113
+ job ["metadata" ]["name" ] = ("notebook -test-" +
52
114
datetime .datetime .now ().strftime ("%H%M%S" )
53
115
+ "-" + uuid .uuid4 ().hex [0 :3 ])
54
- name = job ["metadata" ]["name" ]
116
+ name = job ["metadata" ]["name" ]
55
117
56
118
job ["metadata" ]["namespace" ] = namespace
57
119
@@ -70,6 +132,16 @@ def run_papermill_job(name, namespace, # pylint: disable=too-many-branches,too-m
70
132
71
133
logging .info ("Final job:\n %s" , yaml .safe_dump (final_job .to_dict ()))
72
134
135
+ # Download notebook html to artifacts
136
+ logging .info ("Copying %s to bucket %s" , output_gcs , prow_bucket )
137
+
138
+ storage_client = storage .Client ()
139
+ bucket = storage_client .get_bucket (NB_BUCKET )
140
+ blob = bucket .get_blob (prow_path )
141
+
142
+ destination_bucket = storage_client .get_bucket (prow_bucket )
143
+ bucket .copy_blob (blob , destination_bucket )
144
+
73
145
if not final_job .status .conditions :
74
146
raise RuntimeError ("Job {0}.{1}; did not complete" .format (namespace , name ))
75
147
@@ -78,3 +150,4 @@ def run_papermill_job(name, namespace, # pylint: disable=too-many-branches,too-m
78
150
if last_condition .type not in ["Complete" ]:
79
151
logging .error ("Job didn't complete successfully" )
80
152
raise RuntimeError ("Job {0}.{1} failed" .format (namespace , name ))
153
+
0 commit comments