feat: Add new gcloud commands, API clients, and third-party libraries across various services.

This commit is contained in:
2026-01-01 20:26:35 +01:00
parent 5e23cbece0
commit a19e592eb7
25221 changed files with 8324611 additions and 0 deletions

View File

@@ -0,0 +1,182 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google Inc. 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.
"""Client for interaction with AspectType API CRUD DATAPLEX."""
import os
from apitools.base.py import encoding
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.core import yaml
from googlecloudsdk.core.util import files
import six
def GenerateAspectTypeForCreateRequest(args):
"""Create Aspect Type Request."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1AspectType(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1AspectType, args
),
metadataTemplate=GenerateAspectTypeMetadataTemplate(
args.metadata_template_file_name
),
)
if hasattr(args, 'data_classification') and args.IsSpecified(
'data_classification'
):
request.dataClassification = module.GoogleCloudDataplexV1AspectType.DataClassificationValueValuesEnum(
args.data_classification
)
return request
def GenerateAspectTypeForUpdateRequest(args):
"""Update Aspect Type Request."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1AspectType(
description=args.description,
displayName=args.display_name,
etag=args.etag,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1AspectType, args
),
metadataTemplate=GenerateUpdateAspectTypeMetadataTemplate(
args.metadata_template_file_name
),
)
def GenerateAspectTypeUpdateMask(args):
"""Create Update Mask for AspectType."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('metadata_template_file_name'):
update_mask.append('metadataTemplate')
return update_mask
def GenerateUpdateAspectTypeMetadataTemplate(metadata_template_file_name):
"""Update Metadata Template for AspectType."""
if metadata_template_file_name is None:
return None
return GenerateAspectTypeMetadataTemplate(metadata_template_file_name)
def GenerateAspectTypeMetadataTemplate(metadata_template_file_name):
"""Create Metadata Template from specified file."""
if not os.path.exists(metadata_template_file_name):
raise exceptions.BadFileException('No such file [{0}]'.format(
metadata_template_file_name))
if os.path.isdir(metadata_template_file_name):
raise exceptions.BadFileException('[{0}] is a directory'.format(
metadata_template_file_name))
try:
with files.FileReader(metadata_template_file_name) as import_file:
return ConvertMetadataTemplateFileToProto(import_file)
except Exception as exp:
exp_msg = getattr(exp, 'message', six.text_type(exp))
msg = ('Unable to read Metadata Template config from specified file '
'[{0}] because [{1}]'.format(metadata_template_file_name, exp_msg))
raise exceptions.BadFileException(msg)
def ConvertMetadataTemplateFileToProto(metadata_template_file_path):
"""Construct an AspectTypeMetadataTemplate from a JSON/YAML formatted file.
Args:
metadata_template_file_path: Path to the JSON or YAML file.
Returns:
a protorpc.Message of type GoogleCloudDataplexV1AspectTypeMetadataTemplate
filled in from the JSON or YAML metadata template file.
Raises:
BadFileException if the JSON or YAML file is malformed.
"""
try:
parsed_metadata_template = yaml.load(metadata_template_file_path)
except ValueError as e:
raise exceptions.BadFileException(
'Error parsing metadata template file: {0}'.format(six.text_type(e)))
metadata_template_message = dataplex_api.GetMessageModule(
).GoogleCloudDataplexV1AspectTypeMetadataTemplate
metadata_template = encoding.PyValueToMessage(metadata_template_message,
parsed_metadata_template)
return metadata_template
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_aspectTypes)
def AspectTypeSetIamPolicy(aspect_type_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsAspectTypesSetIamPolicyRequest(
resource=aspect_type_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance(
).projects_locations_aspectTypes.SetIamPolicy(set_iam_policy_req)
def AspectTypeGetIamPolicy(aspect_type_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsAspectTypesGetIamPolicyRequest(
resource=aspect_type_ref.RelativeName())
return dataplex_api.GetClientInstance(
).projects_locations_aspectTypes.GetIamPolicy(get_iam_policy_req)
def AspectTypeAddIamPolicyBinding(aspect_type_ref, member, role):
"""Add IAM policy binding request."""
policy = AspectTypeGetIamPolicy(aspect_type_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return AspectTypeSetIamPolicy(aspect_type_ref, policy)
def AspectTypeRemoveIamPolicyBinding(aspect_type_ref, member, role):
"""Remove IAM policy binding request."""
policy = AspectTypeGetIamPolicy(aspect_type_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return AspectTypeSetIamPolicy(aspect_type_ref, policy)
def AspectTypeSetIamPolicyFromFile(aspect_type_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return AspectTypeSetIamPolicy(aspect_type_ref, policy)

View File

@@ -0,0 +1,207 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 Google Inc. 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.
"""Client for interaction with Asset API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.command_lib.iam import iam_util
def SetIamPolicy(asset_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsLakesZonesAssetsSetIamPolicyRequest(
resource=asset_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance(
).projects_locations_lakes_zones_assets.SetIamPolicy(set_iam_policy_req)
def GetIamPolicy(asset_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsLakesZonesAssetsGetIamPolicyRequest(
resource=asset_ref.RelativeName())
return dataplex_api.GetClientInstance(
).projects_locations_lakes_zones_assets.GetIamPolicy(get_iam_policy_req)
def AddIamPolicyBinding(asset_ref, member, role):
"""Add IAM policy binding request."""
policy = GetIamPolicy(asset_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return SetIamPolicy(asset_ref, policy)
def RemoveIamPolicyBinding(zone_ref, member, role):
"""Remove IAM policy binding request."""
policy = GetIamPolicy(zone_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return SetIamPolicy(zone_ref, policy)
def SetIamPolicyFromFile(asset_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return SetIamPolicy(asset_ref, policy)
def GenerateAssetForCreateRequest(args):
"""Create Asset for Message Create Requests."""
module = dataplex_api.GetMessageModule()
resource_spec_field = module.GoogleCloudDataplexV1AssetResourceSpec
resource_spec = module.GoogleCloudDataplexV1AssetResourceSpec(
name=args.resource_name,
type=resource_spec_field.TypeValueValuesEnum(args.resource_type),
)
if args.IsSpecified('resource_read_access_mode'):
resource_spec.readAccessMode = (
resource_spec_field.ReadAccessModeValueValuesEnum(
args.resource_read_access_mode
)
)
request = module.GoogleCloudDataplexV1Asset(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1Asset, args),
resourceSpec=resource_spec)
discovery = GenerateDiscoverySpec(args)
if discovery != module.GoogleCloudDataplexV1AssetDiscoverySpec():
setattr(request, 'discoverySpec', discovery)
return request
def GenerateAssetForCreateRequestAlpha(args):
return GenerateAssetForCreateRequest(args)
def GenerateAssetForUpdateRequest(args):
"""Create Asset for Message Update Requests."""
module = dataplex_api.GetMessageModule()
asset = module.GoogleCloudDataplexV1Asset(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1Asset, args),
discoverySpec=GenerateDiscoverySpec(args),
)
if args.IsSpecified('resource_read_access_mode'):
setattr(
asset,
'resourceSpec',
module.GoogleCloudDataplexV1AssetResourceSpec(
readAccessMode=(
module.GoogleCloudDataplexV1AssetResourceSpec.ReadAccessModeValueValuesEnum(
args.resource_read_access_mode
)
)
),
)
return asset
def GenerateAssetForUpdateRequestAlpha(args):
return GenerateAssetForUpdateRequest(args)
def GenerateDiscoverySpec(args):
"""Create Discovery Spec for Assets."""
module = dataplex_api.GetMessageModule()
discovery_spec = module.GoogleCloudDataplexV1AssetDiscoverySpec(
enabled=args.discovery_enabled,
includePatterns=args.discovery_include_patterns,
excludePatterns=args.discovery_exclude_patterns)
if args.discovery_schedule:
discovery_spec.schedule = args.discovery_schedule
csv_options = GenerateCsvOptions(args)
if csv_options != module.GoogleCloudDataplexV1AssetDiscoverySpecCsvOptions():
discovery_spec.csvOptions = csv_options
json_options = GenerateJsonOptions(args)
if json_options != module.GoogleCloudDataplexV1AssetDiscoverySpecJsonOptions(
):
discovery_spec.jsonOptions = json_options
return discovery_spec
def GenerateCsvOptions(args):
return dataplex_api.GetMessageModule(
).GoogleCloudDataplexV1AssetDiscoverySpecCsvOptions(
delimiter=args.csv_delimiter,
disableTypeInference=args.csv_disable_type_inference,
encoding=args.csv_encoding,
headerRows=args.csv_header_rows)
def GenerateJsonOptions(args):
return dataplex_api.GetMessageModule(
).GoogleCloudDataplexV1AssetDiscoverySpecJsonOptions(
encoding=args.json_encoding,
disableTypeInference=args.json_disable_type_inference)
def GenerateUpdateMaskAlpha(args):
return GenerateUpdateMask(args)
def GenerateUpdateMask(args):
"""Create Update Mask for Assets."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('discovery_enabled'):
update_mask.append('discoverySpec.enabled')
if args.IsSpecified('discovery_include_patterns'):
update_mask.append('discoverySpec.includePatterns')
if args.IsSpecified('discovery_exclude_patterns'):
update_mask.append('discoverySpec.excludePatterns')
if args.IsSpecified('discovery_schedule'):
update_mask.append('discoverySpec.schedule')
if args.IsSpecified('csv_header_rows'):
update_mask.append('discoverySpec.csvOptions.headerRows')
if args.IsSpecified('csv_delimiter'):
update_mask.append('discoverySpec.csvOptions.delimiter')
if args.IsSpecified('csv_encoding'):
update_mask.append('discoverySpec.csvOptions.encoding')
if args.IsSpecified('csv_disable_type_inference'):
update_mask.append('discoverySpec.csvOptions.disableTypeInference')
if args.IsSpecified('json_encoding'):
update_mask.append('discoverySpec.jsonOptions.encoding')
if args.IsSpecified('json_disable_type_inference'):
update_mask.append('discoverySpec.jsonOptions.disableTypeInference')
if args.IsSpecified('resource_read_access_mode'):
update_mask.append('resourceSpec.readAccessMode')
return update_mask
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_lakes_zones_assets)

View File

@@ -0,0 +1,144 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 Google Inc. 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.
"""Client for interaction with CONTENT API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.command_lib.iam import iam_util
def SetIamPolicy(content_ref, policy):
"""Sets Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsLakesContentSetIamPolicyRequest(
resource=content_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance(
).projects_locations_lakes_content.SetIamPolicy(set_iam_policy_req)
def GetIamPolicy(content_ref):
"""Gets Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsLakesContentGetIamPolicyRequest(
resource=content_ref.RelativeName())
return dataplex_api.GetClientInstance(
).projects_locations_lakes_content.GetIamPolicy(get_iam_policy_req)
def AddIamPolicyBinding(content_ref, member, role):
"""Adds iam policy binding request."""
policy = GetIamPolicy(content_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return SetIamPolicy(content_ref, policy)
def RemoveIamPolicyBinding(lake_ref, member, role):
"""Removes iam policy binding request."""
policy = GetIamPolicy(lake_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return SetIamPolicy(lake_ref, policy)
def SetIamPolicyFromFile(content_ref, policy_file):
"""Sets iam policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return SetIamPolicy(content_ref, policy)
def GenerateContentForCreateRequest(args):
"""Creates Content for Message Create Requests."""
module = dataplex_api.GetMessageModule()
content = module.GoogleCloudDataplexV1Content(
dataText=args.data_text,
description=args.description,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1Content,
args),
path=args.path)
if args.kernel_type:
content.notebook = GenerateNotebook(args)
if args.query_engine:
content.sqlScript = GenerateSqlScript(args)
return content
def GenerateContentForUpdateRequest(args):
"""Creates Content for Message Update Requests."""
module = dataplex_api.GetMessageModule()
content = module.GoogleCloudDataplexV1Content(
dataText=args.data_text,
description=args.description,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1Content,
args),
path=args.path)
if args.kernel_type:
content.notebook = GenerateNotebook(args)
if args.query_engine:
content.sqlScript = GenerateSqlScript(args)
return content
def GenerateNotebook(args):
"""Creates Notebook field for Content Message Create/Update Requests."""
module = dataplex_api.GetMessageModule()
kernel_type_field = module.GoogleCloudDataplexV1ContentNotebook
notebook = module.GoogleCloudDataplexV1ContentNotebook()
if args.kernel_type:
notebook.kernelType = kernel_type_field.KernelTypeValueValuesEnum(
args.kernel_type)
return notebook
def GenerateSqlScript(args):
"""Creates SQL Script field for Content Message Create/Update Requests."""
module = dataplex_api.GetMessageModule()
query_engine_field = module.GoogleCloudDataplexV1ContentSqlScript
sql_script = module.GoogleCloudDataplexV1ContentSqlScript()
if args.query_engine:
sql_script.engine = query_engine_field.EngineValueValuesEnum(
args.query_engine)
return sql_script
def GenerateUpdateMask(args):
"""Creates Update Mask for Content."""
args_api_field_map = {
'description': 'description',
'labels': 'labels',
'path': 'path',
'query_engine': 'sqlScript.engine',
'kernel_type': 'notebook.kernelType',
'data_text': 'data_text'
}
update_mask = []
for k, v in args_api_field_map.items():
if args.IsSpecified(k):
update_mask.append(v)
return update_mask
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_lakes_content)

View File

@@ -0,0 +1,407 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google Inc. 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.
"""Client for interaction with DataTaxonomy API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import json
import os
from apitools.base.py import encoding
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.core import yaml
from googlecloudsdk.core.util import files
import six
def GenerateResourceAccessSpec(args):
"""Generate Resource Access Spec From Arguments."""
module = dataplex_api.GetMessageModule()
resource_access_spec = module.GoogleCloudDataplexV1ResourceAccessSpec(
owners=args.resource_owners,
readers=args.resource_readers,
writers=args.resource_writers)
return resource_access_spec
def GenerateDataAccessSpec(args):
"""Generate Data Access Spec From Arguments."""
module = dataplex_api.GetMessageModule()
data_access_spec = module.GoogleCloudDataplexV1DataAccessSpec(
readers=args.data_readers)
return data_access_spec
def GenerateDataTaxonomyForCreateRequest(args):
"""Create Data Taxonomy Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1DataTaxonomy(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1DataTaxonomy,
args))
return request
def GenerateDataTaxonomyForUpdateRequest(args):
"""Update Data Taxonomy Requests."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1DataTaxonomy(
description=args.description,
displayName=args.display_name,
etag=args.etag,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1DataTaxonomy,
args))
def GenerateUpdateMask(args):
"""Create Update Mask for DataTaxonomy."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
return update_mask
def GenerateAttributeUpdateMask(args):
"""Create Update Mask for DataTaxonomy."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('parent'):
update_mask.append('parentId')
if args.IsSpecified('resource_readers'):
update_mask.append('resourceAccessSpec.readers')
if args.IsSpecified('resource_writers'):
update_mask.append('resourceAccessSpec.writers')
if args.IsSpecified('resource_owners'):
update_mask.append('resourceAccessSpec.owners')
if args.IsSpecified('data_readers'):
update_mask.append('dataAccessSpec.readers')
return update_mask
def GenerateAttributeBindingUpdateMask(args):
"""Create Update Mask for DataAttributeBinding."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('resource_attributes'):
update_mask.append('attributes')
if args.IsSpecified('paths'):
update_mask.append('paths')
if args.IsSpecified('path_file_name'):
update_mask.append('paths')
if args.IsSpecified('etag'):
update_mask.append('etag')
return update_mask
def GenerateDataAttributeForCreateRequest(data_attribute_ref, args):
"""Create Data Attribute Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1DataAttribute(
description=args.description,
displayName=args.display_name,
# parentId is parent attribute which is associated
# with the defined attribute.
parentId=ResolveParentId(data_attribute_ref, args),
resourceAccessSpec=GenerateResourceAccessSpec(args),
dataAccessSpec=GenerateDataAccessSpec(args),
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1DataAttribute, args))
return request
def ResolveParentId(data_attribute_ref, args):
if (args.IsSpecified('parent') and args.parent.find('/') == -1):
return data_attribute_ref.RelativeName(
).rsplit('/', 1)[0] + '/' + args.parent
else:
return args.parent
def GenerateDataAttributeForUpdateRequest(data_attribute_ref, args):
"""Generate attributes for Update Data Attribute Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1DataAttribute(
description=args.description,
displayName=args.display_name,
parentId=ResolveParentId(data_attribute_ref, args),
resourceAccessSpec=GenerateResourceAccessSpec(args),
dataAccessSpec=GenerateDataAccessSpec(args),
etag=args.etag,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1DataAttribute, args))
return request
def GenerateAttributeBindingPath(args):
"""Generate Data Attribute Binding Path."""
if args.IsSpecified('path_file_name'):
return GenerateAttributeBindingPathFromFile(
args.path_file_name, args.path_file_format)
else:
return GenerateAttributeBindingPathFromParam(args)
def GenerateAttributeBindingPathFromParam(args):
"""Create Path from specified path parameter."""
module = dataplex_api.GetMessageModule()
attribute_binding_path = []
if args.paths is not None:
for path in args.paths:
attribute_binding_path.append(
module.GoogleCloudDataplexV1DataAttributeBindingPath(
name=path.get('name'),
attributes=path.get('attributes')
)
)
return attribute_binding_path
def GenerateAttributeBindingPathFromFile(path_file_name, path_file_format):
"""Create Path from specified file."""
if not os.path.exists(path_file_name):
raise exceptions.BadFileException('No such file [{0}]'.format(
path_file_name))
if os.path.isdir(path_file_name):
raise exceptions.BadFileException('[{0}] is a directory'.format(
path_file_name))
try:
with files.FileReader(path_file_name) as import_file:
return ConvertPathFileToProto(import_file, path_file_format)
except Exception as exp:
exp_msg = getattr(exp, 'message', six.text_type(exp))
msg = ('Unable to read Path config from specified file '
'[{0}] because [{1}]'.format(path_file_name, exp_msg))
raise exceptions.BadFileException(msg)
def ConvertPathFileToProto(path_file_path, path_file_format):
"""Construct a DataAttributeBindingPath from a JSON/YAML formatted file.
Args:
path_file_path: Path to the JSON or YAML file.
path_file_format: Format for the file provided.
If file format will not be provided by default it will be json.
Returns:
a protorpc.Message of type GoogleCloudDataplexV1DataAttributeBindingPath
filled in from the JSON or YAML path file.
Raises:
BadFileException if the JSON or YAML file is malformed.
"""
if path_file_format == 'yaml':
parsed_path = yaml.load(path_file_path)
else:
try:
parsed_path = json.load(path_file_path)
except ValueError as e:
raise exceptions.BadFileException('Error parsing JSON: {0}'.format(
six.text_type(e)))
path_message = dataplex_api.GetMessageModule(
).GoogleCloudDataplexV1DataAttributeBindingPath
attribute_binding_path = []
for path in parsed_path['paths']:
attribute_binding_path.append(encoding.PyValueToMessage(path_message, path))
return attribute_binding_path
def GenerateDataAttributeBindingForCreateRequest(args):
"""Create Data Attribute Binding Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1DataAttributeBinding(
description=args.description,
displayName=args.display_name,
resource=args.resource,
attributes=args.resource_attributes,
paths=GenerateAttributeBindingPath(args),
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1DataAttributeBinding, args))
return request
def GenerateDataAttributeBindingForUpdateRequest(args):
"""Update Data Attribute Binding Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1DataAttributeBinding(
description=args.description,
displayName=args.display_name,
etag=args.etag,
attributes=args.resource_attributes,
paths=GenerateAttributeBindingPath(args),
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1DataAttributeBinding, args))
return request
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_dataTaxonomies)
def DataTaxonomySetIamPolicy(taxonomy_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsDataTaxonomiesSetIamPolicyRequest(
resource=taxonomy_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance(
).projects_locations_dataTaxonomies.SetIamPolicy(set_iam_policy_req)
def DataTaxonomyGetIamPolicy(taxonomy_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsDataTaxonomiesGetIamPolicyRequest(
resource=taxonomy_ref.RelativeName())
return dataplex_api.GetClientInstance(
).projects_locations_dataTaxonomies.GetIamPolicy(get_iam_policy_req)
def DataTaxonomyAddIamPolicyBinding(taxonomy_ref, member, role):
"""Add IAM policy binding request."""
policy = DataTaxonomyGetIamPolicy(taxonomy_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return DataTaxonomySetIamPolicy(taxonomy_ref, policy)
def DataTaxonomyRemoveIamPolicyBinding(taxonomy_ref, member, role):
"""Remove IAM policy binding request."""
policy = DataTaxonomyGetIamPolicy(taxonomy_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return DataTaxonomySetIamPolicy(taxonomy_ref, policy)
def DataTaxonomySetIamPolicyFromFile(taxonomy_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return DataTaxonomySetIamPolicy(taxonomy_ref, policy)
def DataAttributeSetIamPolicy(data_attribute_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsDataTaxonomiesAttributesSetIamPolicyRequest(
resource=data_attribute_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance(
).projects_locations_dataTaxonomies_attributes.SetIamPolicy(
set_iam_policy_req)
def DataAttributeGetIamPolicy(data_attribute_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsDataTaxonomiesAttributesGetIamPolicyRequest(
resource=data_attribute_ref.RelativeName())
return dataplex_api.GetClientInstance(
).projects_locations_dataTaxonomies_attributes.GetIamPolicy(
get_iam_policy_req)
def DataAttributeAddIamPolicyBinding(data_attribute_ref, member, role):
"""Add IAM policy binding request."""
policy = DataAttributeGetIamPolicy(data_attribute_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return DataAttributeSetIamPolicy(data_attribute_ref, policy)
def DataAttributeRemoveIamPolicyBinding(data_attribute_ref, member, role):
"""Remove IAM policy binding request."""
policy = DataAttributeGetIamPolicy(data_attribute_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return DataAttributeSetIamPolicy(data_attribute_ref, policy)
def DataAttributeSetIamPolicyFromFile(data_attribute_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return DataAttributeSetIamPolicy(data_attribute_ref, policy)
def DataAttributeBindingSetIamPolicy(attribute_binding_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsDataAttributeBindingsSetIamPolicyRequest(
resource=attribute_binding_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance(
).projects_locations_dataAttributeBindings.SetIamPolicy(set_iam_policy_req)
def DataAttributeBindingGetIamPolicy(attribute_binding_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsDataAttributeBindingsGetIamPolicyRequest(
resource=attribute_binding_ref.RelativeName())
return dataplex_api.GetClientInstance(
).projects_locations_dataAttributeBindings.GetIamPolicy(get_iam_policy_req)
def DataAttributeBindingAddIamPolicyBinding(
attribute_binding_ref, member, role):
"""Add IAM policy binding request."""
policy = DataAttributeGetIamPolicy(attribute_binding_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return DataAttributeSetIamPolicy(attribute_binding_ref, policy)
def DataAttributeBindingRemoveIamPolicyBinding(
attribute_binding_ref, member, role):
"""Remove IAM policy binding request."""
policy = DataAttributeGetIamPolicy(attribute_binding_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return DataAttributeSetIamPolicy(attribute_binding_ref, policy)
def DataAttributeBindingSetIamPolicyFromFile(
attribute_binding_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return DataAttributeSetIamPolicy(attribute_binding_ref, policy)

View File

@@ -0,0 +1,411 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 Google Inc. 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.
"""Client for interaction with Datascan API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.api_lib.util import messages as messages_util
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.generated_clients.apis.dataplex.v1 import dataplex_v1_messages
def GenerateData(args: parser_extensions.Namespace):
"""Generate Data From Arguments."""
module = dataplex_api.GetMessageModule()
if args.IsKnownAndSpecified('data_source_entity'):
data = module.GoogleCloudDataplexV1DataSource(
entity=args.data_source_entity
)
else:
data = module.GoogleCloudDataplexV1DataSource(
resource=args.data_source_resource
)
return data
def GenerateDataQualitySpec(args):
"""Generate DataQualitySpec From Arguments."""
module = dataplex_api.GetMessageModule()
if args.IsSpecified('data_quality_spec_file'):
dataqualityspec = dataplex_api.ReadObject(args.data_quality_spec_file)
if dataqualityspec is not None:
dataqualityspec = messages_util.DictToMessageWithErrorCheck(
dataplex_api.SnakeToCamelDict(dataqualityspec),
module.GoogleCloudDataplexV1DataQualitySpec,
)
else:
dataqualityspec = module.GoogleCloudDataplexV1DataQualitySpec()
return dataqualityspec
def GenerateDataProfileSpec(args: parser_extensions.Namespace):
"""Generate DataProfileSpec From Arguments."""
module = dataplex_api.GetMessageModule()
if args.IsSpecified('data_profile_spec_file'):
dataprofilespec = dataplex_api.ReadObject(args.data_profile_spec_file)
if dataprofilespec is not None:
dataprofilespec = messages_util.DictToMessageWithErrorCheck(
dataplex_api.SnakeToCamelDict(dataprofilespec),
module.GoogleCloudDataplexV1DataProfileSpec,
)
else:
exclude_fields, include_fields, sampling_percent, row_filter = [None] * 4
if args.IsKnownAndSpecified('exclude_field_names'):
exclude_fields = (
module.GoogleCloudDataplexV1DataProfileSpecSelectedFields(
fieldNames=list(
val.strip() for val in args.exclude_field_names.split(',')
)
)
)
if args.IsKnownAndSpecified('include_field_names'):
include_fields = (
module.GoogleCloudDataplexV1DataProfileSpecSelectedFields(
fieldNames=list(
val.strip() for val in args.include_field_names.split(',')
)
)
)
if args.IsKnownAndSpecified('sampling_percent'):
sampling_percent = float(args.sampling_percent)
if args.IsKnownAndSpecified('row_filter'):
row_filter = args.row_filter
dataprofilespec = module.GoogleCloudDataplexV1DataProfileSpec(
excludeFields=exclude_fields,
includeFields=include_fields,
samplingPercent=sampling_percent,
rowFilter=row_filter,
)
if args.IsKnownAndSpecified('export_results_table'):
dataprofilespec.postScanActions = module.GoogleCloudDataplexV1DataProfileSpecPostScanActions(
bigqueryExport=module.GoogleCloudDataplexV1DataProfileSpecPostScanActionsBigQueryExport(
resultsTable=args.export_results_table
)
)
return dataprofilespec
def GenerateDataDiscoverySpec(args: parser_extensions.Namespace):
"""Generate DataDiscoverySpec From Arguments."""
module = dataplex_api.GetMessageModule()
datadiscoveryspec = module.GoogleCloudDataplexV1DataDiscoverySpec()
# BigQuery publishing config.
datadiscoveryspec.bigqueryPublishingConfig = (
module.GoogleCloudDataplexV1DataDiscoverySpecBigQueryPublishingConfig()
)
if args.IsKnownAndSpecified('bigquery_publishing_connection'):
datadiscoveryspec.bigqueryPublishingConfig.connection = (
args.bigquery_publishing_connection
)
if args.IsKnownAndSpecified('bigquery_publishing_table_type'):
datadiscoveryspec.bigqueryPublishingConfig.tableType = module.GoogleCloudDataplexV1DataDiscoverySpecBigQueryPublishingConfig.TableTypeValueValuesEnum(
args.bigquery_publishing_table_type
)
if args.IsKnownAndSpecified('bigquery_publishing_dataset_project'):
datadiscoveryspec.bigqueryPublishingConfig.project = (
args.bigquery_publishing_dataset_project
)
if args.IsKnownAndSpecified('bigquery_publishing_dataset_location'):
datadiscoveryspec.bigqueryPublishingConfig.location = (
args.bigquery_publishing_dataset_location
)
datadiscoveryspec.storageConfig = (
module.GoogleCloudDataplexV1DataDiscoverySpecStorageConfig()
)
if args.IsKnownAndSpecified('storage_include_patterns'):
datadiscoveryspec.storageConfig.includePatterns = (
args.storage_include_patterns
)
if args.IsKnownAndSpecified('storage_exclude_patterns'):
datadiscoveryspec.storageConfig.excludePatterns = (
args.storage_exclude_patterns
)
# CSV options.
datadiscoveryspec.storageConfig.csvOptions = (
module.GoogleCloudDataplexV1DataDiscoverySpecStorageConfigCsvOptions()
)
if args.IsKnownAndSpecified('csv_delimiter'):
datadiscoveryspec.storageConfig.csvOptions.delimiter = args.csv_delimiter
if args.IsKnownAndSpecified('csv_header_row_count'):
try:
datadiscoveryspec.storageConfig.csvOptions.headerRows = int(
args.csv_header_row_count
)
except ValueError:
raise ValueError(
'csv_header_row_count must be an integer, but got'
f' {args.csv_header_row_count}'
)
if args.IsKnownAndSpecified('csv_quote_character'):
datadiscoveryspec.storageConfig.csvOptions.quote = args.csv_quote_character
if args.IsKnownAndSpecified('csv_encoding'):
datadiscoveryspec.storageConfig.csvOptions.encoding = args.csv_encoding
if args.IsKnownAndSpecified('csv_disable_type_inference'):
datadiscoveryspec.storageConfig.csvOptions.typeInferenceDisabled = (
args.csv_disable_type_inference
)
# JSON options.
datadiscoveryspec.storageConfig.jsonOptions = (
module.GoogleCloudDataplexV1DataDiscoverySpecStorageConfigJsonOptions()
)
if args.IsKnownAndSpecified('json_encoding'):
datadiscoveryspec.storageConfig.jsonOptions.encoding = args.json_encoding
if args.IsKnownAndSpecified('json_disable_type_inference'):
datadiscoveryspec.storageConfig.jsonOptions.typeInferenceDisabled = (
args.json_disable_type_inference
)
return datadiscoveryspec
def GenerateDataDocumentationSpec():
"""Generate DataDocumentationSpec From Arguments."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1DataDocumentationSpec()
def GenerateSchedule(args):
"""Generate DataQualitySpec From Arguments."""
module = dataplex_api.GetMessageModule()
schedule = module.GoogleCloudDataplexV1TriggerSchedule(cron=args.schedule)
return schedule
def GenerateTrigger(
args: parser_extensions.Namespace,
) -> dataplex_v1_messages.GoogleCloudDataplexV1Trigger:
"""Generates Trigger for data scan From Arguments.
Args:
args: The arguments of the command.
Returns:
The trigger for the data scan.
"""
module = dataplex_api.GetMessageModule()
trigger = module.GoogleCloudDataplexV1Trigger()
is_one_time_scan = args.IsKnownAndSpecified('one_time') and args.one_time
is_ttl_after_scan_completion = args.IsKnownAndSpecified(
'ttl_after_scan_completion'
)
if is_ttl_after_scan_completion and not is_one_time_scan:
raise ValueError(
'ttl_after_scan_completion is only supported for one-time scans.'
' Provide --one-time to enable one-time scan.'
)
if args.IsKnownAndSpecified('schedule'):
trigger.schedule = GenerateSchedule(args)
elif is_one_time_scan:
trigger.oneTime = module.GoogleCloudDataplexV1TriggerOneTime()
if is_ttl_after_scan_completion:
trigger.oneTime.ttlAfterScanCompletion = args.ttl_after_scan_completion
else:
trigger.onDemand = module.GoogleCloudDataplexV1TriggerOnDemand()
return trigger
def GenerateExecutionSpecForCreateRequest(args):
"""Generate ExecutionSpec From Arguments."""
module = dataplex_api.GetMessageModule()
if hasattr(args, 'field'):
field = args.field
else:
field = (
args.incremental_field if hasattr(args, 'incremental_field') else None
)
executionspec = module.GoogleCloudDataplexV1DataScanExecutionSpec(
field=field,
trigger=GenerateTrigger(args),
)
return executionspec
def GenerateExecutionSpecForUpdateRequest(args):
"""Generate ExecutionSpec From Arguments."""
module = dataplex_api.GetMessageModule()
executionspec = module.GoogleCloudDataplexV1DataScanExecutionSpec(
trigger=GenerateTrigger(args),
)
return executionspec
def GenerateUpdateMask(args: parser_extensions.Namespace):
"""Create Update Mask for Datascan."""
update_mask = []
args_to_mask = {
'description': 'description',
'display_name': 'displayName',
'labels': 'labels',
'on_demand': 'executionSpec.trigger.onDemand',
'schedule': 'executionSpec.trigger.schedule',
}
args_to_mask_attr = {
'data_profile_spec_file': 'dataProfileSpec',
'data_quality_spec_file': 'dataQualitySpec',
'row_filter': 'dataProfileSpec.rowFilter',
'sampling_percent': 'dataProfileSpec.samplingPercent',
'include_field_names': 'dataProfileSpec.includeFields',
'exclude_field_names': 'dataProfileSpec.excludeFields',
'bigquery_publishing_table_type': (
'dataDiscoverySpec.bigqueryPublishingConfig.tableType'
),
'bigquery_publishing_connection': (
'dataDiscoverySpec.bigqueryPublishingConfig.connection'
),
'bigquery_publishing_dataset_location': (
'dataDiscoverySpec.bigqueryPublishingConfig.location'
),
'bigquery_publishing_dataset_project': (
'dataDiscoverySpec.bigqueryPublishingConfig.project'
),
'storage_include_patterns': (
'dataDiscoverySpec.storageConfig.includePatterns'
),
'storage_exclude_patterns': (
'dataDiscoverySpec.storageConfig.excludePatterns'
),
'csv_delimiter': 'dataDiscoverySpec.storageConfig.csvOptions.delimiter',
'csv_header_row_count': (
'dataDiscoverySpec.storageConfig.csvOptions.headerRows'
),
'csv_quote_character': 'dataDiscoverySpec.storageConfig.csvOptions.quote',
'csv_encoding': 'dataDiscoverySpec.storageConfig.csvOptions.encoding',
'csv_disable_type_inference': (
'dataDiscoverySpec.storageConfig.csvOptions.typeInferenceDisabled'
),
'json_encoding': 'dataDiscoverySpec.storageConfig.jsonOptions.encoding',
'json_disable_type_inference': (
'dataDiscoverySpec.storageConfig.jsonOptions.typeInferenceDisabled'
),
}
for arg, val in args_to_mask.items():
if args.IsSpecified(arg):
update_mask.append(val)
for arg, val in args_to_mask_attr.items():
if args.IsKnownAndSpecified(arg):
update_mask.append(val)
return update_mask
def GenerateDatascanForCreateRequest(args: parser_extensions.Namespace):
"""Create Datascan for Message Create Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1DataScan(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1DataScan, args
),
data=GenerateData(args),
executionSpec=GenerateExecutionSpecForCreateRequest(args),
)
if args.scan_type == 'PROFILE':
if args.IsKnownAndSpecified('data_quality_spec_file'):
raise ValueError(
'Data quality spec file specified for data profile scan.'
)
else:
request.dataProfileSpec = GenerateDataProfileSpec(args)
elif args.scan_type == 'QUALITY':
if args.IsKnownAndSpecified('data_profile_spec_file'):
raise ValueError(
'Data profile spec file specified for data quality scan.'
)
elif args.IsSpecified('data_quality_spec_file'):
request.dataQualitySpec = GenerateDataQualitySpec(args)
else:
raise ValueError(
'If scan-type="QUALITY" , data-quality-spec-file is a required'
' argument.'
)
elif args.scan_type == 'DISCOVERY':
request.dataDiscoverySpec = GenerateDataDiscoverySpec(args)
elif args.scan_type == 'DOCUMENTATION':
request.dataDocumentationSpec = GenerateDataDocumentationSpec()
return request
def GenerateDatascanForUpdateRequest(args: parser_extensions.Namespace):
"""Create Datascan for Message Update Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1DataScan(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1DataScan, args
),
executionSpec=GenerateExecutionSpecForUpdateRequest(args),
)
if args.scan_type == 'PROFILE':
if args.IsKnownAndSpecified('data_quality_spec_file'):
raise ValueError(
'Data quality spec file specified for data profile scan.'
)
request.dataProfileSpec = GenerateDataProfileSpec(args)
elif args.scan_type == 'QUALITY':
if args.IsKnownAndSpecified('data_profile_spec_file'):
raise ValueError(
'Data profile spec file specified for data quality scan.'
)
elif args.IsSpecified('data_quality_spec_file'):
request.dataQualitySpec = GenerateDataQualitySpec(args)
else:
request.dataQualitySpec = module.GoogleCloudDataplexV1DataQualitySpec()
elif args.scan_type == 'DISCOVERY':
request.dataDiscoverySpec = GenerateDataDiscoverySpec(args)
elif args.scan_type == 'DOCUMENTATION':
request.dataDocumentationSpec = GenerateDataDocumentationSpec()
return request
def SetIamPolicy(datascan_ref, policy):
"""Set IAM Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule().DataplexProjectsLocationsDataScansSetIamPolicyRequest(
resource=datascan_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule().GoogleIamV1SetIamPolicyRequest(
policy=policy
),
)
return dataplex_api.GetClientInstance().projects_locations_dataScans.SetIamPolicy(
set_iam_policy_req
)
def SetIamPolicyFromFile(datascan_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file, dataplex_api.GetMessageModule().GoogleIamV1Policy
)
return SetIamPolicy(datascan_ref, policy)
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation, dataplex_api.GetClientInstance().projects_locations_dataScans
)

