4242
4343import yaml
4444
45+ import validation_utils
46+
4547
4648# Exclude non-printable control characters (including newlines)
4749PRINTABLE_REGEX = re .compile (r"""^[^\x00-\x1f]*$""" )
5961 )
6062""" )
6163
62- # For easier development, we allow redefining builtins like
63- # --substitutions=PROJECT_ID=foo even though gcloud doesn't.
64- KEY_VALUE_REGEX = re .compile (r'^([A-Z_][A-Z0-9_]*)=(.*)$' )
65-
6664# Default builtin substitutions
6765DEFAULT_SUBSTITUTIONS = {
6866 'BRANCH_NAME' : '' ,
@@ -149,51 +147,6 @@ def sub(match):
149147 quoted_s = shlex .quote (substituted_s )
150148 return quoted_s
151149
152- def get_field_value (container , field_name , field_type ):
153- """Fetch a field from a container with typechecking and default values.
154-
155- The field value is coerced to the desired type. If the field is
156- not present, a instance of `field_type` is constructed with no
157- arguments and used as the default value.
158-
159- Args:
160- container (dict): Object decoded from yaml
161- field_name (str): Field that should be present in `container`
162- field_type (type): Expected type for field value
163-
164- Returns:
165- Any: Fetched or default value of field
166-
167- Raises:
168- ValueError: if field value cannot be converted to the desired type
169- """
170- try :
171- value = container [field_name ]
172- except (IndexError , KeyError ):
173- return field_type ()
174-
175- msg = 'Expected "{}" field to be of type "{}", but found type "{}"'
176- if not isinstance (value , field_type ):
177- # list('some string') is a successful type cast as far as Python
178- # is concerned, but doesn't exactly produce the results we want.
179- # We have a whitelist of conversions we will attempt.
180- whitelist = (
181- (float , str ),
182- (int , str ),
183- (str , float ),
184- (str , int ),
185- (int , float ),
186- )
187- if (type (value ), field_type ) not in whitelist :
188- raise ValueError (msg .format (field_name , field_type , type (value )))
189-
190- try :
191- value = field_type (value )
192- except ValueError as e :
193- e .message = msg .format (field_name , field_type , type (value ))
194- raise
195- return value
196-
197150
198151def get_cloudbuild (raw_config , args ):
199152 """Read and validate a cloudbuild recipe
@@ -210,7 +163,7 @@ def get_cloudbuild(raw_config, args):
210163 'Expected {} contents to be of type "dict", but found type "{}"' .
211164 format (args .config , type (raw_config )))
212165
213- raw_steps = get_field_value (raw_config , 'steps' , list )
166+ raw_steps = validation_utils . get_field_value (raw_config , 'steps' , list )
214167 if not raw_steps :
215168 raise ValueError ('No steps defined in {}' .format (args .config ))
216169
@@ -236,14 +189,14 @@ def get_step(raw_step):
236189 raise ValueError (
237190 'Expected step to be of type "dict", but found type "{}"' .
238191 format (type (raw_step )))
239- raw_args = get_field_value (raw_step , 'args' , list )
240- args = [get_field_value (raw_args , index , str )
192+ raw_args = validation_utils . get_field_value (raw_step , 'args' , list )
193+ args = [validation_utils . get_field_value (raw_args , index , str )
241194 for index in range (len (raw_args ))]
242- dir_ = get_field_value (raw_step , 'dir' , str )
243- raw_env = get_field_value (raw_step , 'env' , list )
244- env = [get_field_value (raw_env , index , str )
195+ dir_ = validation_utils . get_field_value (raw_step , 'dir' , str )
196+ raw_env = validation_utils . get_field_value (raw_step , 'env' , list )
197+ env = [validation_utils . get_field_value (raw_env , index , str )
245198 for index in range (len (raw_env ))]
246- name = get_field_value (raw_step , 'name' , str )
199+ name = validation_utils . get_field_value (raw_step , 'name' , str )
247200 return Step (
248201 args = args ,
249202 dir_ = dir_ ,
@@ -373,46 +326,21 @@ def local_cloudbuild(args):
373326 subprocess .check_call (args )
374327
375328
376- def validate_arg_regex (flag_value , flag_regex ):
377- """Check a named command line flag against a regular expression"""
378- if not re .match (flag_regex , flag_value ):
379- raise argparse .ArgumentTypeError (
380- 'Value "{}" does not match pattern "{}"' .format (
381- flag_value , flag_regex .pattern ))
382- return flag_value
383-
384-
385- def validate_arg_dict (flag_value ):
386- """Parse a command line flag as a key=val,... dict"""
387- if not flag_value :
388- return {}
389- entries = flag_value .split (',' )
390- pairs = []
391- for entry in entries :
392- match = re .match (KEY_VALUE_REGEX , entry )
393- if not match :
394- raise argparse .ArgumentTypeError (
395- 'Value "{}" should be a list like _KEY1=value1,_KEY2=value2"' .format (
396- flag_value ))
397- pairs .append ((match .group (1 ), match .group (2 )))
398- return dict (pairs )
399-
400-
401329def parse_args (argv ):
402330 """Parse and validate command line flags"""
403331 parser = argparse .ArgumentParser (
404332 description = 'Process cloudbuild.yaml locally to build Docker images' )
405333 parser .add_argument (
406334 '--config' ,
407335 type = functools .partial (
408- validate_arg_regex , flag_regex = PRINTABLE_REGEX ),
336+ validation_utils . validate_arg_regex , flag_regex = PRINTABLE_REGEX ),
409337 default = 'cloudbuild.yaml' ,
410338 help = 'Path to cloudbuild.yaml file'
411339 )
412340 parser .add_argument (
413341 '--output_script' ,
414342 type = functools .partial (
415- validate_arg_regex , flag_regex = PRINTABLE_REGEX ),
343+ validation_utils . validate_arg_regex , flag_regex = PRINTABLE_REGEX ),
416344 help = 'Filename to write shell script to' ,
417345 )
418346 parser .add_argument (
@@ -423,7 +351,7 @@ def parse_args(argv):
423351 )
424352 parser .add_argument (
425353 '--substitutions' ,
426- type = validate_arg_dict ,
354+ type = validation_utils . validate_arg_dict ,
427355 default = {},
428356 help = 'Parameters to be substituted in the build specification' ,
429357 )
0 commit comments