2121# See the License for the specific language governing permissions and
2222# limitations under the License.
2323
24- from json import loads as json_loads
2524import os
25+ import uuid
26+ import json
27+ import string
28+
29+ try :
30+ from urlparse import urlparse , urljoin
31+ except ImportError :
32+ from urllib .parse import urlparse , urljoin
2633
2734import click
2835
2936from databricks_cli .click_types import PipelineSpecClickType , PipelineIdClickType
30- from databricks_cli .utils import eat_exceptions , CONTEXT_SETTINGS , pretty_format
3137from databricks_cli .version import print_version_callback , version
3238from databricks_cli .pipelines .api import PipelinesApi
3339from databricks_cli .configure .config import provide_api_client , profile_option , debug_option
40+ from databricks_cli .utils import pipelines_exception_eater , CONTEXT_SETTINGS , pretty_format , \
41+ error_and_quit
42+
43+ try :
44+ json_parse_exception = json .decoder .JSONDecodeError
45+ except AttributeError : # Python 2
46+ json_parse_exception = ValueError
47+
48+ PIPELINE_ID_PERMITTED_CHARACTERS = set (string .ascii_letters + string .digits + '-_' )
3449
3550
3651@click .command (context_settings = CONTEXT_SETTINGS ,
3954@click .option ('--spec' , default = None , type = PipelineSpecClickType (), help = PipelineSpecClickType .help )
4055@debug_option
4156@profile_option
42- @eat_exceptions
57+ @pipelines_exception_eater
4358@provide_api_client
4459def deploy_cli (api_client , spec_arg , spec ):
4560 """
@@ -59,8 +74,19 @@ def deploy_cli(api_client, spec_arg, spec):
5974 raise RuntimeError ('The spec should be provided either by an option or argument' )
6075 src = spec_arg if bool (spec_arg ) else spec
6176 spec_obj = _read_spec (src )
77+ if 'id' not in spec_obj :
78+ pipeline_id = str (uuid .uuid4 ())
79+ click .echo ("Updating spec at {} with id: {}" .format (src , pipeline_id ))
80+ spec_obj ['id' ] = pipeline_id
81+ _write_spec (src , spec_obj )
82+ _validate_pipeline_id (spec_obj ['id' ])
6283 PipelinesApi (api_client ).deploy (spec_obj )
6384
85+ pipeline_id = spec_obj ['id' ]
86+ base_url = "{0.scheme}://{0.netloc}/" .format (urlparse (api_client .url ))
87+ pipeline_url = urljoin (base_url , "#joblist/pipelines/{}" .format (pipeline_id ))
88+ click .echo ("Pipeline successfully deployed: {}" .format (pipeline_url ))
89+
6490
6591@click .command (context_settings = CONTEXT_SETTINGS ,
6692 short_help = 'Stops a delta pipeline and deletes its associated Databricks resources' )
@@ -70,7 +96,7 @@ def deploy_cli(api_client, spec_arg, spec):
7096 help = PipelineIdClickType .help )
7197@debug_option
7298@profile_option
73- @eat_exceptions
99+ @pipelines_exception_eater
74100@provide_api_client
75101def delete_cli (api_client , spec_arg , spec , pipeline_id ):
76102 """
@@ -91,6 +117,7 @@ def delete_cli(api_client, spec_arg, spec, pipeline_id):
91117 """
92118 pipeline_id = _get_pipeline_id (spec_arg = spec_arg , spec = spec , pipeline_id = pipeline_id )
93119 PipelinesApi (api_client ).delete (pipeline_id )
120+ click .echo ("Pipeline {} deleted" .format (pipeline_id ))
94121
95122
96123@click .command (context_settings = CONTEXT_SETTINGS ,
@@ -101,7 +128,7 @@ def delete_cli(api_client, spec_arg, spec, pipeline_id):
101128 help = PipelineIdClickType .help )
102129@debug_option
103130@profile_option
104- @eat_exceptions
131+ @pipelines_exception_eater
105132@provide_api_client
106133def get_cli (api_client , spec_arg , spec , pipeline_id ):
107134 """
@@ -131,7 +158,7 @@ def get_cli(api_client, spec_arg, spec, pipeline_id):
131158 help = PipelineIdClickType .help )
132159@debug_option
133160@profile_option
134- @eat_exceptions
161+ @pipelines_exception_eater
135162@provide_api_client
136163def reset_cli (api_client , spec_arg , spec , pipeline_id ):
137164 """
@@ -152,6 +179,7 @@ def reset_cli(api_client, spec_arg, spec, pipeline_id):
152179 """
153180 pipeline_id = _get_pipeline_id (spec_arg = spec_arg , spec = spec , pipeline_id = pipeline_id )
154181 PipelinesApi (api_client ).reset (pipeline_id )
182+ click .echo ("Reset triggered for pipeline {}" .format (pipeline_id ))
155183
156184
157185def _read_spec (src ):
@@ -161,13 +189,25 @@ def _read_spec(src):
161189 """
162190 extension = os .path .splitext (src )[1 ]
163191 if extension .lower () == '.json' :
164- with open (src , 'r' ) as f :
165- json = f .read ()
166- return json_loads (json )
192+ try :
193+ with open (src , 'r' ) as f :
194+ data = f .read ()
195+ return json .loads (data )
196+ except json_parse_exception as e :
197+ error_and_quit ("Invalid JSON provided in spec\n {}" .format (e ))
167198 else :
168199 raise RuntimeError ('The provided file extension for the spec is not supported' )
169200
170201
202+ def _write_spec (src , spec ):
203+ """
204+ Writes the spec at src as JSON.
205+ """
206+ data = json .dumps (spec , indent = 2 ) + '\n '
207+ with open (src , 'w' ) as f :
208+ f .write (data )
209+
210+
171211def _get_pipeline_id (spec_arg , spec , pipeline_id ):
172212 """
173213 Ensures that the user has either specified a spec (either through argument or option) or a
@@ -180,9 +220,22 @@ def _get_pipeline_id(spec_arg, spec, pipeline_id):
180220 if bool (spec_arg ) or bool (spec ):
181221 src = spec_arg if bool (spec_arg ) else spec
182222 pipeline_id = _read_spec (src )["id" ]
223+ _validate_pipeline_id (pipeline_id )
183224 return pipeline_id
184225
185226
227+ def _validate_pipeline_id (pipeline_id ):
228+ """
229+ Checks if the pipeline_id only contain -, _ and alphanumeric characters
230+ """
231+ if len (pipeline_id ) == 0 :
232+ error_and_quit (u'Empty pipeline id provided' )
233+ if not set (pipeline_id ) <= PIPELINE_ID_PERMITTED_CHARACTERS :
234+ message = u'Pipeline id {} has invalid character(s)\n ' .format (pipeline_id )
235+ message += u'Valid characters are: _ - a-z A-Z 0-9'
236+ error_and_quit (message )
237+
238+
186239@click .group (context_settings = CONTEXT_SETTINGS ,
187240 short_help = 'Utility to interact with the Databricks Delta Pipelines.' )
188241@click .option ('--version' , '-v' , is_flag = True , callback = print_version_callback ,
0 commit comments