View File

@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*- #
# Copyright 2025 Google Inc. 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.
"""Client for interaction with EncryptionConfig API CRUD DATAPLEX."""
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
def GenerateEncryptionConfigForCreateRequest(args):
"""Create EncryptionConfig Request."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1EncryptionConfig(
name='organizations/{0}/locations/{1}/encryptionConfigs/{2}'.format(
args.organization, args.location, args.encryption_config
),
key=args.key,
)
return request
def GenerateUpdateMask(args):
"""Generates update mask for EncryptionConfig."""
update_mask = []
if args.IsSpecified('enable_metastore_encryption'):
update_mask.append('enableMetastoreEncryption')
return update_mask
def GenerateEncryptionConfigForUpdateRequest(args):
"""Update EncryptionConfig Request."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1EncryptionConfig(
name='organizations/{0}/locations/{1}/encryptionConfigs/{2}'.format(
args.organization, args.location, args.encryption_config
),
enableMetastoreEncryption=args.enable_metastore_encryption,
)
return request

View File

@@ -0,0 +1,230 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 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.
"""Client for interaction with Entries API CRUD DATAPLEX."""
from __future__ import annotations
from typing import Any, Dict, List
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.calliope import parser_extensions
from googlecloudsdk.command_lib.dataplex import parsers as dataplex_parsers
from googlecloudsdk.command_lib.util.args import labels_util
from googlecloudsdk.core import log
dataplex_message = dataplex_api.GetMessageModule()
def _GetFieldsForUpdateMask(args: parser_extensions.Namespace) -> List[str]:
"""Create a sorted list of fields to be used in update_mask for Entry based on arguments provided to the command."""
# Map command arguments to the API proto fields.
arg_name_to_field = {
'--fully-qualified-name': 'fully_qualified_name',
'--update-aspects': 'aspects', # For update command
'--remove-aspects': 'aspects', # For update command
'--aspects': 'aspects', # For update-aspects command
'--keys': 'aspects', # For remove-aspects command
'--entry-source-resource': 'entry_source.resource',
'--entry-source-system': 'entry_source.system',
'--entry-source-platform': 'entry_source.platform',
'--entry-source-display-name': 'entry_source.display_name',
'--entry-source-description': 'entry_source.description',
'--entry-source-labels': 'entry_source.labels',
'--entry-source-create-time': 'entry_source.create_time',
'--entry-source-update-time': 'entry_source.update_time',
}
# Remove `--clear-` part from command argument names, as those args are meant
# to clear the respective API proto fields. Select only those that were
# supplied to the command by performing a set intersection.
args_cleaned = set(
map(
lambda arg: arg.replace('--clear-', '--'), args.GetSpecifiedArgNames()
)
)
updatable_args = args_cleaned.intersection(arg_name_to_field)
return sorted(
set(map(lambda arg_name: arg_name_to_field[arg_name], updatable_args))
)
def _GenerateAspectKeys(
args: parser_extensions.Namespace,
*,
remove_aspects_arg_name: str,
update_aspects_arg_name: str,
) -> List[str]:
"""Generate a list of unique aspect keys to be updated or removed.
This will be used along with the update_mask for updating an Entry. This list
is populated based on `--update-aspects` and `--remove-aspects` arguments
(or `--aspects` in case of specialized command like `update-aspects`).
Args:
args: The arguments provided to the command.
remove_aspects_arg_name: The name of the argument that contains the aspect
keys to be removed.
update_aspects_arg_name: The name of the argument that contains aspect
contents to be added or updated.
Returns:
A sorted list of unique aspect keys to be updated or removed. Or empty list
if neither `--update-aspects`, `--remove-aspects` or `--aspects` are
provided to the command.
"""
keys = set()
if args.IsKnownAndSpecified(update_aspects_arg_name):
keys.update(
map(
lambda aspect: aspect.key,
args.GetValue(update_aspects_arg_name).additionalProperties,
)
)
if args.IsKnownAndSpecified(remove_aspects_arg_name):
keys.update(args.GetValue(remove_aspects_arg_name))
return sorted(keys)
def _GetArgValueOrNone(
args: parser_extensions.Namespace, arg_name: str
) -> Any | None:
return args.GetValue(arg_name) if args.IsKnownAndSpecified(arg_name) else None
def _GetEntrySourceLabels(
args: parser_extensions.Namespace,
) -> Dict[str, str] | None:
"""Parse EntrySource labels from the command arguments if defined."""
if not args.IsKnownAndSpecified('entry_source_labels'):
return None
return labels_util.ParseCreateArgs(
args,
labels_cls=dataplex_message.GoogleCloudDataplexV1EntrySource.LabelsValue,
labels_dest='entry_source_labels',
)
def _GetEntrySourceAncestors(
args: parser_extensions.Namespace,
) -> List[Any]:
"""Parse EntrySource ancestors from the command arguments if defined."""
if not args.IsKnownAndSpecified('entry_source_ancestors'):
return []
return dataplex_parsers.ParseEntrySourceAncestors(args.entry_source_ancestors)
def _GetEntrySourceOrNone(
args: parser_extensions.Namespace,
) -> dataplex_message.GoogleCloudDataplexV1EntrySource | None:
"""Parse EntrySource from the command arguments if defined."""
entry_source = dataplex_message.GoogleCloudDataplexV1EntrySource(
resource=_GetArgValueOrNone(args, 'entry_source_resource'),
system=_GetArgValueOrNone(args, 'entry_source_system'),
platform=_GetArgValueOrNone(args, 'entry_source_platform'),
displayName=_GetArgValueOrNone(args, 'entry_source_display_name'),
description=_GetArgValueOrNone(args, 'entry_source_description'),
labels=_GetEntrySourceLabels(args),
ancestors=_GetEntrySourceAncestors(args),
createTime=_GetArgValueOrNone(args, 'entry_source_create_time'),
updateTime=_GetArgValueOrNone(args, 'entry_source_update_time'),
)
return None if not entry_source else entry_source
def Create(args: parser_extensions.Namespace):
"""Create a CreateEntry request based on arguments provided."""
entry_ref = args.CONCEPTS.entry.Parse()
entry_type_ref = args.CONCEPTS.entry_type.Parse()
parent_entry_ref = args.CONCEPTS.parent_entry.Parse()
dataplex_client = dataplex_api.GetClientInstance()
parent_entry_name = ''
if parent_entry_ref is not None:
parent_entry_name = parent_entry_ref.RelativeName()
resource = dataplex_client.projects_locations_entryGroups_entries.Create(
dataplex_message.DataplexProjectsLocationsEntryGroupsEntriesCreateRequest(
entryId=entry_ref.Name(),
googleCloudDataplexV1Entry=dataplex_message.GoogleCloudDataplexV1Entry(
name=entry_ref.RelativeName(),
entryType=entry_type_ref.RelativeName(),
parentEntry=parent_entry_name,
fullyQualifiedName=_GetArgValueOrNone(
args, 'fully_qualified_name'
),
aspects=_GetArgValueOrNone(args, 'aspects'),
entrySource=_GetEntrySourceOrNone(args),
),
parent=entry_ref.Parent().RelativeName(),
)
)
log.CreatedResource(
entry_ref.Name(),
details='in [{0}]'.format(entry_ref.Parent().RelativeName()),
)
return resource
def Update(
args: parser_extensions.Namespace,
remove_aspects_arg_name: str = 'remove_aspects',
update_aspects_arg_name: str = 'update_aspects',
):
"""Create an UpdateEntry request based on arguments provided."""
update_mask = _GetFieldsForUpdateMask(args)
if len(update_mask) < 1:
raise exceptions.HttpException(
'Update commands must specify at least one additional parameter to'
' change.'
)
entry_ref = args.CONCEPTS.entry.Parse()
dataplex_client = dataplex_api.GetClientInstance()
resource = dataplex_client.projects_locations_entryGroups_entries.Patch(
dataplex_message.DataplexProjectsLocationsEntryGroupsEntriesPatchRequest(
name=entry_ref.RelativeName(),
googleCloudDataplexV1Entry=dataplex_message.GoogleCloudDataplexV1Entry(
name=entry_ref.RelativeName(),
fullyQualifiedName=_GetArgValueOrNone(
args, 'fully_qualified_name'
),
aspects=_GetArgValueOrNone(args, update_aspects_arg_name),
entrySource=_GetEntrySourceOrNone(args),
),
deleteMissingAspects=args.IsKnownAndSpecified(
remove_aspects_arg_name
),
updateMask=','.join(update_mask),
aspectKeys=_GenerateAspectKeys(
args,
remove_aspects_arg_name=remove_aspects_arg_name,
update_aspects_arg_name=update_aspects_arg_name,
),
)
)
log.UpdatedResource(entry_ref.RelativeName(), kind='entry')
return resource

