
    F                     ~   d Z ddlmZ ddlmZ ddlmZ ddlZddlZddlZddlZddl	Z	ddl
Z
ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddlZddddi dddZdgZ G d de      Zd Zde fdZ!d Z"d Z#d Z$d Z%d Z&d Z'd Z(d Z)ejT                   G d dejV                               Z,y) a  Command that statically validates gcloud commands for corectness.

To validate a command, run:

```
gcloud meta lint-gcloud-commands --command-string="gcloud compute instances
list"
```

To validate a list of commands in a file:

1) Create a JSON file with the following format:

```
[
  {
    "command_string": "gcloud compute instances list",
  },
  {
    "command_string": "gcloud compute instances describe my-instance",
  }
]
```

2) Then run the command:

```
gcloud meta lint-gcloud-commands --commands-file=commands.json
```

Commands can also be associated with an ID, which will be used to identify the
command in the output. Simply run:

```
gcloud meta lint-gcloud-commands --commands-file=commands.json --serialize
```
This will associated each command with using the index it was found in the file
as the ID. If you want to associate a command with a specific ID, you can do so
by adding the `id` field to the command in the JSON file. For example:

```
[
  {
    "command_string": "gcloud compute instances list",
    "id": 0,
  },
  {
    "command_string": "gcloud compute instances describe my-instance",
    "id": 1,
  }
]
```

This will output the validation results in the following format:

```
{"0": [{<OUTPUT_1>}], "1": [{<OUTPUT_2>}]}
    )absolute_import)division)unicode_literalsN)collections)gcloud_main)base)
exceptions)generate_argument_spec)log)yaml)filesF)command_stringsuccesscommand_argscommand_string_no_argsargs_structureerror_message
error_typez--helpc                       e Zd Zy)CommandValidationErrorN)__name__
__module____qualname__     (lib/surface/meta/lint_gcloud_commands.pyr   r   m   s    r   r   c                 .   t        j                  |       5 }t        j                  |      }ddd       d}i }t	        d D              }|D ]:  }|j                  d      }|r|t        d|d    d      |xs |||d   <   |dz  }< |S # 1 sw Y   `xY w)	z Reads commands from a JSON file.Nr   c              3   >   K   | ]  }|j                  d         yw)idN)get).0command_datas     r   	<genexpr>z+_read_commands_from_file.<locals>.<genexpr>w   s     N<ML!!$'<Ms   r   z,Not all commands have an ID. Id for command r   z	 is None.   )r   
FileReaderjsonloadanyr    
ValueError)commands_filefcommand_file_dataref_idcommand_stringsneeds_idr"   
command_ids           r   _read_commands_from_filer1   q   s    &!		! '&/N<MNN('l!!$'JJ&+,-Y8  7A6JFOL!123
aKF ( 
 '&s   BBr   c                 ~   | j                  d      d   } 	 t        j                  dk(  rt        j                   | d      }nt        j                   |       }|D cg c]  }|j                  d      s| }}|D cg c]  }|j                  d      r| }}||z   S # t        $ r t        d      w xY wc c}w c c}w )z Move all flag arguments to back.#r   ntF)posix;Command could not be validated due to unforeseen edge case.--)splitosnameshlex	Exceptionr   
startswith)r   command_argumentsarg	flag_argsr   s        r   _separate_command_argumentsrA      s    !'',Q/.		ww$++nEB++n5 0H/s3>>$3Gs/)H!2O!2##..:N#!2,O			!! 
 
 E 
 IOs$   A B B51B5;B:B:B2c                 j    d}d}t        j                  |||       }t        j                  dd|      }|S )zLAdds equals signs to gcloud command flags, except for format and help flags.z(--[a-zA-Z0-9-]+) +([^-][^ ]*)z\1=\2z(--[a-zA-Z0-9-]+)= z\1 )resub)commandpatternreplacementmodified_commands       r   _add_equals_to_flagsrI      sA     ( 
 +VVG[':VV2F<LM	r   c                     t        |       } | j                  dd      j                  dd      j                  dd      j                  dd      } | S )Nz--project=PROJECT z--project=my-project z--project=PROJECT_ID z$PROJECT_ID zmy-project zYOUR_PROJECT_ID )rI   replace)command_strs    r   formalize_gcloud_commandrM      sR    $[1+.0GHw&(?@w~}-w!=1	  
r   c                 $   t        | d      j                  d      } d}d}t        j                  | d| t        j                        }g }|j                  |       D ]  }|j                  d      j                         }d|vs|j                  d      s8|j                  d      D ]f  }|j                  d	      }t        |      dk\  s#|d
   j                         s7t        |d
   j                               }|j                  d|        h  |S )zExtracts code snippets from fenced code blocks within a text string.

  Args:
      text: The text string containing fenced code blocks.

  Returns:
      A list of extracted code snippets.
  zutf-8unicode_escapez```(?:[\w ]+\n)?(.*?)```z(?: {3-8}|\t)(.*?)(?:\n\S|\n$)|r$   zgcloud gcloud
