# -*- coding: utf-8 -*- # # Copyright 2018 Google LLC. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Flags for commands in cloudasset.""" from __future__ import absolute_import from __future__ import division from __future__ import unicode_literals from googlecloudsdk.calliope import arg_parsers from googlecloudsdk.calliope import base from googlecloudsdk.command_lib.util.apis import yaml_data from googlecloudsdk.command_lib.util.args import common_args from googlecloudsdk.command_lib.util.args import resource_args from googlecloudsdk.command_lib.util.concepts import concept_parsers def _GetOrAddArgGroup(parser, help_text): """Create a new arg group or return existing group with given help text.""" for arg in parser.arguments: if arg.is_group and arg.help == help_text: return arg return parser.add_argument_group(help_text) def GetOrAddOptionGroup(parser): return _GetOrAddArgGroup(parser, 'The analysis options.') def AddOrganizationArgs(parser, help_text): parser.add_argument( '--organization', metavar='ORGANIZATION_ID', help=help_text) def AddFolderArgs(parser, help_text): parser.add_argument('--folder', metavar='FOLDER_ID', help=help_text) def AddProjectArgs(parser, help_text): parser.add_argument('--project', metavar='PROJECT_ID', help=help_text) def AddParentArgs(parser, project_help_text, org_help_text, folder_help_text): parent_group = parser.add_mutually_exclusive_group(required=True) common_args.ProjectArgument( help_text_to_prepend=project_help_text).AddToParser(parent_group) AddOrganizationArgs(parent_group, org_help_text) AddFolderArgs(parent_group, folder_help_text) def AddAnalyzerParentArgs(parser): """Adds analysis parent(aka scope) argument.""" parent_group = parser.add_mutually_exclusive_group(required=True) AddOrganizationArgs( parent_group, 'Organization ID on which to perform the analysis.' ' Only policies defined at or below this organization ' ' will be targeted in the analysis.') AddFolderArgs( parent_group, 'Folder ID on which to perform the analysis.' ' Only policies defined at or below this folder will be ' ' targeted in the analysis.') AddProjectArgs( parent_group, 'Project ID or number on which to perform the analysis.' ' Only policies defined at or below this project will be ' ' targeted in the analysis.') def AddSnapshotTimeArgs(parser): parser.add_argument( '--snapshot-time', type=arg_parsers.Datetime.Parse, help=('Timestamp to take a snapshot on assets. This can only be a ' 'current or past time. If not specified, the current time will be ' 'used. Due to delays in resource data collection and indexing, ' 'there is a volatile window during which running the same query at ' 'different times may return different results. ' 'See $ gcloud topic datetimes for information on time formats.')) def AddAssetTypesArgs(parser): parser.add_argument( '--asset-types', metavar='ASSET_TYPES', type=arg_parsers.ArgList(), default=[], help=( 'A list of asset types (i.e., "compute.googleapis.com/Disk") to take ' 'a snapshot. If specified and non-empty, only assets matching the ' 'specified types will be returned. ' 'See http://cloud.google.com/asset-inventory/docs/supported-asset-types' ' for supported asset types.')) def AddRelationshipTypesArgs(parser): parser.add_argument( '--relationship-types', metavar='RELATIONSHIP_TYPES', type=arg_parsers.ArgList(), default=[], help=( 'A list of relationship types (i.e., "INSTANCE_TO_INSTANCEGROUP") to ' 'take a snapshot. This argument will only be honoured if ' 'content_type=RELATIONSHIP. If specified and non-empty, only ' 'relationships matching the specified types will be returned. ' 'See http://cloud.google.com/asset-inventory/docs/supported-asset-types' ' for supported relationship types.')) def AddContentTypeArgs(parser, required): """--content-type argument for asset export and get-history.""" if required: help_text = ('Asset content type.') else: help_text = ( 'Asset content type. If specified, only content matching the ' 'specified type will be returned. Otherwise, no content but the ' 'asset name will be returned.') help_text += ( ' Specifying `resource` will export resource metadata, specifying ' '`iam-policy` will export the IAM policy for each child asset, ' 'specifying `org-policy` will export the Org Policy set on child assets,' ' specifying `access-policy` will export the Access Policy set on child ' 'assets, specifying `os-inventory` will export the OS inventory of VM ' 'instances, and specifying `relationship` will export relationships of ' 'the assets.') parser.add_argument( '--content-type', required=required, choices=[ 'resource', 'iam-policy', 'org-policy', 'access-policy', 'os-inventory', 'relationship' ], help=help_text) def AddOutputPathArgs(parser, required): parser.add_argument( '--output-path', metavar='OUTPUT_PATH', required=required, type=arg_parsers.RegexpValidator( r'^gs://.*', '--output-path must be a Google Cloud Storage URI starting with ' '"gs://". For example, "gs://bucket_name/object_name"'), help='Google Cloud Storage URI where the results will go. ' 'URI must start with "gs://". For example, "gs://bucket_name/object_name"' ) def AddOutputPathPrefixArgs(parser): parser.add_argument( '--output-path-prefix', type=arg_parsers.RegexpValidator( r'^gs://.*/.*', '--output-path-prefix must be a Google Cloud Storage URI starting ' 'with "gs://". For example, "gs://bucket_name/object_name_prefix"'), help=( 'Google Cloud Storage URI where the results will go. ' 'URI must start with "gs://". For example, ' '"gs://bucket_name/object_name_prefix", in which case each exported ' 'object uri is in format: ' '"gs://bucket_name/object_name_prefix//" ' 'and it only contains assets for that type.')) def AddOutputPathBigQueryArgs(parser): """Add BigQuery destination args to argument list.""" bigquery_group = parser.add_group( mutex=False, required=False, help='The BigQuery destination for exporting assets.') resource = yaml_data.ResourceYAMLData.FromPath('bq.table') table_dic = resource.GetData() # Update the name 'dataset' in table_ref to 'bigquery-dataset' attributes = table_dic['attributes'] for attr in attributes: if attr['attribute_name'] == 'dataset': attr['attribute_name'] = 'bigquery-dataset' arg_specs = [ resource_args.GetResourcePresentationSpec( verb='export to', name='bigquery-table', required=True, prefixes=False, positional=False, resource_data=table_dic) ] concept_parsers.ConceptParser(arg_specs).AddToParser(bigquery_group) base.Argument( '--output-bigquery-force', action='store_true', dest='force_', default=False, required=False, help=( 'If the destination table already exists and this flag is specified, ' 'the table will be overwritten by the contents of assets snapshot. ' 'If the flag is not specified and the destination table already exists, ' 'the export call returns an error.')).AddToParser(bigquery_group) base.Argument( '--per-asset-type', action='store_true', dest='per_type_', default=False, required=False, help=('If the flag is specified, the snapshot results will be written to ' 'one or more tables, each of which contains results of one ' 'asset type.')).AddToParser(bigquery_group) base.ChoiceArgument( '--partition-key', required=False, choices=['read-time', 'request-time'], help_str=( 'If specified. the snapshot results will be written to partitioned ' 'table(s) with two additional timestamp columns, readTime and ' 'requestTime, one of which will be the partition key.' )).AddToParser(bigquery_group) def AddDestinationArgs(parser): destination_group = parser.add_group( mutex=True, required=True, help='The destination path for exporting assets.') AddOutputPathArgs(destination_group, required=False) AddOutputPathPrefixArgs(destination_group) AddOutputPathBigQueryArgs(destination_group) def AddAssetNamesArgs(parser): parser.add_argument( '--asset-names', metavar='ASSET_NAMES', required=True, type=arg_parsers.ArgList(), help=( 'A list of full names of the assets to get the history for. For more ' 'information, see: ' 'https://cloud.google.com/apis/design/resource_names#full_resource_name ' )) def AddStartTimeArgs(parser): parser.add_argument( '--start-time', required=True, type=arg_parsers.Datetime.Parse, help=('Start time of the time window (inclusive) for the asset history. ' 'Must be after the current time minus 35 days. See $ gcloud topic ' 'datetimes for information on time formats.')) def AddEndTimeArgs(parser): parser.add_argument( '--end-time', required=False, type=arg_parsers.Datetime.Parse, help=('End time of the time window (exclusive) for the asset history. ' 'Defaults to current time if not specified. ' 'See $ gcloud topic datetimes for information on time formats.')) def AddOperationArgs(parser): parser.add_argument( 'id', metavar='OPERATION_NAME', help='Name of the operation to describe.') def AddListContentTypeArgs(parser): help_text = ( 'Asset content type. If not specified, no content but the asset name and' ' type will be returned in the feed. For more information, see ' 'https://cloud.google.com/asset-inventory/docs/reference/rest/v1/feeds#ContentType' ) parser.add_argument( '--content-type', choices=[ 'resource', 'iam-policy', 'org-policy', 'access-policy', 'os-inventory', 'relationship' ], help=help_text) def AddSavedQueriesQueryId(parser, query_id_help_text): parser.add_argument( 'query_id', metavar='QUERY_ID', help=query_id_help_text) def AddSavedQueriesQueryFilePath(parser, is_required): query_file_path_help_text = ( 'Path to JSON or YAML file that contains the query.') parser.add_argument( '--query-file-path', required=is_required, help=query_file_path_help_text) def AddSavedQueriesQueryDescription(parser): description_help_text = ( 'A string describing the query.' ) parser.add_argument('--description', help=description_help_text) def AddFeedIdArgs(parser, help_text): parser.add_argument('feed', metavar='FEED_ID', help=help_text) def AddFeedNameArgs(parser, help_text): parser.add_argument('name', help=help_text) def AddFeedAssetTypesArgs(parser): parser.add_argument( '--asset-types', metavar='ASSET_TYPES', type=arg_parsers.ArgList(), default=[], help=( 'A comma-separated list of types of the assets types to receive ' 'updates. For example: ' '`compute.googleapis.com/Disk,compute.googleapis.com/Network`. Regular ' 'expressions (https://github.com/google/re2/wiki/Syntax) are also supported. For ' 'more information, see: ' 'https://cloud.google.com/resource-manager/docs/cloud-asset-inventory/overview' )) def AddFeedRelationshipTypesArgs(parser): parser.add_argument( '--relationship-types', metavar='RELATIONSHIP_TYPES', type=arg_parsers.ArgList(), default=[], help=( 'A comma-separated list of the relationship types (i.e., ' '"INSTANCE_TO_INSTANCEGROUP") to receive updates. This argument will ' 'only be honoured if content_type=RELATIONSHIP.' 'See http://cloud.google.com/asset-inventory/docs/supported-asset-types' ' for supported relationship types.')) def AddFeedAssetNamesArgs(parser): parser.add_argument( '--asset-names', metavar='ASSET_NAMES', type=arg_parsers.ArgList(), default=[], help=( 'A comma-separated list of the full names of the assets to ' 'receive updates. For example: ' '`//compute.googleapis.com/projects/my_project_123/zones/zone1/instances/instance1`.' ' For more information, see: https://cloud.google.com/apis/design/resource_names#full_resource_name' )) def AddFeedCriteriaArgs(parser): parent_group = parser.add_group(mutex=False, required=True) AddFeedAssetTypesArgs(parent_group) AddFeedAssetNamesArgs(parent_group) AddFeedRelationshipTypesArgs(parent_group) def FeedContentTypeArgs(parser, help_text): parser.add_argument( '--content-type', choices=[ 'resource', 'iam-policy', 'org-policy', 'access-policy', 'os-inventory', 'relationship' ], help=help_text) def AddFeedContentTypeArgs(parser): help_text = ( 'Asset content type. If not specified, no content but the asset name and' ' type will be returned in the feed. For more information, see ' 'https://cloud.google.com/resource-manager/docs/cloud-asset-inventory/overview#asset_content_type' ) FeedContentTypeArgs(parser, help_text) def AddFeedPubSubTopicArgs(parser, required): parser.add_argument( '--pubsub-topic', metavar='PUBSUB_TOPIC', required=required, help=('Name of the Cloud Pub/Sub topic to publish to, of the form ' '`projects/PROJECT_ID/topics/TOPIC_ID`. ' 'You can list existing topics with ' '`gcloud pubsub topics list --format="text(name)"`')) def AddChangeFeedContentTypeArgs(parser): help_text = ( 'Asset content type to overwrite the existing one. For more information,' ' see: ' 'https://cloud.google.com/resource-manager/docs/cloud-asset-inventory/overview#asset_content_type' ) FeedContentTypeArgs(parser, help_text) def AddClearFeedContentTypeArgs(parser): parser.add_argument( '--clear-content-type', action='store_true', help=('Clear any existing content type setting on the feed. ' 'Content type will be unspecified, no content but' ' the asset name and type will be returned in the feed.')) def AddUpdateFeedContentTypeArgs(parser): parent_group = parser.add_group(mutex=True) AddChangeFeedContentTypeArgs(parent_group) AddClearFeedContentTypeArgs(parent_group) def FeedConditionExpressionArgs(parser, help_text): parser.add_argument('--condition-expression', help=help_text) def AddFeedConditionExpressionArgs(parser): help_text = ( 'Feed condition expression. If not specified, no condition will be ' 'applied to feed. For more information, see: ' 'https://cloud.google.com/asset-inventory/docs/monitoring-asset-changes#feed_with_condition' ) FeedConditionExpressionArgs(parser, help_text) def AddChangeFeedConditionExpressionArgs(parser): help_text = ( 'Condition expression to overwrite the existing one. For more ' 'information, see: ' 'https://cloud.google.com/asset-inventory/docs/monitoring-asset-changes#feed_with_condition' ) FeedConditionExpressionArgs(parser, help_text) def AddClearFeedConditionExpressionArgs(parser): parser.add_argument( '--clear-condition-expression', action='store_true', help=('Clear any existing condition expression setting on the feed. ' 'No condition will be applied to feed.')) def AddUpdateFeedConditionExpressionArgs(parser): parent_group = parser.add_group(mutex=True) AddChangeFeedConditionExpressionArgs(parent_group) AddClearFeedConditionExpressionArgs(parent_group) def FeedConditionTitleArgs(parser, help_text): parser.add_argument('--condition-title', help=help_text) def AddFeedConditionTitleArgs(parser): help_text = ('Title of the feed condition. For reference only.') FeedConditionTitleArgs(parser, help_text) def AddChangeFeedConditionTitleArgs(parser): help_text = ('Condition title to overwrite the existing one.') FeedConditionTitleArgs(parser, help_text) def AddClearFeedConditionTitleArgs(parser): parser.add_argument( '--clear-condition-title', action='store_true', help=('Clear any existing condition title setting on the feed. ' 'Condition title will be empty.')) def AddUpdateFeedConditionTitleArgs(parser): parent_group = parser.add_group(mutex=True) AddChangeFeedConditionTitleArgs(parent_group) AddClearFeedConditionTitleArgs(parent_group) def FeedConditionDescriptionArgs(parser, help_text): parser.add_argument('--condition-description', help=help_text) def AddFeedConditionDescriptionArgs(parser): help_text = ('Description of the feed condition. For reference only.') FeedConditionDescriptionArgs(parser, help_text) def AddChangeFeedConditionDescriptionArgs(parser): help_text = ('Condition description to overwrite the existing one.') FeedConditionDescriptionArgs(parser, help_text) def AddClearFeedConditionDescriptionArgs(parser): parser.add_argument( '--clear-condition-description', action='store_true', help=('Clear any existing condition description setting on the feed. ' 'Condition description will be empty.')) def AddUpdateFeedConditionDescriptionArgs(parser): parent_group = parser.add_group(mutex=True) AddChangeFeedConditionDescriptionArgs(parent_group) AddClearFeedConditionDescriptionArgs(parent_group) def AddAnalyzerFullResourceNameArgs(parser): parser.add_argument('--full-resource-name', help='The full resource name.') def AddAnalyzerResourceSelectorGroup(parser): resource_selector_group = parser.add_group( mutex=False, required=False, help='Specifies a resource for analysis. Leaving it empty means ANY.') AddAnalyzerFullResourceNameArgs(resource_selector_group) def AddAnalyzerIdentityArgs(parser): parser.add_argument( '--identity', help=( 'The identity appearing in the form of principals in the IAM policy ' 'binding.')) def AddAnalyzerIdentitySelectorGroup(parser): identity_selector_group = parser.add_group( mutex=False, required=False, help='Specifies an identity for analysis. Leaving it empty means ANY.') AddAnalyzerIdentityArgs(identity_selector_group) def AddAnalyzerRolesArgs(parser): parser.add_argument( '--roles', metavar='ROLES', type=arg_parsers.ArgList(), help='The roles to appear in the result.') def AddAnalyzerPermissionsArgs(parser): parser.add_argument( '--permissions', metavar='PERMISSIONS', type=arg_parsers.ArgList(), help='The permissions to appear in the result.') def AddAnalyzerAccessSelectorGroup(parser): access_selector_group = parser.add_group( mutex=False, required=False, help=('Specifies roles or permissions for analysis. Leaving it empty ' 'means ANY.')) AddAnalyzerRolesArgs(access_selector_group) AddAnalyzerPermissionsArgs(access_selector_group) def AddAnalyzerSelectorsGroup(parser): AddAnalyzerResourceSelectorGroup(parser) AddAnalyzerIdentitySelectorGroup(parser) AddAnalyzerAccessSelectorGroup(parser) def AddAnalyzerExpandGroupsArgs(parser): parser.add_argument( '--expand-groups', action='store_true', help=( 'If true, the identities section of the result will expand any ' 'Google groups appearing in an IAM policy binding. Default is false.' )) parser.set_defaults(expand_groups=False) def AddAnalyzerExpandRolesArgs(parser): parser.add_argument( '--expand-roles', action='store_true', help=('If true, the access section of result will expand any roles ' 'appearing in IAM policy bindings to include their permissions. ' 'Default is false.')) parser.set_defaults(expand_roles=False) def AddAnalyzerExpandResourcesArgs(parser): parser.add_argument( '--expand-resources', action='store_true', help=('If true, the resource section of the result will expand any ' 'resource attached to an IAM policy to include resources lower in ' 'the resource hierarchy. Default is false.')) parser.set_defaults(expand_resources=False) def AddAnalyzerOutputResourceEdgesArgs(parser): parser.add_argument( '--output-resource-edges', action='store_true', help=('If true, the result will output the relevant parent/child ' 'relationships between resources. ' 'Default is false.')) parser.set_defaults(output_resource_edges=False) def AddAnalyzerOutputGroupEdgesArgs(parser): parser.add_argument( '--output-group-edges', action='store_true', help=('If true, the result will output the relevant membership ' 'relationships between groups. ' 'Default is false.')) parser.set_defaults(output_group_edges=False) def AddAnalyzerExecutionTimeout(parser): parser.add_argument( '--execution-timeout', type=arg_parsers.Duration(), help=( 'The amount of time the executable has to complete. See JSON ' 'representation of ' '[Duration](https://developers.google.com/protocol-buffers/docs/proto3#json). ' 'Deafult is empty. ')) def AddAnalyzerShowAccessControlEntries(parser): parser.add_argument( '--show-response', action='store_true', help=( 'If true, the response will be showed as-is in the command output.')) parser.set_defaults(show_response=False) def AddAnalyzerAnalyzeServiceAccountImpersonationArgs(parser): """Adds analyze service account impersonation arg into options. Args: parser: the option group. """ parser.add_argument( '--analyze-service-account-impersonation', action='store_true', help=( 'If true, the response will include access analysis from identities ' 'to resources via service account impersonation. This is a very ' 'expensive operation, because many derived queries will be executed. ' 'We highly recommend you use AnalyzeIamPolicyLongrunning rpc instead.' ' Default is false.')) parser.set_defaults(analyze_service_account_impersonation=False) def AddAnalyzerIncludeDenyPolicyAnalysisArgs(parser): """Adds include deny policy analysis arg into options. Args: parser: the option group. """ parser.add_argument( '--include-deny-policy-analysis', action='store_true', help=( 'If true, the response will include analysis for deny policies.' 'This is a very expensive operation, because many derived queries ' 'will be executed.' ), ) parser.set_defaults(include_deny_policy_analysis=False) def AddAnalyzerOptionsGroup(parser, is_sync): """Adds a group of options.""" options_group = GetOrAddOptionGroup(parser) AddAnalyzerExpandGroupsArgs(options_group) AddAnalyzerExpandRolesArgs(options_group) AddAnalyzerExpandResourcesArgs(options_group) AddAnalyzerOutputResourceEdgesArgs(options_group) AddAnalyzerOutputGroupEdgesArgs(options_group) AddAnalyzerAnalyzeServiceAccountImpersonationArgs(options_group) if is_sync: AddAnalyzerExecutionTimeout(options_group) AddAnalyzerShowAccessControlEntries(options_group) def AddAnalyzerAccessTimeArgs(parser): parser.add_argument( '--access-time', type=arg_parsers.Datetime.Parse, help=('The hypothetical access timestamp to evaluate IAM conditions.')) def AddAnalyzerSavedAnalysisQueryArgs(parser): """Adds a saved analysis query.""" identity_selector_group = parser.add_group( mutex=False, required=False, help='Specifies the name of a saved analysis query.') text = ( 'The name of a saved query. \n' 'When a `saved_analysis_query` is provided, ' 'its query content will be used as the base query. Other flags\' values ' 'will override the base query to compose the final query to run. ' 'IDs might be in one of the following formats:\n' '* projects/project_number/savedQueries/saved_query_id' '* folders/folder_number/savedQueries/saved_query_id' '* organizations/organization_number/savedQueries/saved_query_id') identity_selector_group.add_argument('--saved-analysis-query', help=text) def AddAnalyzerConditionContextGroup(parser): """Adds a group of options.""" condition_context_group = parser.add_group( mutex=False, required=False, help='The hypothetical context to evaluate IAM conditions.') AddAnalyzerAccessTimeArgs(condition_context_group) def AddStatementArgs(parser): """Adds the sql statement.""" parser.add_argument( '--statement', help=( 'A BigQuery Standard SQL compatible statement. If the query execution ' 'finishes within timeout and there is no pagination, the full query ' 'results will be returned. Otherwise, pass job_reference from ' 'previous call as `--job-reference` to obtain the full results.')) def AddJobReferenceArgs(parser): """Adds the previous job reference.""" parser.add_argument( '--job-reference', help=('Reference to the query job, which is from the previous call.')) def AddQueryArgs(parser): """Adds query args.""" query_group = parser.add_group( mutex=True, required=True, help='The query or job reference of the query request.') AddStatementArgs(query_group) AddJobReferenceArgs(query_group) def AddPageSize(parser): """Adds page size.""" parser.add_argument( '--page-size', type=int, help=( 'The maximum number of rows to return in the results. One page is also limited to 10 MB.' )) def AddPageToken(parser): """Adds page token.""" parser.add_argument( '--page-token', help=('A page token received from previous call.')) def AddTimeout(parser): """Adds timeout.""" parser.add_argument( '--timeout', type=arg_parsers.Duration(), help=( 'Maximum amount of time that the client will wait for the query to complete.' )) def AddReadTimeWindowArgs(parser): """Adds read time window.""" time_window_group = parser.add_group( mutex=False, required=False, help='Specifies what time period or point in time to query asset metadata at.' ) AddStartTimeArgs(time_window_group) AddEndTimeArgs(time_window_group) def AddTimeArgs(parser): """Adds read time.""" time_group = parser.add_group( mutex=True, required=False, help='Specifies what time period or point in time to query asset metadata at.' ) AddSnapshotTimeArgs(time_group) AddReadTimeWindowArgs(time_group) def AddQuerySystemBigQueryArgs(parser): """Add BigQuery destination args to argument list for query system.""" bigquery_group = parser.add_group( mutex=False, required=False, help='The BigQuery destination for query system.') resource = yaml_data.ResourceYAMLData.FromPath('bq.table') table_dic = resource.GetData() # Update the name 'dataset' in table_ref to 'bigquery-dataset' attributes = table_dic['attributes'] for attr in attributes: if attr['attribute_name'] == 'dataset': attr['attribute_name'] = 'bigquery-dataset' arg_specs = [ resource_args.GetResourcePresentationSpec( verb='for the export', name='bigquery-table', required=False, prefixes=False, positional=False, resource_data=table_dic) ] if arg_specs: concept_parsers.ConceptParser(arg_specs).AddToParser(bigquery_group) base.ChoiceArgument( '--write-disposition', help_str='Specifies the action that occurs if the destination table or partition already exists.', choices={ 'write-truncate': """If the table or partition already exists, BigQuery overwrites the entire table or all the partition\'s data.""", 'write-append': """If the table or partition already exists, BigQuery appends the data to the table or the latest partition.""", 'write-empty': """If the table already exists and contains data, an error is returned.""", }, ).AddToParser(bigquery_group) def AddDestinationGroup(parser): destination_group = parser.add_group( mutex=True, required=True, help='The destination path for writing IAM policy analysis results.', ) AddGcsOutputPathArgs(destination_group) AddBigQueryDestinationGroup(destination_group) def AddGcsOutputPathArgs(parser): parser.add_argument( '--gcs-output-path', metavar='GCS_OUTPUT_PATH', required=False, type=arg_parsers.RegexpValidator( r'^gs://.*', '--gcs-output-path must be a Google Cloud Storage URI starting with ' '"gs://". For example, "gs://bucket_name/object_name".', ), help=( 'Google Cloud Storage URI where the results will be written. URI must' ' start with "gs://". For example, "gs://bucket_name/object_name".' ), ) def AddBigQueryDestinationGroup(parser): bigquery_destination_group = parser.add_group( mutex=False, required=False, help='BigQuery destination where the results will go.', ) AddBigQueryDatasetArgs(bigquery_destination_group) AddBigQueryTablePrefixArgs(bigquery_destination_group) AddBigQueryPartitionKeyArgs(bigquery_destination_group) AddBigQueryWriteDispositionArgs(bigquery_destination_group) def AddBigQueryDatasetArgs(parser): parser.add_argument( '--bigquery-dataset', metavar='BIGQUERY_DATASET', required=True, type=arg_parsers.RegexpValidator( r'^projects/[A-Za-z0-9\-]+/datasets/[\w]+', '--bigquery-dataset must be a dataset relative name starting with ' '"projects/". For example, ' '"projects/project_id/datasets/dataset_id".', ), help=( 'BigQuery dataset where the results will be written. Must be a ' 'dataset relative name starting with "projects/". For example, ' '"projects/project_id/datasets/dataset_id".' ), ) def AddBigQueryTablePrefixArgs(parser): parser.add_argument( '--bigquery-table-prefix', metavar='BIGQUERY_TABLE_PREFIX', required=True, type=arg_parsers.RegexpValidator( r'[\w]+', '--bigquery-table-prefix must be a BigQuery table name consists of ' 'letters, numbers and underscores".', ), help=( 'The prefix of the BigQuery tables to which the analysis results ' 'will be written. A table name consists of letters, numbers and ' 'underscores".' ), ) def AddBigQueryPartitionKeyArgs(parser): parser.add_argument( '--bigquery-partition-key', choices=['PARTITION_KEY_UNSPECIFIED', 'REQUEST_TIME'], help=( 'This enum determines the partition key column for the bigquery' ' tables. Partitioning can improve query performance and reduce query' ' cost by filtering partitions. Refer to' ' https://cloud.google.com/bigquery/docs/partitioned-tables for' ' details.' ), ) def AddBigQueryWriteDispositionArgs(parser): parser.add_argument( '--bigquery-write-disposition', metavar='BIGQUERY_WRITE_DISPOSITION', help=( 'Specifies the action that occurs if the destination table or ' 'partition already exists. The following values are supported: ' 'WRITE_TRUNCATE, WRITE_APPEND and WRITE_EMPTY. The default value is ' 'WRITE_APPEND.' ), )