View File

@@ -0,0 +1,107 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google Inc. 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.
"""Client for interaction with EntryGroup API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.command_lib.iam import iam_util
def GenerateEntryGroupForCreateRequest(args):
"""Create Entry Group Request."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1EntryGroup(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1EntryGroup,
args))
return request
def GenerateEntryGroupForUpdateRequest(args):
"""Update Entry Group Request."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1EntryGroup(
description=args.description,
displayName=args.display_name,
etag=args.etag,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1EntryGroup,
args))
def GenerateEntryGroupUpdateMask(args):
"""Create Update Mask for EntryGroup."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
return update_mask
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_entryGroups)
def EntryGroupSetIamPolicy(entry_group_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsEntryGroupsSetIamPolicyRequest(
resource=entry_group_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance(
).projects_locations_entryGroups.SetIamPolicy(set_iam_policy_req)
def EntryGroupGetIamPolicy(entry_group_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsEntryGroupsGetIamPolicyRequest(
resource=entry_group_ref.RelativeName())
return dataplex_api.GetClientInstance(
).projects_locations_entryGroups.GetIamPolicy(get_iam_policy_req)
def EntryGroupAddIamPolicyBinding(entry_group_ref, member, role):
"""Add IAM policy binding request."""
policy = EntryGroupGetIamPolicy(entry_group_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return EntryGroupSetIamPolicy(entry_group_ref, policy)
def EntryGroupRemoveIamPolicyBinding(entry_group_ref, member, role):
"""Remove IAM policy binding request."""
policy = EntryGroupGetIamPolicy(entry_group_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return EntryGroupSetIamPolicy(entry_group_ref, policy)
def EntryGroupSetIamPolicyFromFile(entry_group_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return EntryGroupSetIamPolicy(entry_group_ref, policy)

View File

@@ -0,0 +1,69 @@
# -*- coding: utf-8 -*- #
# Copyright 2025 Google Inc. 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.
"""Utilities for Dataplex Entry Link."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.calliope import exceptions
module = dataplex_api.GetMessageModule()
# Define a mapping between user-friendly strings and API enum values
ENTRY_REFERENCE_TYPE_MAPPING = {
'UNSPECIFIED': (
module.GoogleCloudDataplexV1EntryLinkEntryReference.TypeValueValuesEnum.UNSPECIFIED
),
'SOURCE': (
module.GoogleCloudDataplexV1EntryLinkEntryReference.TypeValueValuesEnum.SOURCE
),
'TARGET': (
module.GoogleCloudDataplexV1EntryLinkEntryReference.TypeValueValuesEnum.TARGET
),
}
def CreateEntryReferences(entry_references_content):
"""Create Entry References."""
entry_references_message = []
if not entry_references_content:
raise exceptions.BadFileException(
'The entry references file is empty.'
)
for entry_reference in entry_references_content:
reference_type_input = entry_reference['type'].upper()
reference_type_enum = ENTRY_REFERENCE_TYPE_MAPPING.get(reference_type_input)
if not reference_type_enum:
raise exceptions.BadFileException(
f'Invalid entry reference type: {reference_type_input}.'
' Valid types are: UNSPECIFIED, SOURCE, TARGET.'
)
entry_reference_message = (
module.GoogleCloudDataplexV1EntryLinkEntryReference(
name=entry_reference['name'],
type=reference_type_enum,
)
)
if 'path' in entry_reference:
entry_reference_message.path = entry_reference['path']
entry_references_message.append(entry_reference_message)
if len(entry_references_message) != 2:
raise exceptions.BadFileException(
'The entry references file must contain exactly two entries.'
)
return entry_references_message

View File

@@ -0,0 +1,138 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google Inc. 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.
"""Client for interaction with EntryType API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.command_lib.iam import iam_util
def GenerateEntryTypeForCreateRequest(args):
"""Create Entry Type Request."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1EntryType(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1EntryType,
args),
platform=args.platform,
system=args.system,
typeAliases=args.type_aliases,
requiredAspects=GenerateEntryTypeRequiredAspects(args))
return request
def GenerateEntryTypeForUpdateRequest(args):
"""Update Entry Type Request."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1EntryType(
description=args.description,
displayName=args.display_name,
etag=args.etag,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1EntryType,
args),
platform=args.platform,
system=args.system,
typeAliases=args.type_aliases,
requiredAspects=GenerateEntryTypeRequiredAspects(args))
def GenerateEntryTypeUpdateMask(args):
"""Create Update Mask for EntryType."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('platform'):
update_mask.append('platform')
if args.IsSpecified('system'):
update_mask.append('system')
if args.IsSpecified('type_aliases'):
update_mask.append('typeAliases')
if args.IsSpecified('required_aspects'):
update_mask.append('requiredAspects')
return update_mask
def GenerateEntryTypeRequiredAspects(args):
"""Create Required Aspects."""
module = dataplex_api.GetMessageModule()
required_aspects = []
if args.required_aspects is not None:
for required_aspect in args.required_aspects:
required_aspects.append(
module.GoogleCloudDataplexV1EntryTypeAspectInfo(
type=required_aspect.get('type')
)
)
return required_aspects
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_entryTypes)
def EntryTypeSetIamPolicy(entry_type_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsEntryTypesSetIamPolicyRequest(
resource=entry_type_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance(
).projects_locations_entryTypes.SetIamPolicy(set_iam_policy_req)
def EntryTypeGetIamPolicy(entry_type_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsEntryTypesGetIamPolicyRequest(
resource=entry_type_ref.RelativeName())
return dataplex_api.GetClientInstance(
).projects_locations_entryTypes.GetIamPolicy(get_iam_policy_req)
def EntryTypeAddIamPolicyBinding(entry_type_ref, member, role):
"""Add IAM policy binding request."""
policy = EntryTypeGetIamPolicy(entry_type_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return EntryTypeSetIamPolicy(entry_type_ref, policy)
def EntryTypeRemoveIamPolicyBinding(entry_type_ref, member, role):
"""Remove IAM policy binding request."""
policy = EntryTypeGetIamPolicy(entry_type_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return EntryTypeSetIamPolicy(entry_type_ref, policy)
def EntryTypeSetIamPolicyFromFile(entry_type_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return EntryTypeSetIamPolicy(entry_type_ref, policy)

View File

@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 Google Inc. 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.
"""Client for interaction with Environment API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.command_lib.iam import iam_util
def SetIamPolicy(environment_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsLakesEnvironmentsSetIamPolicyRequest(
resource=environment_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance(
).projects_locations_lakes_environments.SetIamPolicy(set_iam_policy_req)
def GetIamPolicy(environment_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsLakesEnvironmentsGetIamPolicyRequest(
resource=environment_ref.RelativeName())
return dataplex_api.GetClientInstance(
).projects_locations_lakes_environments.GetIamPolicy(get_iam_policy_req)
def AddIamPolicyBinding(environment_ref, member, role):
"""Add IAM policy binding request."""
policy = GetIamPolicy(environment_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return SetIamPolicy(environment_ref, policy)
def RemoveIamPolicyBinding(environment_ref, member, role):
"""Remove IAM policy binding request."""
policy = GetIamPolicy(environment_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return SetIamPolicy(environment_ref, policy)
def SetIamPolicyFromFile(environment_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return SetIamPolicy(environment_ref, policy)
def GenerateInfrastructureSpec(args):
"""Generate InfrastructureSpec From Arguments."""
module = dataplex_api.GetMessageModule()
compute_resource = module.GoogleCloudDataplexV1EnvironmentInfrastructureSpecComputeResources(
diskSizeGb=args.compute_disk_size_gb,
nodeCount=args.compute_node_count,
maxNodeCount=args.compute_max_node_count)
os_image_runtime = module.GoogleCloudDataplexV1EnvironmentInfrastructureSpecOsImageRuntime(
imageVersion=args.os_image_version,
javaLibraries=args.os_image_java_libraries,
pythonPackages=args.os_image_python_packages,
properties=args.os_image_properties)
infrastructure_spec = module.GoogleCloudDataplexV1EnvironmentInfrastructureSpec(
compute=compute_resource, osImage=os_image_runtime)
return infrastructure_spec
def GenerateSessionSpec(args):
"""Generate SessionSpec From Arguments."""
module = dataplex_api.GetMessageModule()
session_spec = module.GoogleCloudDataplexV1EnvironmentSessionSpec(
enableFastStartup=args.session_enable_fast_startup,
maxIdleDuration=args.session_max_idle_duration)
return session_spec
def GenerateEnvironmentForCreateRequest(args):
"""Create Environment for Message Create Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1Environment(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1Environment,
args),
infrastructureSpec=GenerateInfrastructureSpec(args),
sessionSpec=GenerateSessionSpec(args))
return request
def GenerateEnvironmentForUpdateRequest(args):
"""Create Environment for Message Update Requests."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1Environment(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1Environment,
args),
infrastructureSpec=GenerateInfrastructureSpec(args),
sessionSpec=GenerateSessionSpec(args))
def GenerateUpdateMask(args):
"""Create Update Mask for Environments."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('compute_disk_size_gb'):
update_mask.append('infrastructureSpec.compute.diskSizeGb')
if args.IsSpecified('compute_node_count'):
update_mask.append('infrastructureSpec.compute.nodeCount')
if args.IsSpecified('compute_max_node_count'):
update_mask.append('infrastructureSpec.compute.maxNodeCount')
if args.IsSpecified('os_image_version'):
update_mask.append('infrastructureSpec.osImage.imageVersion')
if args.IsSpecified('os_image_java_libraries'):
update_mask.append('infrastructureSpec.osImage.javaLibraries')
if args.IsSpecified('os_image_python_packages'):
update_mask.append('infrastructureSpec.osImage.pythonPackages')
if args.IsSpecified('os_image_properties'):
update_mask.append('infrastructureSpec.osImage.properties')
if args.IsSpecified('session_max_idle_duration'):
update_mask.append('sessionSpec.maxIdleDuration')
if args.IsSpecified('session_enable_fast_startup'):
update_mask.append('sessionSpec.enableFastStartup')
return update_mask
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_lakes_environments)

View File

@@ -0,0 +1,197 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google Inc. 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.
"""Client for interaction with Glossary API CRUD DATAPLEX."""
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.command_lib.iam import iam_util
def GenerateGlossaryForCreateRequest(args):
"""Create Glossary Request."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1Glossary(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1Glossary, args
),
)
return request
def GenerateGlossaryForUpdateRequest(args):
"""Update Glossary Request."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1Glossary(
description=args.description,
displayName=args.display_name,
etag=args.etag,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1Glossary, args
),
)
def GenerateGlossaryCategoryForCreateRequest(args):
"""Create Glossary Category Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1GlossaryCategory(
description=args.description,
displayName=args.display_name,
# parent represents the immediate parent of the category in glossary
# hierarchy.
parent=args.parent,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1GlossaryCategory, args
),
)
return request
def GenerateGlossaryCategoryForUpdateRequest(args):
"""Update Glossary Category Requests."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1GlossaryCategory(
description=args.description,
displayName=args.display_name,
parent=args.parent,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1GlossaryCategory, args
),
)
def GenerateGlossaryTermForCreateRequest(args):
"""Create Glossary Term Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1GlossaryTerm(
description=args.description,
displayName=args.display_name,
# parent represents the immediate parent of the term in glossary
# hierarchy.
parent=args.parent,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1GlossaryTerm, args
),
)
return request
def GenerateGlossaryTermForUpdateRequest(args):
"""Update Glossary Term Requests."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1GlossaryTerm(
description=args.description,
displayName=args.display_name,
parent=args.parent,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1GlossaryTerm, args
),
)
def GenerateUpdateMask(args):
"""Creates Update Mask for Glossary."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
return update_mask
def GenerateCategoryUpdateMask(args):
"""Create Update Mask for Glossary Category."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('parent'):
update_mask.append('parent')
return update_mask
def GenerateTermUpdateMask(args):
"""Create Update Mask for Glossary Term."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('parent'):
update_mask.append('parent')
return update_mask
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation, dataplex_api.GetClientInstance().projects_locations_glossaries
)
def GlossarySetIamPolicy(glossary_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule().DataplexProjectsLocationsGlossariesSetIamPolicyRequest(
resource=glossary_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule().GoogleIamV1SetIamPolicyRequest(
policy=policy
),
)
return dataplex_api.GetClientInstance().projects_locations_glossaries.SetIamPolicy(
set_iam_policy_req
)
def GlossaryGetIamPolicy(glossary_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule().DataplexProjectsLocationsGlossariesGetIamPolicyRequest(
resource=glossary_ref.RelativeName()
)
return dataplex_api.GetClientInstance().projects_locations_glossaries.GetIamPolicy(
get_iam_policy_req
)
def GlossaryAddIamPolicyBinding(glossary_ref, member, role):
"""Add IAM policy binding request."""
policy = GlossaryGetIamPolicy(glossary_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role
)
return GlossarySetIamPolicy(glossary_ref, policy)
def GlossaryRemoveIamPolicyBinding(glossary_ref, member, role):
"""Remove IAM policy binding request."""
policy = GlossaryGetIamPolicy(glossary_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return GlossarySetIamPolicy(glossary_ref, policy)
def GlossarySetIamPolicyFromFile(glossary_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file, dataplex_api.GetMessageModule().GoogleIamV1Policy
)
return GlossarySetIamPolicy(glossary_ref, policy)

View File

@@ -0,0 +1,251 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google Inc. 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.
"""Client for interaction with Governance Rules API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import os
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.api_lib.util import messages as messages_util
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.command_lib.iam import iam_util
from googlecloudsdk.core import yaml
from googlecloudsdk.core.util import files
import six
def GenerateGovernanceRuleForCreateRequest(args):
"""Create Governance Rule Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1GovernanceRule(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1GovernanceRule, args
),
query=GenerateGovernanceRuleQuery(args),
specs=GenerateGovernanceRuleSpecs(args),
fields=GenerateGovernanceRuleFields(args),
)
if args.IsSpecified('rule_metadata_file'):
rule_metadata_file = ReadRuleMetadataFile(args)
if rule_metadata_file is None:
raise ValueError(
'Rule metadata file is empty for Governance Rules create request.'
)
if rule_metadata_file.get('query') is None:
raise ValueError(
'Query should be provided for Governance Rules create request.'
)
if GenerateGovernanceRuleSpecs(
args
) is None and not GenerateGovernanceRuleFields(args):
raise ValueError(
'Either specs or field should be provided for Governance Rules create'
' request.'
)
else:
raise ValueError(
'Rule metadata file is not specified for Governance Rules create'
' request.'
)
return request
def GenerateGovernanceRuleForUpdateRequest(args):
"""Update Governance Rule Requests."""
module = dataplex_api.GetMessageModule()
request = module.GoogleCloudDataplexV1GovernanceRule(
description=args.description,
displayName=args.display_name,
etag=args.etag,
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1GovernanceRule, args
),
)
if (
args.IsSpecified('rule_metadata_file')
and args.rule_metadata_file is not None
):
query = GenerateGovernanceRuleQuery(args)
specs = GenerateGovernanceRuleSpecs(args)
fields = GenerateGovernanceRuleFields(args)
if query is not None:
request.query = query
if specs is not None:
request.specs = specs
if fields:
request.fields = fields
return request
def ReadRuleMetadataFile(args):
"""Read Rule Metadata File."""
if not os.path.exists(args.rule_metadata_file):
raise exceptions.BadFileException(
'No such file [{0}]'.format(args.rule_metadata_file)
)
if os.path.isdir(args.rule_metadata_file):
raise exceptions.BadFileException(
'[{0}] is a directory'.format(args.rule_metadata_file)
)
try:
with files.FileReader(args.rule_metadata_file) as import_file:
return yaml.load(import_file)
except Exception as exp:
exp_msg = getattr(exp, 'message', six.text_type(exp))
msg = (
'Unable to read Rule Metadata config from specified file '
'[{0}] because [{1}]'.format(args.rule_metadata_file, exp_msg)
)
raise exceptions.BadFileException(msg)
def GenerateGovernanceRuleQuery(args):
"""Generate Governance Rule Query From Rule Metadata File."""
module = dataplex_api.GetMessageModule()
governance_rule_query = module.GoogleCloudDataplexV1GovernanceRuleQuery()
rule_metadata_file = ReadRuleMetadataFile(args)
if (
rule_metadata_file is not None
and rule_metadata_file.get('query') is not None
):
governance_rule_query = messages_util.DictToMessageWithErrorCheck(
dataplex_api.SnakeToCamelDict(rule_metadata_file.get('query')),
module.GoogleCloudDataplexV1GovernanceRuleQuery,
True,
)
return governance_rule_query
def GenerateGovernanceRuleSpecs(args):
"""Generate Governance Rule Specs From Rule Metadata File."""
module = dataplex_api.GetMessageModule()
governance_rule_specs = None
rule_metadata_file = ReadRuleMetadataFile(args)
if (
rule_metadata_file is not None
and rule_metadata_file.get('specs') is not None
):
governance_rule_specs = messages_util.DictToMessageWithErrorCheck(
dataplex_api.SnakeToCamelDict(rule_metadata_file.get('specs')),
module.GoogleCloudDataplexV1GovernanceRuleSpecs,
)
return governance_rule_specs
def GenerateGovernanceRuleFields(args):
"""Generate Governance Rule Fields From Rule Metadata File."""
module = dataplex_api.GetMessageModule()
governance_rule_fields = []
rule_metadata_file = ReadRuleMetadataFile(args)
if (
rule_metadata_file is not None
and rule_metadata_file.get('fields') is not None
):
fields = rule_metadata_file.get('fields')
for field in fields:
governance_rule_fields.append(
messages_util.DictToMessageWithErrorCheck(
dataplex_api.SnakeToCamelDict(field),
module.GoogleCloudDataplexV1GovernanceRuleField,
)
)
return governance_rule_fields
def GenerateUpdateMask(args):
"""Create Update Mask for Governance Rule."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('rule_metadata_file'):
if args.rule_metadata_file is not None:
if GenerateGovernanceRuleQuery(args) is not None:
update_mask.append('query')
if GenerateGovernanceRuleSpecs(args) is not None:
update_mask.append('specs')
if GenerateGovernanceRuleFields(args):
update_mask.append('fields')
return update_mask
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_governanceRules,
)
def SetIamPolicy(governance_rule_ref, policy):
"""Set Iam Policy request."""
google_iam_v1_set_iam_policy_request = (
dataplex_api.GetMessageModule().GoogleIamV1SetIamPolicyRequest(
policy=policy
)
)
set_iam_policy_req = dataplex_api.GetMessageModule().DataplexProjectsLocationsGovernanceRulesSetIamPolicyRequest(
resource=governance_rule_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=google_iam_v1_set_iam_policy_request,
)
return dataplex_api.GetClientInstance().projects_locations_governanceRules.SetIamPolicy(
set_iam_policy_req
)
def GetIamPolicy(governance_rule_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule().DataplexProjectsLocationsGovernanceRulesGetIamPolicyRequest(
resource=governance_rule_ref.RelativeName()
)
return dataplex_api.GetClientInstance().projects_locations_governanceRules.GetIamPolicy(
get_iam_policy_req
)
def AddIamPolicyBinding(governance_rule_ref, member, role):
"""Add IAM policy binding request."""
policy = GetIamPolicy(governance_rule_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role
)
return SetIamPolicy(governance_rule_ref, policy)
def RemoveIamPolicyBinding(governance_rule_ref, member, role):
"""Remove IAM policy binding request."""
policy = GetIamPolicy(governance_rule_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return SetIamPolicy(governance_rule_ref, policy)
def SetIamPolicyFromFile(governance_rule_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file, dataplex_api.GetMessageModule().GoogleIamV1Policy
)
return SetIamPolicy(governance_rule_ref, policy)

View File

@@ -0,0 +1,123 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 Google Inc. 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.
"""Client for interaction with LAKE API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.api_lib.storage import storage_api
from googlecloudsdk.command_lib.iam import iam_util
def SetIamPolicy(lake_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsLakesSetIamPolicyRequest(
resource=lake_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance().projects_locations_lakes.SetIamPolicy(
set_iam_policy_req)
def GetIamPolicy(lake_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsLakesGetIamPolicyRequest(
resource=lake_ref.RelativeName())
return dataplex_api.GetClientInstance().projects_locations_lakes.GetIamPolicy(
get_iam_policy_req)
def AddIamPolicyBinding(lake_ref, member, role):
"""Add iam policy binding request."""
policy = GetIamPolicy(lake_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return SetIamPolicy(lake_ref, policy)
def RemoveIamPolicyBinding(lake_ref, member, role):
"""Remove iam policy binding request."""
policy = GetIamPolicy(lake_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return SetIamPolicy(lake_ref, policy)
def AddServiceAccountToDatasetPolicy(access_message_type, dataset_policy,
member, role):
"""Add service account to dataset."""
for entry in dataset_policy.access:
if entry.role == role and member in entry.userByEmail:
return False
dataset_policy.access.append(
access_message_type(userByEmail=member, role='{0}'.format(role)))
return True
def SetIamPolicyFromFile(lake_ref, policy_file):
"""Set iam policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return SetIamPolicy(lake_ref, policy)
def RemoveServiceAccountFromBucketPolicy(bucket_ref, member, role):
"""Deauthorize Account for Buckets."""
policy = storage_api.StorageClient().GetIamPolicy(bucket_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return storage_api.StorageClient().SetIamPolicy(bucket_ref, policy)
def RemoveServiceAccountFromDatasetPolicy(dataset_policy, member, role):
"""Deauthorize Account for Dataset."""
for entry in dataset_policy.access:
if entry.role == role and member in entry.userByEmail:
dataset_policy.access.remove(entry)
return True
return False
def GenerateUpdateMask(args):
"""Create Update Mask for Lakes."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('metastore_service'):
update_mask.append('metastore.service')
return update_mask
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_lakes)
def WaitForLongOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_lakes,
sleep_ms=10000,
pre_start_sleep_ms=120000)

View File

@@ -0,0 +1,124 @@
# -*- coding: utf-8 -*- #
# Copyright 2024 Google Inc. 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.
"""Client for interaction with Metadata Job API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.calliope import exceptions
IMPORT_TYPE = 'IMPORT'
EXPORT_TYPE = 'EXPORT'
def GenerateMetadataJob(args):
"""Generates a Metadata Job."""
if args.type == IMPORT_TYPE:
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1MetadataJob(
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1MetadataJob, args
),
type=module.GoogleCloudDataplexV1MetadataJob.TypeValueValuesEnum(
args.type
),
importSpec=GenerateImportMetadataJobSpec(args),
)
elif args.type == EXPORT_TYPE:
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1MetadataJob(
labels=dataplex_api.CreateLabels(
module.GoogleCloudDataplexV1MetadataJob, args
),
type=module.GoogleCloudDataplexV1MetadataJob.TypeValueValuesEnum(
args.type
),
exportSpec=GenerateExportMetadataJobSpec(args),
)
raise exceptions.BadArgumentException(
'--type', 'Current type is not supported in Gcloud.'
)
def GenerateImportMetadataJobSpec(args):
"""Generates a Metadata Import Job Spec."""
module = dataplex_api.GetMessageModule()
import_job_spec = module.GoogleCloudDataplexV1MetadataJobImportJobSpec(
aspectSyncMode=module.GoogleCloudDataplexV1MetadataJobImportJobSpec.AspectSyncModeValueValuesEnum(
args.import_aspect_sync_mode
),
entrySyncMode=module.GoogleCloudDataplexV1MetadataJobImportJobSpec.EntrySyncModeValueValuesEnum(
args.import_entry_sync_mode
),
scope=module.GoogleCloudDataplexV1MetadataJobImportJobSpecImportJobScope(
entryGroups=args.import_entry_groups
if args.import_entry_groups
else [],
glossaries=args.import_glossaries if args.import_glossaries else [],
entryTypes=args.import_entry_types
if args.import_entry_types
else [],
aspectTypes=args.import_aspect_types
if args.import_aspect_types
else [],
entryLinkTypes=args.import_entry_link_types
if args.import_entry_link_types
else [],
referencedEntryScopes=args.import_referenced_entry_scopes
if args.import_referenced_entry_scopes
else [],
),
sourceCreateTime=args.import_source_create_time,
sourceStorageUri=args.import_source_storage_uri,
)
if hasattr(args, 'import_log_level') and args.IsSpecified('import_log_level'):
import_job_spec.logLevel = module.GoogleCloudDataplexV1MetadataJobImportJobSpec.LogLevelValueValuesEnum(
args.import_log_level
)
return import_job_spec
def GenerateExportMetadataJobSpec(args):
"""Generates a Metadata Export Job Spec."""
module = dataplex_api.GetMessageModule()
export_job_spec = module.GoogleCloudDataplexV1MetadataJobExportJobSpec(
outputPath=args.export_output_path,
scope=module.GoogleCloudDataplexV1MetadataJobExportJobSpecExportJobScope(
entryTypes=args.export_entry_types,
aspectTypes=args.export_aspect_types,
),
)
if hasattr(args, 'export_organization_level') and args.IsSpecified(
'export_organization_level'
):
export_job_spec.scope.organizationLevel = args.export_organization_level
elif hasattr(args, 'export_projects') and args.IsSpecified('export_projects'):
export_job_spec.scope.projects = args.export_projects
elif hasattr(args, 'export_entry_groups') and args.IsSpecified(
'export_entry_groups'
):
export_job_spec.scope.entryGroups = args.export_entry_groups
return export_job_spec
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_metadataJobs,
)

View File

@@ -0,0 +1,108 @@
# -*- coding: utf-8 -*- #
# Copyright 2022 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.
"""Client for interaction with Tasks API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.command_lib.iam import iam_util
def SetIamPolicy(task_ref, policy):
"""Set IAM Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule().DataplexProjectsLocationsLakesTasksSetIamPolicyRequest(
resource=task_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule().GoogleIamV1SetIamPolicyRequest(
policy=policy
),
)
return dataplex_api.GetClientInstance().projects_locations_lakes_tasks.SetIamPolicy(
set_iam_policy_req
)
def GetIamPolicy(task_ref):
"""Get IAM Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule().DataplexProjectsLocationsLakesTasksGetIamPolicyRequest(
resource=task_ref.RelativeName()
)
return dataplex_api.GetClientInstance().projects_locations_lakes_tasks.GetIamPolicy(
get_iam_policy_req
)
def AddIamPolicyBinding(task_ref, member, role):
"""Add IAM policy binding request."""
policy = GetIamPolicy(task_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role
)
return SetIamPolicy(task_ref, policy)
def RemoveIamPolicyBinding(task_ref, member, role):
"""Remove IAM policy binding request."""
policy = GetIamPolicy(task_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return SetIamPolicy(task_ref, policy)
def SetIamPolicyFromFile(task_ref, policy_file):
"""Set IAM policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file, dataplex_api.GetMessageModule().GoogleIamV1Policy
)
return SetIamPolicy(task_ref, policy)
def RunTask(task_ref, args):
"""Runs dataplex task with input updates to execution spec args and labels."""
run_task_req = dataplex_api.GetMessageModule().DataplexProjectsLocationsLakesTasksRunRequest(
name=task_ref.RelativeName(),
googleCloudDataplexV1RunTaskRequest=dataplex_api.GetMessageModule().GoogleCloudDataplexV1RunTaskRequest(
labels=dataplex_api.CreateLabels(
dataplex_api.GetMessageModule().GoogleCloudDataplexV1RunTaskRequest,
args,
),
args=CreateArgs(
dataplex_api.GetMessageModule().GoogleCloudDataplexV1RunTaskRequest,
args,
),
),
)
run_task_response = (
dataplex_api.GetClientInstance().projects_locations_lakes_tasks.Run(
run_task_req
)
)
return run_task_response
def CreateArgs(run_task_request, args):
"""Creates Args input compatible for creating a RunTaskRequest object."""
if getattr(args, "ARGS", None):
args_ref = dataplex_api.FetchExecutionSpecArgs(args.ARGS)
if len(args_ref) > 0:
return run_task_request.ArgsValue(
additionalProperties=[
run_task_request.ArgsValue.AdditionalProperty(
key=key, value=value
)
for key, value in sorted(args_ref.items())
]
)
return None

View File

@@ -0,0 +1,148 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 Google Inc. 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.
"""Client for interaction with Dataplex."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
import argparse
import io
from googlecloudsdk.api_lib.storage import storage_api
from googlecloudsdk.api_lib.storage import storage_util
from googlecloudsdk.api_lib.util import apis
from googlecloudsdk.api_lib.util import waiter
from googlecloudsdk.calliope import exceptions
from googlecloudsdk.core import resources
from googlecloudsdk.core import yaml
def GetClientInstance():
return apis.GetClientInstance('dataplex', 'v1')
def GetMessageModule():
return apis.GetMessagesModule('dataplex', 'v1')
def WaitForOperation(
operation, resource, sleep_ms=5000, pre_start_sleep_ms=1000
):
"""Waits for the given google.longrunning.Operation to complete."""
operation_ref = resources.REGISTRY.Parse(
operation.name, collection='dataplex.projects.locations.operations'
)
poller = waiter.CloudOperationPoller(
resource, GetClientInstance().projects_locations_operations
)
return waiter.WaitFor(
poller,
operation_ref,
'Waiting for [{0}] to finish'.format(operation_ref.RelativeName()),
sleep_ms=sleep_ms,
pre_start_sleep_ms=pre_start_sleep_ms,
)
def CreateLabels(dataplex_resource, args):
if getattr(args, 'labels', None):
return dataplex_resource.LabelsValue(
additionalProperties=[
dataplex_resource.LabelsValue.AdditionalProperty(
key=key, value=value
)
for key, value in sorted(args.labels.items())
]
)
return None
def ReadObject(object_url, storage_client=None):
"""Reads an object's content from GCS.
Args:
object_url: Can be a local file path or the URL of the object to be read
from gcs bucket (must have "gs://" prefix).
storage_client: Storage api client used to read files from gcs.
Returns:
A str for the content of the file.
Raises:
ObjectReadError:
If the read of GCS object is not successful.
"""
if not object_url.startswith('gs://'):
return yaml.load_path(object_url)
client = storage_client or storage_api.StorageClient()
object_ref = storage_util.ObjectReference.FromUrl(object_url)
try:
bytes_io = client.ReadObject(object_ref)
wrapper = io.TextIOWrapper(bytes_io, encoding='utf-8')
return yaml.load(wrapper.read())
except Exception as e:
raise exceptions.BadFileException(
'Unable to read file {0} due to incorrect file path or insufficient'
' read permissions'.format(object_url)
) from e
def SnakeToCamel(arg_str):
"""Converts snake case string to camel case."""
return ''.join(
word.capitalize() if ind > 0 else word
for ind, word in enumerate(arg_str.split('_'))
)
def SnakeToCamelDict(arg_type):
"""Reccursive method to convert all nested snake case dictionary keys to camel case."""
if isinstance(arg_type, list):
return [
SnakeToCamelDict(list_val)
if isinstance(list_val, (dict, list))
else list_val
for list_val in arg_type
]
return {
SnakeToCamel(key): (
SnakeToCamelDict(value) if isinstance(value, (dict, list)) else value
)
for key, value in arg_type.items()
}
def FetchExecutionSpecArgs(args_map_as_list):
"""Returns Dataplex task execution spec args as a map of key,value pairs from an input list of strings of format key=value."""
execution_args_map = dict()
for arg_entry in args_map_as_list:
if '=' not in arg_entry:
raise argparse.ArgumentTypeError(
"Execution spec argument '{}' should be of the type argKey=argValue."
.format(arg_entry)
)
arg_entry_split = arg_entry.split('=', 1)
if (
len(arg_entry_split) < 2
or len(arg_entry_split[0].strip()) == 0
or len(arg_entry_split[1]) == 0
):
raise argparse.ArgumentTypeError(
"Execution spec argument '{}' should be of the format"
' argKey=argValue.'.format(arg_entry)
)
execution_args_map[arg_entry_split[0]] = arg_entry_split[1]
return execution_args_map

View File

@@ -0,0 +1,154 @@
# -*- coding: utf-8 -*- #
# Copyright 2021 Google Inc. 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.
"""Client for interaction with ZONE API CRUD DATAPLEX."""
from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals
from googlecloudsdk.api_lib.dataplex import util as dataplex_api
from googlecloudsdk.command_lib.iam import iam_util
def SetIamPolicy(zone_ref, policy):
"""Set Iam Policy request."""
set_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsLakesZonesSetIamPolicyRequest(
resource=zone_ref.RelativeName(),
googleIamV1SetIamPolicyRequest=dataplex_api.GetMessageModule()
.GoogleIamV1SetIamPolicyRequest(policy=policy))
return dataplex_api.GetClientInstance(
).projects_locations_lakes_zones.SetIamPolicy(set_iam_policy_req)
def GetIamPolicy(zone_ref):
"""Get Iam Policy request."""
get_iam_policy_req = dataplex_api.GetMessageModule(
).DataplexProjectsLocationsLakesZonesGetIamPolicyRequest(
resource=zone_ref.RelativeName())
return dataplex_api.GetClientInstance(
).projects_locations_lakes_zones.GetIamPolicy(get_iam_policy_req)
def AddIamPolicyBinding(zone_ref, member, role):
"""Add iam policy binding request."""
policy = GetIamPolicy(zone_ref)
iam_util.AddBindingToIamPolicy(
dataplex_api.GetMessageModule().GoogleIamV1Binding, policy, member, role)
return SetIamPolicy(zone_ref, policy)
def RemoveIamPolicyBinding(lake_ref, member, role):
"""Remove iam policy binding request."""
policy = GetIamPolicy(lake_ref)
iam_util.RemoveBindingFromIamPolicy(policy, member, role)
return SetIamPolicy(lake_ref, policy)
def SetIamPolicyFromFile(zone_ref, policy_file):
"""Set iam policy binding request from file."""
policy = iam_util.ParsePolicyFile(
policy_file,
dataplex_api.GetMessageModule().GoogleIamV1Policy)
return SetIamPolicy(zone_ref, policy)
def GenerateZoneForCreateRequest(args):
"""Create Zone for Message Create Requests."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1Zone(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1Zone, args),
type=module.GoogleCloudDataplexV1Zone.TypeValueValuesEnum(args.type),
discoverySpec=GenerateDiscoverySpec(args),
resourceSpec=module.GoogleCloudDataplexV1ZoneResourceSpec(
locationType=module.GoogleCloudDataplexV1ZoneResourceSpec
.LocationTypeValueValuesEnum(args.resource_location_type)))
def GenerateZoneForUpdateRequest(args):
"""Create Zone for Message Update Requests."""
module = dataplex_api.GetMessageModule()
return module.GoogleCloudDataplexV1Zone(
description=args.description,
displayName=args.display_name,
labels=dataplex_api.CreateLabels(module.GoogleCloudDataplexV1Zone, args),
discoverySpec=GenerateDiscoverySpec(args))
def GenerateDiscoverySpec(args):
return dataplex_api.GetMessageModule().GoogleCloudDataplexV1ZoneDiscoverySpec(
enabled=args.discovery_enabled,
includePatterns=args.discovery_include_patterns,
excludePatterns=args.discovery_exclude_patterns,
schedule=args.discovery_schedule,
csvOptions=GenerateCsvOptions(args),
jsonOptions=GenerateJsonOptions(args))
def GenerateCsvOptions(args):
return dataplex_api.GetMessageModule(
).GoogleCloudDataplexV1ZoneDiscoverySpecCsvOptions(
delimiter=args.csv_delimiter,
disableTypeInference=args.csv_disable_type_inference,
encoding=args.csv_encoding,
headerRows=args.csv_header_rows)
def GenerateJsonOptions(args):
return dataplex_api.GetMessageModule(
).GoogleCloudDataplexV1ZoneDiscoverySpecJsonOptions(
encoding=args.json_encoding,
disableTypeInference=args.json_disable_type_inference)
def GenerateUpdateMask(args):
"""Create Update Mask for Zones."""
update_mask = []
if args.IsSpecified('description'):
update_mask.append('description')
if args.IsSpecified('display_name'):
update_mask.append('displayName')
if args.IsSpecified('labels'):
update_mask.append('labels')
if args.IsSpecified('discovery_enabled'):
update_mask.append('discoverySpec.enabled')
if args.IsSpecified('discovery_include_patterns'):
update_mask.append('discoverySpec.includePatterns')
if args.IsSpecified('discovery_exclude_patterns'):
update_mask.append('discoverySpec.excludePatterns')
if args.IsSpecified('discovery_schedule'):
update_mask.append('discoverySpec.schedule')
if args.IsSpecified('csv_header_rows'):
update_mask.append('discoverySpec.csvOptions.headerRows')
if args.IsSpecified('csv_delimiter'):
update_mask.append('discoverySpec.csvOptions.delimiter')
if args.IsSpecified('csv_encoding'):
update_mask.append('discoverySpec.csvOptions.encoding')
if args.IsSpecified('csv_disable_type_inference'):
update_mask.append('discoverySpec.csvOptions.disableTypeInference')
if args.IsSpecified('json_encoding'):
update_mask.append('discoverySpec.jsonOptions.encoding')
if args.IsSpecified('json_disable_type_inference'):
update_mask.append('discoverySpec.jsonOptions.disableTypeInference')
return update_mask
def WaitForOperation(operation):
"""Waits for the given google.longrunning.Operation to complete."""
return dataplex_api.WaitForOperation(
operation,
dataplex_api.GetClientInstance().projects_locations_lakes_zones)