r   )bytesdecoderC   compileDOTALLfinditergroupstripr=   r8   lenrM   append)	textfenced_patternindented_patterncombined_patterncode_snippetsmatchrL   cmdcmd_new_liness	            r   _extract_gcloud_commandsrd      s    
tW		$	$%5	6$..'  ZZ	*+,bii -((
e ++a.&&(K#;+A+A(+K  +iiom	]	q	 ]1%5%;%;%=.}Q/?/E/E/GHw{m45	 , 
r   c                     t        j                  g       }| dd } |j                         }| D ].  }|j                  d      r |S |j	                  |      }|s |S |}0 |S )z9Returns the command node for the given command arguments.r$   Nr7   )r   	CreateCLI_TopElementr=   LoadSubElement)r>   clicurrent_command_nodeargumentchild_command_nodes        r   _get_command_noderm      s~    b!#'+*#h4 
 
	 .<<XF	 . $ 
r   c                 L    dj                  | j                  j                        S )1Returns the command string without any arguments. )joinaicommand_name)command_nodes    r   _get_command_no_argsru      s    	,//..	//r   c                 0    t        j                  |       }|S )ro   )r
   GenerateArgumentSpecifications)rt   argument_trees     r   _get_command_args_treery      s    (GG- 
r   c                 P    g fdfdfd| D ]
  } |        S )z&Returns a dict of positional metavars.c                 j    d| v r.| j                  dd      r| d   rj                  | d          y y y y )Nr:   
positionalF)r    r[   )nodepositional_argss    r   _process_argz._get_positional_metavars.<locals>._process_arg   s<    ~$((<7	ftF|, 
 8~r   c                 $    | D ]
  } |        y )Nr   )r}   r?   _traverse_trees     r   _traverse_arg_groupz5_get_positional_metavars.<locals>._traverse_arg_group   s    S r   c                 @    d| v r| d   d   } |       y  |        y )NrX   	argumentsr   )r}   rX   r   r   s     r   r   z0_get_positional_metavars.<locals>._traverse_tree   s(    $7mK(e% 4r   r   )	args_treer}   r   r   r   r~   s     @@@@r   _get_positional_metavarsr      s3     /-
 d4 	r   c                   	
 t               i }t        |d         
d } ||       } 	
fd}d}| D ]_  		}	j                  d      r'	j                  d      }|dk7  r	d| }	|d	z   d }nd
}n |       \  }}|xs 	}|xs d
}||d||<   |d	z  }a t	        j
                  t        |j                         d             S )z$Normalizes command args for storage.r   c                     t        | D cg c]  }|j                  d      s| c}      }| D cg c]  }|j                  d      r| }}||z   S c c}w c c}w )zSorts command arguments.

    Arguments starting with '--' are placed at the back, and all arguments are
    ordered alphabetically.

    Args:
      args: The command arguments to sort.

    Returns:
      The sorted command arguments.
    r7   )sortedr=   )argsr?   r@   r~   s       r   _sort_command_argsz3_normalize_command_args.<locals>._sort_command_args  s[     tDts~~d/CtDEI&*Gds#..2FsdOGY&& EGs   AAAAc                  n    D ]/  } | vs}| j                         }j                  |        ||fc S  y)N)NN)upperadd)positional_metavarcommand_valuecommand_arg_namecommand_argpositional_args_in_treepositionals_useds      r   "_get_next_available_positional_argzC_normalize_command_args.<locals>._get_next_available_positional_arg  sJ    5	#3	3#-335/0.. 6 r   r   r7   =Nr$    )valueindexc                     | d   d   S )Nr$   r   r   )items    r   <lambda>z)_normalize_command_args.<locals>.<lambda>?  s    d1gg6Fr   )key)setr   r=   findr   OrderedDictr   items)r   r   arg_name_valuer   r   	arg_indexr   equals_indexr   r   r   r   s            @@@r   _normalize_command_argsr     s    U.4Y{5KL'  $L1, )!k"d# %%c*l		&}5#L1$4$67 )K(L% *8[#)rm(N#$ NI) "* 
	 	 ^!!#)FG
 r   c                       e Zd ZdZ ej
                         Z ej
                         Zg ZdZ	dZ
ddZd Zd Zd Zd Z	 	 	 dd	Zd
 Zed        Zd Zy)GenerateCommandzvGenerate YAML file to implement given command.

  The command YAML file is generated in the --output-dir directory.
  Fc                     t        |      }t        |      }| j                  |||      \  }}}|sy| j                  ||||      }|sy| j	                  d|||       y)zValidate a single command.NT)rM   rA   _validate_command_prefix_validate_command_suffix_store_validation_results)selfr   r-   r>   command_successrt   flag_argumentsflag_successs           r   _validate_commandz!GenerateCommand._validate_commandP  sr    -n=N3NC%%&7P 2O\> 00nnfL ""4Pr   c           
          t        |      }|j                         D ]  \  }}	 | j                  ||        y# t        $ r#}| j	                  d||dd| d       Y d}~Cd}~ww xY w)z+Validate multiple commands given in a file.FNz Command could not be validated: r   )r1   r   r   r<   r   )r   r*   commandsrE   r-   es         r   _validate_commands_from_filez,GenerateCommand._validate_commands_from_file`  st    '6H#>>+

w/ ,  
&&.qc2$	
 	

s   8	A$AA$c                     t        j                  |      5 }|j                         }ddd       t              }d}|D ]  }| j	                  ||       |dz  } y# 1 sw Y   5xY w)z2Validate multiple commands given in a text string.Nr   r$   )r   r%   readrd   r   )r   commands_text_filer+   r\   r   r-   rE   s          r   _validate_commands_from_textz,GenerateCommand._validate_commands_from_textp  s^    			,	-VVXd 
.'-HF
Wf-kf 	 
.	-s   AA$c           
         t        j                  g       }|dd }d}|j                         }|D ]u  }|j                  d      r
d|||d fc S |j	                  |      }|s*| j                  d||||d dj                  |      d        y	|dz  }|j                  rmd|||d fc S  ||d }|s| j                  d||||d d
d       y	t        d      )zDValidate that the argument string contains a valid command or group.r$   Nr   r7   TFzInvalid choice: '{}'UnrecognizedCommandError)FNNzCommand name argument expectedr6   )	r   rf   rg   r=   rh   r   formatis_groupr   )	r   r>   r   r-   ri   r   rj   rk   remaining_flagss	            r   r   z(GenerateCommand._validate_command_prefixz  s.   



#C)!"-E??,% 
		T	" ef%
 	
 2@@J!&&ef%"))(3&	
 !qje!** ef%
 	
3 &@ (/O
$$



EF
#
*
$  !E r   c                    t         D ]  }||v s y d}|r|D ]  }d|v s	d|v sd|v sd} |sg }|s|j                  d       	 |j                  j                  |d       y# t        j
                  t        j                  t        j                  f$ r Y yt        j                  $ rX}dt        |      v rY d	}~y| j                  d|||t        j                  |      t!        |      j"                         Y d	}~yd	}~ww xY w)
zDValidates that the given flags can be parsed by the argparse parser.TFz	--projectz--folderz--organizationz--project=myproject)raise_errorzNo such file or directoryN)_IGNORE_ARGSr[   _parser
parse_argsr   MissingFileErrorgcloud_exceptionsBadFileExceptionr   FileLoadErrorargparseArgumentErrorstrr   six	text_typetyper   )	r   rt   r>   r   r-   ignored_argfound_parentr   r   s	            r   r   z(GenerateCommand._validate_command_suffix  s    $	)	) $ L*+;&[(;., + 45%%&7T%J& # 	** 
   !! 	$A	.
$$




--

q'

 s$   A" "2D D (C;:<C;;D Nc                 h   t        j                  t              }||d<   	 t        t	        |            }t        |      |d<   t        |      |d<   |rt        ||d         |d<   ||d<   ||d<   ||d<   t        j                  t        |j                                     }	| j                  r=|| j                  vr|	g| j                  |<   n| j                  |   j                  |	       | j                   r|	| j"                  |<   y| j$                  j                  |	       y# t        $ r ||d<   Y w xY w)	z4Store information related to the command validation.r   r   r   r   r   r   r   N)copydeepcopy_PARSING_OUTPUT_TEMPLATErm   rA   ru   ry   r<   r   r   r   r   r   serialize_results_SERIALIZED_VALIDATION_RESULTSr[   index_results_INDEXED_VALIDATION_RESULTS_VALIDATION_RESULTS)
r   r   r   r-   r   r   r   validation_outputrt   sorted_validation_outputs
             r   r   z)GenerateCommand._store_validation_results  sf    &>?*8&'	C&
%n
5l 5I
501 -C<,P() *A
)*:;+' $+i )6o&&0l#*66 &&()  	t::	:7O6P++F3++F3::$	
 
" &&~6 %%&>?1  C4B01Cs   0D   D10D1c                    | j                   r=t        j                  j                  t	        j
                  | j                               y| j                  r=t        j                  j                  t	        j
                  | j                               yt        j                  j                  t	        j
                  | j                               y)z$Output collected validation results.N)
r   r   outPrintr&   dumpsr   r   r   r   )r   s    r   _log_validation_resultsz'GenerateCommand._log_validation_results
  sq    	ggmmDJJt??@A				ggmmDJJtBBCD	ggmmDJJt7789r   c                     | j                  d      }|j                  dd       |j                  dd       |j                  dd	       | j                  d
dd       y )NT)mutexz--command-stringz&Gcloud command to statically validate.)helpz--commands-filez9JSON file containing list of gcloud commands to validate.z--commands-text-filezRaw text containing gcloud command(s) to validate. For example, the commands could be in fenced code blocks or indented code blocks.z--serialize
store_truez:Output results in a dictionary serialized by reference id.)actionr   )	add_groupadd_argument)parsercommand_groups     r   ArgszGenerateCommand.Args  s    $$4$0M5   H      I  r   c                 4   |j                   rd| _        |j                  d      r| j                  |j                         nH|j                  d      r| j                  |j                         n| j                  |j                         | j                          y )NTr   r   )
	serializer   IsSpecifiedr   r   r   r   r   r*   r   )r   r   s     r   RunzGenerateCommand.Run,  sw    ~~#d()
T001			.	/
''(?(?@
''(:(:;  "r   )r   )NNN)r   r   r   __doc__r   r   r   r   r   r   r   r   r   r   r   r   r   r   staticmethodr   r   r   r   r   r   r   C  s    
 !8 7 7 9#:;#:#:#< -Q 
 6p(^ ,@\:  0	#r   r   )-r   
__future__r   r   r   r   r   r&   r9   rC   r;   typingr   googlecloudsdkr   googlecloudsdk.callioper   r	   r   googlecloudsdk.command_lib.metar
   googlecloudsdk.corer   r   googlecloudsdk.core.utilr   r   r   r   r<   r   r1   r   rA   rI   rM   rd   rm   ru   ry   r   r   UniverseCompatibleCommandr   r   r   r   <module>r      s   9v '  '    	 	   & ( C B # $ * 
 "  zY &" "&
B0
4:z q#dll q# q#r   